2 * Distributed Universal Number Discovery (DUNDi)
4 * Copyright (C) 2004, Digium Inc.
6 * Written by Mark Spencer <markster@digium.com>
8 * This program is Free Software distributed under the terms of
9 * of the GNU General Public License.
12 #include <asterisk/file.h>
13 #include <asterisk/logger.h>
14 #include <asterisk/channel.h>
15 #include <asterisk/config.h>
16 #include <asterisk/options.h>
17 #include <asterisk/pbx.h>
18 #include <asterisk/module.h>
19 #include <asterisk/frame.h>
20 #include <asterisk/file.h>
21 #include <asterisk/channel_pvt.h>
22 #include <asterisk/cli.h>
23 #include <asterisk/lock.h>
24 #include <asterisk/md5.h>
25 #include <asterisk/dundi.h>
26 #include <asterisk/sched.h>
27 #include <asterisk/io.h>
28 #include <asterisk/utils.h>
29 #include <asterisk/crypto.h>
30 #include <asterisk/astdb.h>
31 #include <asterisk/acl.h>
32 #include <asterisk/aes.h>
34 #include "dundi-parser.h"
37 #include <arpa/inet.h>
38 #include <netinet/in.h>
39 #include <sys/socket.h>
42 #if defined(__FreeBSD__) || defined(__NetBSD__)
43 #include <sys/types.h>
44 #include <netinet/in_systm.h>
46 #include <netinet/ip.h>
47 #include <sys/ioctl.h>
48 #include <netinet/in.h>
50 #if defined(__FreeBSD__)
51 #include <net/if_dl.h>
56 #define MAX_RESULTS 64
58 #define MAX_PACKET_SIZE 8192
60 extern char ast_config_AST_KEY_DIR[];
62 static char *tdesc = "Distributed Universal Number Discovery (DUNDi)";
64 static char *app = "DUNDiLookup";
65 static char *synopsis = "Look up a number with DUNDi";
66 static char *descrip =
67 "DUNDiLookup(number[|context[|options]])\n"
68 " Looks up a given number in the global context specified or in\n"
69 "the reserved 'e164' context if not specified. Returns -1 if the channel\n"
70 "is hungup during the lookup or 0 otherwise. On completion, the variable\n"
71 "${DUNDTECH} and ${DUNDDEST} will contain the technology and destination\n"
72 "of the appropriate technology and destination to access the number. If no\n"
73 "answer was found, and the priority n + 101 exists, execution will continue\n"
74 "at that location.\n";
76 #define DUNDI_MODEL_INBOUND (1 << 0)
77 #define DUNDI_MODEL_OUTBOUND (1 << 1)
78 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
80 /* Keep times of last 10 lookups */
81 #define DUNDI_TIMING_HISTORY 10
83 #define FLAG_ISREG (1 << 0) /* Transaction is register request */
84 #define FLAG_DEAD (1 << 1) /* Transaction is dead */
85 #define FLAG_FINAL (1 << 2) /* Transaction has final message sent */
86 #define FLAG_ISQUAL (1 << 3) /* Transaction is a qualification */
87 #define FLAG_ENCRYPT (1 << 4) /* Transaction is encrypted wiht ECX/DCX */
88 #define FLAG_SENDFULLKEY (1 << 5) /* Send full key on transaction */
89 #define FLAG_STOREHIST (1 << 6) /* Record historic performance */
91 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
94 #define DUNDI_SECRET_TIME 15 /* Testing only */
96 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
102 static struct io_context *io;
103 static struct sched_context *sched;
104 static int netsocket = -1;
105 static pthread_t netthreadid = AST_PTHREADT_NULL;
107 static int dundidebug = 0;
108 static int authdebug = 0;
109 static int dundi_ttl = DUNDI_DEFAULT_TTL;
110 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
111 static int global_autokilltimeout = 0;
112 static dundi_eid global_eid;
113 static int default_expiration = 60;
114 static int global_storehistory = 0;
115 static char dept[80];
117 static char locality[80];
118 static char stateprov[80];
119 static char country[80];
120 static char email[80];
121 static char phone[80];
122 static char secretpath[80];
123 static char cursecret[80];
124 static char ipaddr[80];
125 static time_t rotatetime;
126 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
128 struct permission *next;
133 struct dundi_packet {
135 struct dundi_packet *next;
137 struct dundi_transaction *parent;
140 unsigned char data[0];
143 struct dundi_hint_metadata {
144 unsigned short flags;
145 char exten[AST_MAX_EXTENSION];
148 struct dundi_request;
150 struct dundi_transaction {
151 struct sockaddr_in addr; /* Other end of transaction */
152 struct timeval start; /* When this transaction was created */
153 dundi_eid eids[DUNDI_MAX_STACK + 1];
154 int eidcount; /* Number of eids in eids */
155 dundi_eid us_eid; /* Our EID, to them */
156 dundi_eid them_eid; /* Their EID, to us */
157 aes_encrypt_ctx ecx; /* AES 128 Encryption context */
158 aes_decrypt_ctx dcx; /* AES 128 Decryption context */
159 int flags; /* Has final packet been sent */
160 int ttl; /* Remaining TTL for queries on this one */
161 int thread; /* We have a calling thread */
162 int retranstimer; /* How long to wait before retransmissions */
163 int autokillid; /* ID to kill connection if answer doesn't come back fast enough */
164 int autokilltimeout; /* Recommended timeout for autokill */
165 unsigned short strans; /* Our transaction identifier */
166 unsigned short dtrans; /* Their transaction identifer */
167 unsigned char iseqno; /* Next expected received seqno */
168 unsigned char oiseqno; /* Last received incoming seqno */
169 unsigned char oseqno; /* Next transmitted seqno */
170 unsigned char aseqno; /* Last acknowledge seqno */
171 struct dundi_packet *packets; /* Packets to be retransmitted */
172 struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
173 struct dundi_transaction *next; /* Next with respect to the parent */
174 struct dundi_request *parent; /* Parent request (if there is one) */
175 struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
178 struct dundi_request {
179 char dcontext[AST_MAX_EXTENSION];
180 char number[AST_MAX_EXTENSION];
183 struct dundi_result *dr;
184 struct dundi_entity_info *dei;
185 struct dundi_hint_metadata *hmd;
191 unsigned long crc32; /* CRC-32 of all but root EID's in avoid list */
192 struct dundi_transaction *trans; /* Transactions */
193 struct dundi_request *next;
196 static struct dundi_mapping {
197 char dcontext[AST_MAX_EXTENSION];
198 char lcontext[AST_MAX_EXTENSION];
203 char dest[AST_MAX_EXTENSION];
204 struct dundi_mapping *next;
207 static struct dundi_peer {
209 struct sockaddr_in addr; /* Address of DUNDi peer */
210 struct permission *permit;
211 struct permission *include;
221 unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
222 unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
223 unsigned long us_keycrc32; /* CRC-32 of our key */
224 aes_encrypt_ctx us_ecx; /* Cached AES 128 Encryption context */
225 aes_decrypt_ctx us_dcx; /* Cached AES 128 Decryption context */
226 unsigned long them_keycrc32;/* CRC-32 of our key */
227 aes_encrypt_ctx them_ecx; /* Cached AES 128 Encryption context */
228 aes_decrypt_ctx them_dcx; /* Cached AES 128 Decryption context */
229 time_t keyexpire; /* When to expire/recreate key */
231 int lookuptimes[DUNDI_TIMING_HISTORY];
232 char *lookups[DUNDI_TIMING_HISTORY];
234 struct dundi_transaction *regtrans; /* Registration transaction */
235 struct dundi_transaction *qualtrans; /* Qualify transaction */
236 struct dundi_transaction *keypending;
238 int dynamic; /* Are we dynamic? */
239 int lastms; /* Last measured latency */
240 int maxms; /* Max permissible latency */
241 struct timeval qualtx; /* Time of transmit */
242 struct dundi_peer *next;
245 AST_MUTEX_DEFINE_STATIC(peerlock);
247 static int dundi_xmit(struct dundi_packet *pack);
249 static void dundi_debug_output(const char *data)
252 ast_verbose("%s", data);
255 static void dundi_error_output(const char *data)
257 ast_log(LOG_WARNING, "%s", data);
260 static int has_permission(struct permission *ps, char *cont)
264 if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
271 static char *tech2str(int tech)
274 case DUNDI_PROTO_NONE:
276 case DUNDI_PROTO_IAX:
278 case DUNDI_PROTO_SIP:
280 case DUNDI_PROTO_H323:
287 static int str2tech(char *str)
289 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
290 return DUNDI_PROTO_IAX;
291 else if (!strcasecmp(str, "SIP"))
292 return DUNDI_PROTO_SIP;
293 else if (!strcasecmp(str, "H323"))
294 return DUNDI_PROTO_H323;
299 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, dundi_eid *avoid[], int direct[]);
300 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
301 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
303 /* Look for an exact match first */
304 struct dundi_transaction *trans;
307 if (!inaddrcmp(&trans->addr, sin) &&
308 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
309 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
311 trans->dtrans = ntohs(hdr->strans) & 32767;
314 trans = trans->allnext;
317 switch(hdr->cmdresp & 0x7f) {
318 case DUNDI_COMMAND_DPDISCOVER:
319 case DUNDI_COMMAND_EIDQUERY:
320 case DUNDI_COMMAND_REGREQ:
321 case DUNDI_COMMAND_NULL:
322 case DUNDI_COMMAND_ENCRYPT:
324 /* Create new transaction */
325 trans = create_transaction(NULL);
327 memcpy(&trans->addr, sin, sizeof(trans->addr));
328 trans->dtrans = ntohs(hdr->strans) & 32767;
330 ast_log(LOG_WARNING, "Out of memory!\n");
340 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
342 static int dundi_ack(struct dundi_transaction *trans, int final)
344 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
346 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
349 struct dundi_packet pack;
350 struct dundi_hdr hdr;
352 struct dundi_transaction trans;
353 /* Never respond to an INVALID with another INVALID */
354 if (h->cmdresp == DUNDI_COMMAND_INVALID)
356 memset(&tmp, 0, sizeof(tmp));
357 memset(&trans, 0, sizeof(trans));
358 memcpy(&trans.addr, sin, sizeof(trans.addr));
359 tmp.hdr.strans = h->dtrans;
360 tmp.hdr.dtrans = h->strans;
361 tmp.hdr.iseqno = h->oseqno;
362 tmp.hdr.oseqno = h->iseqno;
363 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
364 tmp.hdr.cmdflags = 0;
365 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
366 tmp.pack.datalen = sizeof(struct dundi_hdr);
367 tmp.pack.parent = &trans;
368 dundi_xmit(&tmp.pack);
371 static void reset_global_eid(void)
373 #if defined(SIOCGIFHWADDR)
378 s = socket(AF_INET, SOCK_STREAM, 0);
382 memset(&ifr, 0, sizeof(ifr));
383 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
384 if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
385 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
386 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
392 #if defined(ifa_broadaddr)
394 struct ifaddrs *ifap;
396 if (getifaddrs(&ifap) == 0) {
398 for (p = ifap; p; p = p->ifa_next) {
399 if (p->ifa_addr->sa_family == AF_LINK) {
400 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
403 sdp->sdl_data + sdp->sdl_nlen, 6);
404 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);
413 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
416 static int get_trans_id(void)
418 struct dundi_transaction *t;
419 int stid = (rand() % 32766) + 1;
424 if (t->strans == tid)
430 tid = (tid % 32766) + 1;
431 } while (tid != stid);
435 static int reset_transaction(struct dundi_transaction *trans)
438 tid = get_trans_id();
447 trans->flags &= ~FLAG_FINAL;
451 static struct dundi_peer *find_peer(dundi_eid *eid)
453 struct dundi_peer *cur;
458 if (!dundi_eid_cmp(&cur->eid,eid))
465 static void build_iv(unsigned char *iv)
467 /* XXX Would be nice to be more random XXX */
468 unsigned int *fluffy;
470 fluffy = (unsigned int *)(iv);
475 struct dundi_query_state {
476 dundi_eid *eids[DUNDI_MAX_STACK + 1];
477 int directs[DUNDI_MAX_STACK + 1];
479 char called_context[AST_MAX_EXTENSION];
480 char called_number[AST_MAX_EXTENSION];
481 struct dundi_mapping *maps;
484 struct dundi_transaction *trans;
491 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)
495 if (!ast_strlen_zero(map->lcontext)) {
497 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
498 flags |= DUNDI_FLAG_EXISTS;
499 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
500 flags |= DUNDI_FLAG_CANMATCH;
501 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
502 flags |= DUNDI_FLAG_MATCHMORE;
503 if (ast_ignore_pattern(map->lcontext, called_number))
504 flags |= DUNDI_FLAG_IGNOREPAT;
506 /* Clearly we can't say 'don't ask' anymore if we found anything... */
508 hmd->flags &= ~DUNDI_HINT_DONT_ASK;
510 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
511 /* Skip partial answers */
512 flags &= ~(DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
515 struct varshead headp;
516 struct ast_var_t *newvariable;
517 flags |= map->options & 0xffff;
518 dr[anscnt].flags = flags;
519 dr[anscnt].techint = map->tech;
520 dr[anscnt].weight = map->weight;
521 dr[anscnt].expiration = DUNDI_DEFAULT_CACHE_TIME;
522 strncpy(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
523 dr[anscnt].eid = *us_eid;
524 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
525 if (flags & DUNDI_FLAG_EXISTS) {
526 AST_LIST_HEAD_INIT(&headp);
527 newvariable = ast_var_assign("NUMBER", called_number);
528 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
529 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
530 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
531 newvariable = ast_var_assign("SECRET", cursecret);
532 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
533 newvariable = ast_var_assign("IPADDR", ipaddr);
534 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
535 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
536 while (!AST_LIST_EMPTY(&headp)) { /* List Deletion. */
537 newvariable = AST_LIST_FIRST(&headp);
538 AST_LIST_REMOVE_HEAD(&headp, entries);
539 ast_var_delete(newvariable);
542 dr[anscnt].dest[0] = '\0';
545 /* No answers... Find the fewest number of digits from the
546 number for which we have no answer. */
547 char tmp[AST_MAX_EXTENSION]="";
548 for (x=0;x<AST_MAX_EXTENSION;x++) {
549 tmp[x] = called_number[x];
552 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
553 /* Oops found something we can't match. If this is longer
554 than the running hint, we have to consider it */
555 if (strlen(tmp) > strlen(hmd->exten)) {
556 strncpy(hmd->exten, tmp, sizeof(hmd->exten) - 1);
566 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
568 static void *dundi_lookup_thread(void *data)
570 struct dundi_query_state *st = data;
571 struct dundi_result dr[MAX_RESULTS];
572 struct dundi_ie_data ied;
573 struct dundi_hint_metadata hmd;
578 int expiration = DUNDI_DEFAULT_CACHE_TIME;
580 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
581 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
582 memset(&ied, 0, sizeof(ied));
583 memset(&dr, 0, sizeof(dr));
584 memset(&hmd, 0, sizeof(hmd));
585 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
586 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
587 for (x=0;x<st->nummaps;x++)
588 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
591 for (x=0;x<ouranswers;x++) {
592 if (dr[x].weight < max)
597 /* If we do not have a canonical result, keep looking */
598 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, st->eids, st->directs);
600 /* Append answer in result */
603 if ((res < -1) && (!ouranswers))
604 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
607 ast_mutex_lock(&peerlock);
608 /* Truncate if "don't ask" isn't present */
609 if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
611 if (st->trans->flags & FLAG_DEAD) {
612 ast_log(LOG_DEBUG, "Our transaction went away!\n");
613 st->trans->thread = 0;
614 destroy_trans(st->trans, 0);
616 for (x=0;x<ouranswers;x++) {
618 if (dr[x].expiration && (expiration > dr[x].expiration))
619 expiration = dr[x].expiration;
620 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
622 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
623 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
624 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
625 st->trans->thread = 0;
627 ast_mutex_unlock(&peerlock);
632 static inline int calc_ms(struct timeval *start)
635 gettimeofday(&tv, NULL);
636 return ((tv.tv_sec - start->tv_sec) * 1000 + (tv.tv_usec - start->tv_usec) / 1000);
639 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[]);
641 static void *dundi_query_thread(void *data)
643 struct dundi_query_state *st = data;
644 struct dundi_entity_info dei;
645 struct dundi_ie_data ied;
646 struct dundi_hint_metadata hmd;
649 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
650 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
651 memset(&ied, 0, sizeof(ied));
652 memset(&dei, 0, sizeof(dei));
653 memset(&hmd, 0, sizeof(hmd));
654 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
656 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
657 strncpy(dei.orgunit, dept, sizeof(dei.orgunit));
658 strncpy(dei.org, org, sizeof(dei.org));
659 strncpy(dei.locality, locality, sizeof(dei.locality));
660 strncpy(dei.stateprov, stateprov, sizeof(dei.stateprov));
661 strncpy(dei.country, country, sizeof(dei.country));
662 strncpy(dei.email, email, sizeof(dei.email));
663 strncpy(dei.phone, phone, sizeof(dei.phone));
666 /* If we do not have a canonical result, keep looking */
667 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
669 ast_mutex_lock(&peerlock);
670 if (st->trans->flags & FLAG_DEAD) {
671 ast_log(LOG_DEBUG, "Our transaction went away!\n");
672 st->trans->thread = 0;
673 destroy_trans(st->trans, 0);
676 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
677 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
678 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
679 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
680 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
681 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
682 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
683 if (!ast_strlen_zero(dei.ipaddr))
684 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
686 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
687 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
688 st->trans->thread = 0;
690 ast_mutex_unlock(&peerlock);
695 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
697 struct dundi_query_state *st;
701 struct dundi_ie_data ied;
704 pthread_t lookupthread;
706 if (ies->eidcount > 1) {
707 /* Since it is a requirement that the first EID is the authenticating host
708 and the last EID is the root, it is permissible that the first and last EID
709 could be the same. In that case, we should go ahead copy only the "root" section
710 since we will not need it for authentication. */
711 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
714 totallen = sizeof(struct dundi_query_state);
715 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
716 st = malloc(totallen);
718 memset(st, 0, totallen);
719 strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
720 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
722 st->ttl = ies->ttl - 1;
726 for (x=skipfirst;ies->eids[x];x++) {
727 st->eids[x-skipfirst] = (dundi_eid *)s;
728 *st->eids[x-skipfirst] = *ies->eids[x];
729 s += sizeof(dundi_eid);
731 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);
732 pthread_attr_init(&attr);
733 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
735 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
737 ast_log(LOG_WARNING, "Unable to create thread!\n");
739 memset(&ied, 0, sizeof(ied));
740 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
741 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
745 ast_log(LOG_WARNING, "Out of memory!\n");
746 memset(&ied, 0, sizeof(ied));
747 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
748 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
754 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
756 struct dundi_query_state *st;
759 struct dundi_ie_data ied;
761 struct dundi_mapping *cur;
765 pthread_t lookupthread;
767 totallen = sizeof(struct dundi_query_state);
768 /* Count matching map entries */
772 if (!strcasecmp(cur->dcontext, ccontext))
776 /* If no maps, return -1 immediately */
780 if (ies->eidcount > 1) {
781 /* Since it is a requirement that the first EID is the authenticating host
782 and the last EID is the root, it is permissible that the first and last EID
783 could be the same. In that case, we should go ahead copy only the "root" section
784 since we will not need it for authentication. */
785 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
789 totallen += mapcount * sizeof(struct dundi_mapping);
790 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
791 st = malloc(totallen);
793 memset(st, 0, totallen);
794 strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
795 strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
797 st->ttl = ies->ttl - 1;
798 st->nocache = ies->cbypass;
802 for (x=skipfirst;ies->eids[x];x++) {
803 st->eids[x-skipfirst] = (dundi_eid *)s;
804 *st->eids[x-skipfirst] = *ies->eids[x];
805 st->directs[x-skipfirst] = ies->eid_direct[x];
806 s += sizeof(dundi_eid);
808 /* Append mappings */
810 st->maps = (struct dundi_mapping *)s;
813 if (!strcasecmp(cur->dcontext, ccontext)) {
816 st->maps[x].next = NULL;
822 st->nummaps = mapcount;
823 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
824 pthread_attr_init(&attr);
825 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
827 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
829 ast_log(LOG_WARNING, "Unable to create thread!\n");
831 memset(&ied, 0, sizeof(ied));
832 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
833 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
837 ast_log(LOG_WARNING, "Out of memory!\n");
838 memset(&ied, 0, sizeof(ied));
839 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
840 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
846 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
851 char eidpeer_str[20];
852 char eidroot_str[20];
857 expiration = DUNDI_DEFAULT_CACHE_TIME;
859 /* Only cache hint if "don't ask" is there... */
860 if (!(ntohs(hint->flags)& DUNDI_HINT_DONT_ASK))
863 unaffected = ntohs(hint->flags) & DUNDI_HINT_UNAFFECTED;
865 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
866 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
867 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
868 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
871 timeout += expiration;
872 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
874 ast_db_put("dundi/cache", key1, data);
875 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
876 ast_db_put("dundi/cache", key2, data);
877 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
881 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration)
887 char eidpeer_str[20];
888 char eidroot_str[20];
892 expiration = DUNDI_DEFAULT_CACHE_TIME;
893 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
894 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
895 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
896 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
897 /* Build request string */
899 timeout += expiration;
900 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
901 for (x=start;x<req->respcount;x++) {
902 /* Skip anything with an illegal pipe in it */
903 if (strchr(req->dr[x].dest, '|'))
905 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
906 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
907 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
909 ast_db_put("dundi/cache", key1, data);
910 ast_db_put("dundi/cache", key2, data);
914 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
917 char *ptr, *term, *src;
926 /* Build request string */
927 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
929 if (sscanf(ptr, "%ld|%n", &timeout, &length) == 1) {
930 expiration = timeout - now;
931 if (expiration > 0) {
932 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
934 while((sscanf(ptr, "%d/%d/%d/%n", &flags, &weight, &tech, &length) == 3)) {
936 term = strchr(ptr, '|');
939 src = strrchr(ptr, '/');
945 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
946 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags), eid_str_full);
947 /* Make sure it's not already there */
948 for (z=0;z<req->respcount;z++) {
949 if ((req->dr[z].techint == tech) &&
950 !strcmp(req->dr[z].dest, ptr))
953 if (z == req->respcount) {
954 /* Copy into parent responses */
955 req->dr[req->respcount].flags = flags;
956 req->dr[req->respcount].weight = weight;
957 req->dr[req->respcount].techint = tech;
958 req->dr[req->respcount].expiration = expiration;
959 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
960 strncpy(req->dr[req->respcount].dest, ptr,
961 sizeof(req->dr[req->respcount].dest));
962 strncpy(req->dr[req->respcount].tech, tech2str(tech),
963 sizeof(req->dr[req->respcount].tech));
965 req->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
966 } else if (req->dr[z].weight > weight)
967 req->dr[z].weight = weight;
971 /* We found *something* cached */
972 if (expiration < *lowexpiration)
973 *lowexpiration = expiration;
976 ast_db_del("dundi/cache", key);
978 ast_db_del("dundi/cache", key);
984 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
988 char eidroot_str[20];
992 char eid_str_full[20];
997 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
998 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
999 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1000 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1001 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1002 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1003 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1004 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1005 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1007 if (!req->respcount) {
1009 /* Look and see if we have a hint that would preclude us from looking at this
1010 peer for this number. */
1011 if (!(tmp[x] = req->number[x]))
1014 /* Check for hints */
1015 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1016 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1017 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1018 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1019 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1020 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1022 if (strlen(tmp) > strlen(req->hmd->exten)) {
1023 /* Update meta data if appropriate */
1024 strncpy(req->hmd->exten, tmp, sizeof(req->hmd->exten) - 1);
1034 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1036 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1038 if (!trans->addr.sin_addr.s_addr)
1039 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1040 trans->us_eid = p->us_eid;
1041 trans->them_eid = p->eid;
1042 /* Enable encryption if appropriate */
1043 if (!ast_strlen_zero(p->inkey))
1044 trans->flags |= FLAG_ENCRYPT;
1046 trans->autokilltimeout = p->maxms;
1047 if (p->lastms > 1) {
1048 trans->retranstimer = p->lastms * 2;
1049 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1050 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1051 /* Keep it from being silly */
1052 if (trans->retranstimer < 10)
1053 trans->retranstimer = 10;
1056 trans->autokilltimeout = global_autokilltimeout;
1059 static int do_register_expire(void *data)
1061 struct dundi_peer *peer = data;
1063 /* Called with peerlock already held */
1064 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1065 peer->registerexpire = -1;
1067 memset(&peer->addr, 0, sizeof(peer->addr));
1071 static int update_key(struct dundi_peer *peer)
1073 unsigned char key[16];
1074 struct ast_key *ekey, *skey;
1077 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1079 aes_encrypt_key128(key, &peer->us_ecx);
1080 aes_decrypt_key128(key, &peer->us_dcx);
1081 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1083 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1084 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1087 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1089 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1090 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1093 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1094 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1097 if ((res = ast_sign_bin(skey, peer->txenckey, 128, peer->txenckey + 128))) {
1098 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1101 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1102 peer->sentfullkey = 0;
1104 time(&peer->keyexpire);
1105 peer->keyexpire += dundi_key_ttl;
1110 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
1112 unsigned char curblock[16];
1114 memcpy(curblock, iv, sizeof(curblock));
1117 curblock[x] ^= src[x];
1118 aes_encrypt(curblock, dst, ecx);
1119 memcpy(curblock, dst, sizeof(curblock));
1126 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
1128 unsigned char lastblock[16];
1130 memcpy(lastblock, iv, sizeof(lastblock));
1132 aes_decrypt(src, dst, dcx);
1134 dst[x] ^= lastblock[x];
1135 memcpy(lastblock, src, sizeof(lastblock));
1143 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)
1145 int space = *dstlen;
1146 unsigned long bytes;
1147 struct dundi_hdr *h;
1148 unsigned char *decrypt_space;
1149 decrypt_space = alloca(srclen);
1152 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1154 h = (struct dundi_hdr *)dst;
1157 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1158 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1162 *dstlen = bytes + 6;
1163 /* Return new header */
1167 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1169 unsigned char *compress_space;
1172 unsigned long bytes;
1173 struct dundi_ie_data ied;
1174 struct dundi_peer *peer;
1175 unsigned char iv[16];
1176 len = pack->datalen + pack->datalen / 100 + 42;
1177 compress_space = alloca(len);
1178 if (compress_space) {
1179 memset(compress_space, 0, len);
1180 /* We care about everthing save the first 6 bytes of header */
1182 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1184 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1187 memset(&ied, 0, sizeof(ied));
1188 /* Say who we are */
1189 if (!pack->h->iseqno && !pack->h->oseqno) {
1190 /* Need the key in the first copy */
1191 if (!(peer = find_peer(&trans->them_eid)))
1193 if (update_key(peer))
1195 if (!peer->sentfullkey)
1196 trans->flags |= FLAG_SENDFULLKEY;
1197 /* Append key data */
1198 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1199 if (trans->flags & FLAG_SENDFULLKEY) {
1200 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1201 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1203 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1205 /* Setup contexts */
1206 trans->ecx = peer->us_ecx;
1207 trans->dcx = peer->us_dcx;
1209 /* We've sent the full key */
1210 peer->sentfullkey = 1;
1212 /* Build initialization vector */
1214 /* Add the field, rounded up to 16 bytes */
1215 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1217 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1218 ast_log(LOG_NOTICE, "Final packet too large!\n");
1221 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1222 ied.pos += ((bytes + 15) / 16) * 16;
1223 /* Reconstruct header */
1224 pack->datalen = sizeof(struct dundi_hdr);
1225 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1226 pack->h->cmdflags = 0;
1227 memcpy(pack->h->ies, ied.buf, ied.pos);
1228 pack->datalen += ied.pos;
1234 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1236 unsigned char dst[128];
1238 struct ast_key *key, *skey;
1241 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1242 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1245 } else if (!newkey || !newsig)
1247 if (!memcmp(peer->rxenckey, newkey, 128) &&
1248 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1249 /* By definition, a match */
1253 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1255 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1256 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1260 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1262 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1263 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1267 /* First check signature */
1268 res = ast_check_signature_bin(skey, newkey, 128, newsig);
1272 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1275 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1278 /* Decrypted, passes signature */
1279 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1280 memcpy(peer->rxenckey, newkey, 128);
1281 memcpy(peer->rxenckey + 128, newsig, 128);
1282 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1283 aes_decrypt_key128(dst, &peer->them_dcx);
1284 aes_encrypt_key128(dst, &peer->them_ecx);
1288 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1290 /* Handle canonical command / response */
1291 int final = hdr->cmdresp & 0x80;
1292 int cmd = hdr->cmdresp & 0x7f;
1297 unsigned char *bufcpy;
1298 struct dundi_ie_data ied;
1299 struct dundi_ies ies;
1300 struct dundi_peer *peer;
1303 memset(&ied, 0, sizeof(ied));
1304 memset(&ies, 0, sizeof(ies));
1306 bufcpy = alloca(datalen);
1309 /* Make a copy for parsing */
1310 memcpy(bufcpy, hdr->ies, datalen);
1311 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1312 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1313 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1318 case DUNDI_COMMAND_DPDISCOVER:
1319 case DUNDI_COMMAND_EIDQUERY:
1320 if (cmd == DUNDI_COMMAND_EIDQUERY)
1321 resp = DUNDI_COMMAND_EIDRESPONSE;
1323 resp = DUNDI_COMMAND_DPRESPONSE;
1324 /* A dialplan or entity discover -- qualify by highest level entity */
1325 peer = find_peer(ies.eids[0]);
1327 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1328 dundi_send(trans, resp, 0, 1, &ied);
1331 trans->us_eid = peer->us_eid;
1332 if (strlen(peer->inkey)) {
1333 hasauth = encrypted;
1337 /* Okay we're authentiated and all, now we check if they're authorized */
1338 if (!ies.called_context)
1339 ies.called_context = "e164";
1340 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1341 res = dundi_answer_entity(trans, &ies, ies.called_context);
1343 if (!ies.called_number || ast_strlen_zero(ies.called_number)) {
1344 /* They're not permitted to access that context */
1345 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1346 dundi_send(trans, resp, 0, 1, &ied);
1347 } else if (has_permission(peer->permit, ies.called_context)) {
1348 res = dundi_answer_query(trans, &ies, ies.called_context);
1350 /* There is no such dundi context */
1351 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1352 dundi_send(trans, resp, 0, 1, &ied);
1356 /* They're not permitted to access that context */
1357 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1358 dundi_send(trans, resp, 0, 1, &ied);
1362 /* They're not permitted to access that context */
1363 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1364 dundi_send(trans, resp, 0, 1, &ied);
1368 case DUNDI_COMMAND_REGREQ:
1369 /* A register request -- should only have one entity */
1370 peer = find_peer(ies.eids[0]);
1371 if (!peer || !peer->dynamic) {
1372 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1373 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1376 trans->us_eid = peer->us_eid;
1377 if (!ast_strlen_zero(peer->inkey)) {
1378 hasauth = encrypted;
1382 int expire = default_expiration;
1383 char iabuf[INET_ADDRSTRLEN];
1386 if (peer->registerexpire > -1)
1387 ast_sched_del(sched, peer->registerexpire);
1388 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1389 ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
1390 snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
1391 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1392 if (inaddrcmp(&peer->addr, &trans->addr)) {
1393 if (option_verbose > 2)
1394 ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), iabuf, ntohs(trans->addr.sin_port));
1398 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1399 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1400 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1402 qualify_peer(peer, 1);
1406 case DUNDI_COMMAND_PRECACHE:
1407 /* Success of some sort */
1408 ast_log(LOG_DEBUG, "Looks like a precache with %d answers\n", ies.anscount);
1409 /* A dialplan or entity discover -- qualify by highest level entity */
1410 peer = find_peer(ies.eids[0]);
1411 if (peer && ies.called_number) {
1412 struct dundi_request dr;
1413 struct dundi_result dr2[1];
1414 memset(&dr, 0, sizeof(dr));
1415 memset(&dr2, 0, sizeof(dr2));
1416 /* Build placeholder Dundi Request */
1417 trans->us_eid = peer->us_eid;
1418 if (!ies.called_context)
1419 ies.called_context = "e164";
1420 strncpy(dr.dcontext, ies.called_context, sizeof(dr.dcontext) - 1);
1421 strncpy(dr.number, ies.called_number, sizeof(dr.number) - 1);
1423 trans->parent = &dr;
1425 /* Make sure we have all the proper auths */
1426 if (strlen(peer->inkey)) {
1427 authpass = encrypted;
1430 authpass &= has_permission(peer->include, ies.called_context);
1431 authpass &= peer->canprecache;
1433 /* Okay we're authentiated and all, now we check if they're authorized */
1434 for (x=0;x<ies.anscount;x++) {
1435 /* Copy into parent responses */
1436 trans->parent->dr[0].flags = ntohs(ies.answers[x]->flags);
1437 trans->parent->dr[0].techint = ies.answers[x]->protocol;
1438 trans->parent->dr[0].weight = ntohs(ies.answers[x]->weight);
1439 trans->parent->dr[0].eid = ies.answers[x]->eid;
1440 if (ies.expiration > 0)
1441 trans->parent->dr[0].expiration = ies.expiration;
1443 trans->parent->dr[0].expiration = DUNDI_DEFAULT_CACHE_TIME;
1444 dundi_eid_to_str(trans->parent->dr[0].eid_str,
1445 sizeof(trans->parent->dr[0].eid_str),
1446 &ies.answers[x]->eid);
1447 strncpy(trans->parent->dr[0].dest, ies.answers[x]->data,
1448 sizeof(trans->parent->dr[0].dest));
1449 strncpy(trans->parent->dr[0].tech, tech2str(ies.answers[x]->protocol),
1450 sizeof(trans->parent->dr[0].tech));
1451 trans->parent->respcount=1;
1452 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1453 the cache know if this request was unaffected by our entity list. */
1454 cache_save(&trans->them_eid, trans->parent, 0,
1455 ies.hint ? ntohs(ies.hint->flags) & DUNDI_HINT_UNAFFECTED : 0, ies.expiration);
1458 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1459 if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
1460 trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
1461 if (ntohs(ies.hint->flags) & DUNDI_HINT_DONT_ASK) {
1462 if (strlen(ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1463 strncpy(trans->parent->hmd->exten, ies.hint->data,
1464 sizeof(trans->parent->hmd->exten) - 1);
1467 trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
1472 if (!authpass && ies.eids[0])
1473 ast_log(LOG_NOTICE, "Peer '%s' does not have permission to pre-cache!\n",
1474 dundi_eid_to_str(eid_str, sizeof(eid_str), ies.eids[0]));
1475 /* Close connection if not final */
1477 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1479 case DUNDI_COMMAND_DPRESPONSE:
1480 /* A dialplan response, lets see what we got... */
1481 if (ies.cause < 1) {
1482 /* Success of some sort */
1483 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1484 if (trans->flags & FLAG_ENCRYPT) {
1485 authpass = encrypted;
1489 /* Pass back up answers */
1490 if (trans->parent && trans->parent->dr) {
1491 y = trans->parent->respcount;
1492 for (x=0;x<ies.anscount;x++) {
1493 if (trans->parent->respcount < trans->parent->maxcount) {
1494 /* Make sure it's not already there */
1495 for (z=0;z<trans->parent->respcount;z++) {
1496 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1497 !strcmp(trans->parent->dr[z].dest, ies.answers[x]->data))
1500 if (z == trans->parent->respcount) {
1501 /* Copy into parent responses */
1502 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1503 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1504 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1505 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1506 if (ies.expiration > 0)
1507 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1509 trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
1510 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1511 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1512 &ies.answers[x]->eid);
1513 strncpy(trans->parent->dr[trans->parent->respcount].dest, ies.answers[x]->data,
1514 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1515 strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1516 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1517 trans->parent->respcount++;
1518 trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
1519 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1520 /* Update weight if appropriate */
1521 trans->parent->dr[z].weight = ies.answers[x]->weight;
1524 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1525 trans->parent->number, trans->parent->dcontext);
1527 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1528 the cache know if this request was unaffected by our entity list. */
1529 cache_save(&trans->them_eid, trans->parent, y,
1530 ies.hint ? ntohs(ies.hint->flags) & DUNDI_HINT_UNAFFECTED : 0, ies.expiration);
1532 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1533 if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
1534 trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
1535 if (ntohs(ies.hint->flags) & DUNDI_HINT_DONT_ASK) {
1536 if (strlen(ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1537 strncpy(trans->parent->hmd->exten, ies.hint->data,
1538 sizeof(trans->parent->hmd->exten) - 1);
1541 trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
1544 if (ies.expiration > 0) {
1545 if (trans->parent->expiration > ies.expiration) {
1546 trans->parent->expiration = ies.expiration;
1550 /* Close connection if not final */
1552 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1556 /* Auth failure, check for data */
1558 /* Cancel if they didn't already */
1559 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1563 case DUNDI_COMMAND_EIDRESPONSE:
1564 /* A dialplan response, lets see what we got... */
1565 if (ies.cause < 1) {
1566 /* Success of some sort */
1567 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1568 if (trans->flags & FLAG_ENCRYPT) {
1569 authpass = encrypted;
1573 /* Pass back up answers */
1574 if (trans->parent && trans->parent->dei && ies.q_org) {
1575 if (!trans->parent->respcount) {
1576 trans->parent->respcount++;
1578 strncpy(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit) - 1);
1580 strncpy(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org) - 1);
1582 strncpy(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality) - 1);
1583 if (ies.q_stateprov)
1584 strncpy(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov) - 1);
1586 strncpy(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country) - 1);
1588 strncpy(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email) - 1);
1590 strncpy(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone) - 1);
1592 strncpy(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr) - 1);
1593 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1594 /* If it's them, update our address */
1595 ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
1596 trans->addr.sin_addr);
1600 if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
1601 trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
1604 /* Close connection if not final */
1606 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1610 /* Auth failure, check for data */
1612 /* Cancel if they didn't already */
1613 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1617 case DUNDI_COMMAND_REGRESPONSE:
1618 /* A dialplan response, lets see what we got... */
1619 if (ies.cause < 1) {
1621 /* Success of some sort */
1622 if (trans->flags & FLAG_ENCRYPT) {
1623 hasauth = encrypted;
1628 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1630 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1631 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1634 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),
1635 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1636 /* Close connection if not final */
1638 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1641 /* Auth failure, cancel if they didn't for some reason */
1643 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1647 case DUNDI_COMMAND_INVALID:
1648 case DUNDI_COMMAND_NULL:
1649 /* Do nothing special */
1651 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1653 case DUNDI_COMMAND_ENCREJ:
1654 if ((trans->flags & FLAG_SENDFULLKEY) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
1655 /* No really, it's over at this point */
1657 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1659 /* Send with full key */
1660 trans->flags |= FLAG_SENDFULLKEY;
1662 /* Ooops, we got a final message, start by sending ACK... */
1663 dundi_ack(trans, hdr->cmdresp & 0x80);
1664 trans->aseqno = trans->iseqno;
1665 /* Now, we gotta create a new transaction */
1666 if (!reset_transaction(trans)) {
1667 /* Make sure handle_frame doesn't destroy us */
1668 hdr->cmdresp &= 0x7f;
1669 /* Parse the message we transmitted */
1670 memset(&ies, 0, sizeof(ies));
1671 dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
1672 /* Reconstruct outgoing encrypted packet */
1673 memset(&ied, 0, sizeof(ied));
1674 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1675 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1676 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1678 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1679 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
1680 peer->sentfullkey = 1;
1685 case DUNDI_COMMAND_ENCRYPT:
1687 /* No nested encryption! */
1688 if ((trans->iseqno == 1) && !trans->oseqno) {
1689 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1690 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1691 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1693 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1697 apply_peer(trans, peer);
1698 /* Key passed, use new contexts for this session */
1699 trans->ecx = peer->them_ecx;
1700 trans->dcx = peer->them_dcx;
1702 if ((trans->flags & FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1703 struct dundi_hdr *dhdr;
1704 unsigned char decoded[MAX_PACKET_SIZE];
1706 ddatalen = sizeof(decoded);
1707 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1709 /* Handle decrypted response */
1711 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1712 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1713 /* Carry back final flag */
1714 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1717 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1721 /* Turn off encryption */
1722 trans->flags &= ~FLAG_ENCRYPT;
1723 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1727 /* Send unknown command if we don't know it, with final flag IFF it's the
1728 first command in the dialog and only if we haven't recieved final notification */
1730 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1731 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1737 static void destroy_packet(struct dundi_packet *pack, int needfree);
1738 static void destroy_packets(struct dundi_packet *p)
1740 struct dundi_packet *prev;
1744 if (prev->retransid > -1)
1745 ast_sched_del(sched, prev->retransid);
1751 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1753 /* Ack transmitted packet corresponding to iseqno */
1754 struct dundi_packet *pack;
1755 pack = trans->packets;
1757 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1758 destroy_packet(pack, 0);
1759 if (trans->lasttrans) {
1760 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1761 destroy_packets(trans->lasttrans);
1763 trans->lasttrans = pack;
1764 if (trans->autokillid > -1)
1765 ast_sched_del(sched, trans->autokillid);
1766 trans->autokillid = -1;
1774 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1776 struct dundi_transaction *trans;
1777 trans = find_transaction(h, sin);
1779 dundi_reject(h, sin);
1782 /* Got a transaction, see where this header fits in */
1783 if (h->oseqno == trans->iseqno) {
1784 /* Just what we were looking for... Anything but ack increments iseqno */
1785 if (ack_trans(trans, h->iseqno) && (trans->flags & FLAG_FINAL)) {
1786 /* If final, we're done */
1787 destroy_trans(trans, 0);
1790 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1791 trans->oiseqno = trans->iseqno;
1793 handle_command_response(trans, h, datalen, 0);
1795 if (trans->aseqno != trans->iseqno) {
1796 dundi_ack(trans, h->cmdresp & 0x80);
1797 trans->aseqno = trans->iseqno;
1799 /* Delete any saved last transmissions */
1800 destroy_packets(trans->lasttrans);
1801 trans->lasttrans = NULL;
1802 if (h->cmdresp & 0x80) {
1803 /* Final -- destroy now */
1804 destroy_trans(trans, 0);
1806 } else if (h->oseqno == trans->oiseqno) {
1807 /* Last incoming sequence number -- send ACK without processing */
1808 dundi_ack(trans, 0);
1810 /* Out of window -- simply drop */
1811 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
1816 static int socket_read(int *id, int fd, short events, void *cbdata)
1818 struct sockaddr_in sin;
1820 struct dundi_hdr *h;
1821 unsigned char buf[MAX_PACKET_SIZE];
1824 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1826 if (errno != ECONNREFUSED)
1827 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1830 if (res < sizeof(struct dundi_hdr)) {
1831 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
1835 h = (struct dundi_hdr *)buf;
1837 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
1838 ast_mutex_lock(&peerlock);
1839 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
1840 ast_mutex_unlock(&peerlock);
1844 static void build_secret(char *secret, int seclen)
1850 ast_base64encode(secret ,tmp, sizeof(tmp), seclen);
1851 /* Eliminate potential bad characters */
1852 while((s = strchr(secret, ';'))) *s = '+';
1853 while((s = strchr(secret, '/'))) *s = '+';
1854 while((s = strchr(secret, ':'))) *s = '+';
1855 while((s = strchr(secret, '@'))) *s = '+';
1859 static void save_secret(const char *newkey, const char *oldkey)
1863 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
1865 snprintf(tmp, sizeof(tmp), "%s", newkey);
1866 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
1867 ast_db_put(secretpath, "secret", tmp);
1868 snprintf(tmp, sizeof(tmp), "%ld", rotatetime);
1869 ast_db_put(secretpath, "secretexpiry", tmp);
1872 static void load_password(void)
1879 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
1880 if (sscanf(tmp, "%ld", &expired) == 1) {
1881 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
1882 current = strchr(tmp, ';');
1889 if ((time(NULL) - expired) < 0) {
1890 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
1891 expired = time(NULL) + DUNDI_SECRET_TIME;
1892 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
1901 /* Current key is still valid, just setup rotatation properly */
1902 strncpy(cursecret, current, sizeof(cursecret) - 1);
1903 rotatetime = expired;
1905 /* Current key is out of date, rotate or eliminate all together */
1906 build_secret(cursecret, sizeof(cursecret));
1907 save_secret(cursecret, last);
1911 static void check_password(void)
1918 printf("%ld/%ld\n", now, rotatetime);
1920 if ((now - rotatetime) >= 0) {
1921 /* Time to rotate keys */
1922 strncpy(oldsecret, cursecret, sizeof(oldsecret) - 1);
1923 build_secret(cursecret, sizeof(cursecret));
1924 save_secret(cursecret, oldsecret);
1928 static void *network_thread(void *ignore)
1930 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
1931 from the network, and queue them for delivery to the channels */
1933 /* Establish I/O callback for socket read */
1934 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
1936 res = ast_sched_wait(sched);
1937 if ((res > 1000) || (res < 0))
1939 res = ast_io_wait(io, res);
1941 ast_mutex_lock(&peerlock);
1942 ast_sched_runq(sched);
1943 ast_mutex_unlock(&peerlock);
1950 static int start_network_thread(void)
1952 return ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
1955 static int dundi_do_debug(int fd, int argc, char *argv[])
1958 return RESULT_SHOWUSAGE;
1960 ast_cli(fd, "DUNDi Debugging Enabled\n");
1961 return RESULT_SUCCESS;
1964 static int dundi_do_store_history(int fd, int argc, char *argv[])
1967 return RESULT_SHOWUSAGE;
1968 global_storehistory = 1;
1969 ast_cli(fd, "DUNDi History Storage Enabled\n");
1970 return RESULT_SUCCESS;
1973 static int dundi_flush(int fd, int argc, char *argv[])
1976 if ((argc < 2) || (argc > 3))
1977 return RESULT_SHOWUSAGE;
1979 if (!strcasecmp(argv[2], "stats"))
1982 return RESULT_SHOWUSAGE;
1985 /* Flush statistics */
1986 struct dundi_peer *p;
1988 ast_mutex_lock(&peerlock);
1991 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
1993 free(p->lookups[x]);
1994 p->lookups[x] = NULL;
1995 p->lookuptimes[x] = 0;
2000 ast_mutex_unlock(&peerlock);
2002 ast_db_deltree("dundi/cache", NULL);
2003 ast_cli(fd, "DUNDi Cache Flushed\n");
2005 return RESULT_SUCCESS;
2008 static int dundi_no_debug(int fd, int argc, char *argv[])
2011 return RESULT_SHOWUSAGE;
2013 ast_cli(fd, "DUNDi Debugging Disabled\n");
2014 return RESULT_SUCCESS;
2017 static int dundi_no_store_history(int fd, int argc, char *argv[])
2020 return RESULT_SHOWUSAGE;
2021 global_storehistory = 0;
2022 ast_cli(fd, "DUNDi History Storage Disabled\n");
2023 return RESULT_SUCCESS;
2026 static char *model2str(int model)
2029 case DUNDI_MODEL_INBOUND:
2031 case DUNDI_MODEL_OUTBOUND:
2033 case DUNDI_MODEL_SYMMETRIC:
2040 static char *complete_peer_helper(char *line, char *word, int pos, int state, int rpos)
2044 struct dundi_peer *p;
2048 ast_mutex_lock(&peerlock);
2051 if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
2052 if (++which > state)
2058 ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
2061 ast_mutex_unlock(&peerlock);
2065 static char *complete_peer_4(char *line, char *word, int pos, int state)
2067 return complete_peer_helper(line, word, pos, state, 3);
2070 static int rescomp(const void *a, const void *b)
2072 const struct dundi_result *resa, *resb;
2075 if (resa->weight < resb->weight)
2077 if (resa->weight > resb->weight)
2082 static void sort_results(struct dundi_result *results, int count)
2084 qsort(results, count, sizeof(results[0]), rescomp);
2087 static int dundi_do_lookup(int fd, int argc, char *argv[])
2095 struct dundi_result dr[MAX_RESULTS];
2096 struct timeval start;
2097 if ((argc < 3) || (argc > 4))
2098 return RESULT_SHOWUSAGE;
2100 if (!strcasecmp(argv[3], "bypass"))
2103 return RESULT_SHOWUSAGE;
2105 strncpy(tmp, argv[2], sizeof(tmp) - 1);
2106 context = strchr(tmp, '@');
2111 gettimeofday(&start, NULL);
2112 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2115 ast_cli(fd, "DUNDi lookup returned error.\n");
2117 ast_cli(fd, "DUNDi lookup returned no results.\n");
2119 sort_results(dr, res);
2120 for (x=0;x<res;x++) {
2121 ast_cli(fd, "%d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
2123 ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
2124 return RESULT_SUCCESS;
2127 static int dundi_do_query(int fd, int argc, char *argv[])
2133 struct dundi_entity_info dei;
2134 if ((argc < 3) || (argc > 3))
2135 return RESULT_SHOWUSAGE;
2136 if (dundi_str_to_eid(&eid, argv[2])) {
2137 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2138 return RESULT_SHOWUSAGE;
2140 strncpy(tmp, argv[2], sizeof(tmp) - 1);
2141 context = strchr(tmp, '@');
2146 res = dundi_query_eid(&dei, context, eid);
2148 ast_cli(fd, "DUNDi Query EID returned error.\n");
2150 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2152 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2153 ast_cli(fd, "Department: %s\n", dei.orgunit);
2154 ast_cli(fd, "Organization: %s\n", dei.org);
2155 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2156 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2157 ast_cli(fd, "Country: %s\n", dei.country);
2158 ast_cli(fd, "E-mail: %s\n", dei.email);
2159 ast_cli(fd, "Phone: %s\n", dei.phone);
2160 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2162 return RESULT_SUCCESS;
2165 static int dundi_show_peer(int fd, int argc, char *argv[])
2167 struct dundi_peer *peer;
2168 struct permission *p;
2170 char iabuf[INET_ADDRSTRLEN];
2175 return RESULT_SHOWUSAGE;
2176 ast_mutex_lock(&peerlock);
2179 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2184 switch(peer->order) {
2189 order = "Secondary";
2195 order = "Quartiary";
2200 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2201 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2202 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
2203 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2204 ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
2205 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2206 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2207 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2208 if (peer->include) {
2209 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2213 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2217 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2221 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2225 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2226 if (peer->lookups[x]) {
2228 ast_cli(fd, "Last few query times:\n");
2229 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2234 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2236 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2237 ast_mutex_unlock(&peerlock);
2238 return RESULT_SUCCESS;
2241 static int dundi_show_peers(int fd, int argc, char *argv[])
2243 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2244 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2245 struct dundi_peer *peer;
2246 char iabuf[INET_ADDRSTRLEN];
2247 int registeredonly=0;
2250 if ((argc != 3) && (argc != 4) && (argc != 5))
2251 return RESULT_SHOWUSAGE;
2253 if (!strcasecmp(argv[3], "registered")) {
2256 return RESULT_SHOWUSAGE;
2258 ast_mutex_lock(&peerlock);
2259 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2260 for (peer = peers;peer;peer = peer->next) {
2261 char status[20] = "";
2262 int print_line = -1;
2263 char srch[2000] = "";
2264 if (registeredonly && !peer->addr.sin_addr.s_addr)
2267 if (peer->lastms < 0)
2268 strncpy(status, "UNREACHABLE", sizeof(status) - 1);
2269 else if (peer->lastms > peer->maxms)
2270 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2271 else if (peer->lastms)
2272 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2274 strncpy(status, "UNKNOWN", sizeof(status) - 1);
2276 strncpy(status, "Unmonitored", sizeof(status) - 1);
2278 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2280 strcpy(avgms, "Unavail");
2281 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2282 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2283 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2286 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2288 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2290 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2298 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2299 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2300 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2303 ast_mutex_unlock(&peerlock);
2304 return RESULT_SUCCESS;
2309 static int dundi_show_trans(int fd, int argc, char *argv[])
2311 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2312 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2313 struct dundi_transaction *trans;
2314 char iabuf[INET_ADDRSTRLEN];
2316 return RESULT_SHOWUSAGE;
2317 ast_mutex_lock(&peerlock);
2318 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2319 for (trans = alltrans;trans;trans = trans->allnext) {
2320 ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr),
2321 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2323 ast_mutex_unlock(&peerlock);
2324 return RESULT_SUCCESS;
2329 static int dundi_show_entityid(int fd, int argc, char *argv[])
2333 return RESULT_SHOWUSAGE;
2334 ast_mutex_lock(&peerlock);
2335 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2336 ast_mutex_unlock(&peerlock);
2337 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2338 return RESULT_SUCCESS;
2341 static int dundi_show_requests(int fd, int argc, char *argv[])
2343 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2344 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2345 struct dundi_request *req;
2348 return RESULT_SHOWUSAGE;
2349 ast_mutex_lock(&peerlock);
2350 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2351 for (req = requests;req;req = req->next) {
2352 ast_cli(fd, FORMAT, req->number, req->dcontext,
2353 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2355 ast_mutex_unlock(&peerlock);
2356 return RESULT_SUCCESS;
2361 /* Grok-a-dial DUNDi */
2363 static int dundi_show_mappings(int fd, int argc, char *argv[])
2365 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2366 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2367 struct dundi_mapping *map;
2370 return RESULT_SHOWUSAGE;
2371 ast_mutex_lock(&peerlock);
2372 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2373 for (map = mappings;map;map = map->next) {
2374 ast_cli(fd, FORMAT, map->dcontext, map->weight,
2375 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2376 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2378 ast_mutex_unlock(&peerlock);
2379 return RESULT_SUCCESS;
2384 static char debug_usage[] =
2385 "Usage: dundi debug\n"
2386 " Enables dumping of DUNDi packets for debugging purposes\n";
2388 static char no_debug_usage[] =
2389 "Usage: dundi no debug\n"
2390 " Disables dumping of DUNDi packets for debugging purposes\n";
2392 static char store_history_usage[] =
2393 "Usage: dundi store history\n"
2394 " Enables storing of DUNDi requests and times for debugging\n"
2397 static char no_store_history_usage[] =
2398 "Usage: dundi no store history\n"
2399 " Disables storing of DUNDi requests and times for debugging\n"
2402 static char show_peers_usage[] =
2403 "Usage: dundi show peers\n"
2404 " Lists all known DUNDi peers.\n";
2406 static char show_trans_usage[] =
2407 "Usage: dundi show trans\n"
2408 " Lists all known DUNDi transactions.\n";
2410 static char show_mappings_usage[] =
2411 "Usage: dundi show mappings\n"
2412 " Lists all known DUNDi mappings.\n";
2414 static char show_entityid_usage[] =
2415 "Usage: dundi show entityid\n"
2416 " Displays the global entityid for this host.\n";
2418 static char show_peer_usage[] =
2419 "Usage: dundi show peer [peer]\n"
2420 " Provide a detailed description of a specifid DUNDi peer.\n";
2422 static char show_requests_usage[] =
2423 "Usage: dundi show requests\n"
2424 " Lists all known pending DUNDi requests.\n";
2426 static char lookup_usage[] =
2427 "Usage: dundi lookup <number>[@context] [bypass]\n"
2428 " Lookup the given number within the given DUNDi context\n"
2429 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2430 "keyword is specified.\n";
2432 static char query_usage[] =
2433 "Usage: dundi query <entity>[@context]\n"
2434 " Attempts to retrieve contact information for a specific\n"
2435 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2436 "e164 if none is specified).\n";
2438 static char flush_usage[] =
2439 "Usage: dundi flush [stats]\n"
2440 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2441 "'stats' is present, clears timer statistics instead of normal\n"
2444 static struct ast_cli_entry cli_debug =
2445 { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2447 static struct ast_cli_entry cli_store_history =
2448 { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2450 static struct ast_cli_entry cli_no_store_history =
2451 { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2453 static struct ast_cli_entry cli_flush =
2454 { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2456 static struct ast_cli_entry cli_no_debug =
2457 { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2459 static struct ast_cli_entry cli_show_peers =
2460 { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2462 static struct ast_cli_entry cli_show_trans =
2463 { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2465 static struct ast_cli_entry cli_show_entityid =
2466 { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2468 static struct ast_cli_entry cli_show_mappings =
2469 { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2471 static struct ast_cli_entry cli_show_requests =
2472 { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2474 static struct ast_cli_entry cli_show_peer =
2475 { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2477 static struct ast_cli_entry cli_lookup =
2478 { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2480 static struct ast_cli_entry cli_queryeid =
2481 { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2483 STANDARD_LOCAL_USER;
2487 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2489 struct dundi_transaction *trans;
2492 /* Don't allow creation of transactions to non-registered peers */
2493 if (p && !p->addr.sin_addr.s_addr)
2495 tid = get_trans_id();
2498 trans = malloc(sizeof(struct dundi_transaction));
2500 memset(trans, 0, sizeof(struct dundi_transaction));
2501 if (global_storehistory) {
2502 gettimeofday(&trans->start, NULL);
2503 trans->flags |= FLAG_STOREHIST;
2505 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2506 trans->autokillid = -1;
2508 apply_peer(trans, p);
2509 if (!p->sentfullkey)
2510 trans->flags |= FLAG_SENDFULLKEY;
2512 trans->strans = tid;
2513 trans->allnext = alltrans;
2519 static int dundi_xmit(struct dundi_packet *pack)
2522 char iabuf[INET_ADDRSTRLEN];
2524 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2525 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2527 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2528 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2529 ntohs(pack->parent->addr.sin_port), strerror(errno));
2536 static void destroy_packet(struct dundi_packet *pack, int needfree)
2538 struct dundi_packet *prev, *cur;
2541 cur = pack->parent->packets;
2545 prev->next = cur->next;
2547 pack->parent->packets = cur->next;
2554 if (pack->retransid > -1)
2555 ast_sched_del(sched, pack->retransid);
2559 pack->retransid = -1;
2564 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2566 struct dundi_transaction *cur, *prev;
2567 struct dundi_peer *peer;
2573 if (trans->flags & (FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2576 if (peer->regtrans == trans)
2577 peer->regtrans = NULL;
2578 if (peer->keypending == trans)
2579 peer->keypending = NULL;
2580 if (peer->qualtrans == trans) {
2582 if (peer->lastms > -1)
2583 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2586 gettimeofday(&tv, NULL);
2587 ms = (tv.tv_sec - peer->qualtx.tv_sec) * 1000 +
2588 (tv.tv_usec - peer->qualtx.tv_usec) / 1000;
2591 if (ms < peer->maxms) {
2592 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2593 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2594 } else if (peer->lastms < peer->maxms) {
2595 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);
2599 peer->qualtrans = NULL;
2601 if (trans->flags & FLAG_STOREHIST) {
2602 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2603 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2606 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2607 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2608 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2609 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2610 peer->lookups[x] = peer->lookups[x-1];
2611 if (peer->lookups[x]) {
2612 peer->avgms += peer->lookuptimes[x];
2616 peer->lookuptimes[0] = calc_ms(&trans->start);
2617 peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2618 if (peer->lookups[0]) {
2619 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2620 peer->avgms += peer->lookuptimes[0];
2631 if (trans->parent) {
2632 /* Unlink from parent if appropriate */
2634 cur = trans->parent->trans;
2638 prev->next = trans->next;
2640 trans->parent->trans = trans->next;
2646 if (!trans->parent->trans) {
2647 /* Wake up sleeper */
2648 if (trans->parent->pfds[1] > -1) {
2649 write(trans->parent->pfds[1], "killa!", 6);
2653 /* Unlink from all trans */
2659 prev->allnext = trans->allnext;
2661 alltrans = trans->allnext;
2667 destroy_packets(trans->packets);
2668 destroy_packets(trans->lasttrans);
2669 trans->packets = NULL;
2670 if (trans->autokillid > -1)
2671 ast_sched_del(sched, trans->autokillid);
2672 trans->autokillid = -1;
2673 if (trans->thread) {
2674 /* If used by a thread, mark as dead and be done */
2675 trans->flags |= FLAG_DEAD;
2680 static int dundi_rexmit(void *data)
2682 struct dundi_packet *pack;
2683 char iabuf[INET_ADDRSTRLEN];
2685 ast_mutex_lock(&peerlock);
2687 if (pack->retrans < 1) {
2688 pack->retransid = -1;
2689 if (!(pack->parent->flags & FLAG_ISQUAL))
2690 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
2691 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2692 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2693 destroy_trans(pack->parent, 1);
2696 /* Decrement retransmission, try again */
2701 ast_mutex_unlock(&peerlock);
2705 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2707 struct dundi_packet *pack;
2711 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2712 /* Reserve enough space for encryption */
2713 if (trans->flags & FLAG_ENCRYPT)
2717 memset(pack, 0, len);
2718 pack->h = (struct dundi_hdr *)(pack->data);
2719 if (cmdresp != DUNDI_COMMAND_ACK) {
2720 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
2721 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
2722 pack->next = trans->packets;
2723 trans->packets = pack;
2725 pack->parent = trans;
2726 pack->h->strans = htons(trans->strans);
2727 pack->h->dtrans = htons(trans->dtrans);
2728 pack->h->iseqno = trans->iseqno;
2729 pack->h->oseqno = trans->oseqno;
2730 pack->h->cmdresp = cmdresp;
2731 pack->datalen = sizeof(struct dundi_hdr);
2733 memcpy(pack->h->ies, ied->buf, ied->pos);
2734 pack->datalen += ied->pos;
2737 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
2738 trans->flags |= FLAG_FINAL;
2740 pack->h->cmdflags = flags;
2741 if (cmdresp != DUNDI_COMMAND_ACK) {
2743 trans->oseqno = trans->oseqno % 256;
2745 trans->aseqno = trans->iseqno;
2746 /* If we have their public key, encrypt */
2747 if (trans->flags & FLAG_ENCRYPT) {
2749 case DUNDI_COMMAND_REGREQ:
2750 case DUNDI_COMMAND_REGRESPONSE:
2751 case DUNDI_COMMAND_DPDISCOVER:
2752 case DUNDI_COMMAND_DPRESPONSE:
2753 case DUNDI_COMMAND_EIDQUERY:
2754 case DUNDI_COMMAND_EIDRESPONSE:
2756 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
2757 res = dundi_encrypt(trans, pack);
2765 res = dundi_xmit(pack);
2767 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
2769 if (cmdresp == DUNDI_COMMAND_ACK)
2776 static int do_autokill(void *data)
2778 struct dundi_transaction *trans = data;
2780 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
2781 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
2782 trans->autokillid = -1;
2783 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
2787 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
2789 struct dundi_peer *p;
2790 if (!dundi_eid_cmp(eid, us)) {
2791 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
2794 ast_mutex_lock(&peerlock);
2797 if (!dundi_eid_cmp(&p->eid, eid)) {
2798 if (has_permission(p->include, context))
2799 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
2801 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
2807 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
2808 ast_mutex_unlock(&peerlock);
2811 static int dundi_discover(struct dundi_transaction *trans)
2813 struct dundi_ie_data ied;
2815 if (!trans->parent) {
2816 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
2819 memset(&ied, 0, sizeof(ied));
2820 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
2821 if (!dundi_eid_zero(&trans->us_eid))
2822 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
2823 for (x=0;x<trans->eidcount;x++)
2824 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
2825 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
2826 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
2827 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
2828 if (trans->parent->cbypass)
2829 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
2830 if (trans->autokilltimeout)
2831 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
2832 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
2835 static int dundi_query(struct dundi_transaction *trans)
2837 struct dundi_ie_data ied;
2839 if (!trans->parent) {
2840 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
2843 memset(&ied, 0, sizeof(ied));
2844 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
2845 if (!dundi_eid_zero(&trans->us_eid))
2846 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
2847 for (x=0;x<trans->eidcount;x++)
2848 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
2849 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
2850 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
2851 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
2852 if (trans->autokilltimeout)
2853 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
2854 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
2857 static int discover_transactions(struct dundi_request *dr)
2859 struct dundi_transaction *trans;
2860 ast_mutex_lock(&peerlock);
2863 dundi_discover(trans);
2864 trans = trans->next;
2866 ast_mutex_unlock(&peerlock);
2870 static int query_transactions(struct dundi_request *dr)
2872 struct dundi_transaction *trans;
2873 ast_mutex_lock(&peerlock);
2877 trans = trans->next;
2879 ast_mutex_unlock(&peerlock);
2883 static int optimize_transactions(struct dundi_request *dr, int order)
2885 /* Minimize the message propagation through DUNDi by
2886 alerting the network to hops which should be not be considered */
2887 struct dundi_transaction *trans;
2888 struct dundi_peer *peer;
2892 ast_mutex_lock(&peerlock);
2895 /* Pop off the true root */
2896 if (trans->eidcount) {
2897 tmp = trans->eids[--trans->eidcount];
2900 tmp = trans->us_eid;
2906 if (has_permission(peer->include, dr->dcontext) &&
2907 dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
2908 (peer->order <= order)) {
2909 /* For each other transaction, make sure we don't
2910 ask this EID about the others if they're not