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__) || defined(__NetBSD__)
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;
106 static pthread_t precachethreadid = AST_PTHREADT_NULL;
108 static int dundidebug = 0;
109 static int authdebug = 0;
110 static int dundi_ttl = DUNDI_DEFAULT_TTL;
111 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
112 static int global_autokilltimeout = 0;
113 static dundi_eid global_eid;
114 static int default_expiration = 60;
115 static int global_storehistory = 0;
116 static char dept[80];
118 static char locality[80];
119 static char stateprov[80];
120 static char country[80];
121 static char email[80];
122 static char phone[80];
123 static char secretpath[80];
124 static char cursecret[80];
125 static char ipaddr[80];
126 static time_t rotatetime;
127 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
129 struct permission *next;
134 struct dundi_packet {
136 struct dundi_packet *next;
138 struct dundi_transaction *parent;
141 unsigned char data[0];
144 struct dundi_hint_metadata {
145 unsigned short flags;
146 char exten[AST_MAX_EXTENSION];
149 struct dundi_precache_queue {
150 struct dundi_precache_queue *next;
156 struct dundi_request;
158 struct dundi_transaction {
159 struct sockaddr_in addr; /* Other end of transaction */
160 struct timeval start; /* When this transaction was created */
161 dundi_eid eids[DUNDI_MAX_STACK + 1];
162 int eidcount; /* Number of eids in eids */
163 dundi_eid us_eid; /* Our EID, to them */
164 dundi_eid them_eid; /* Their EID, to us */
165 aes_encrypt_ctx ecx; /* AES 128 Encryption context */
166 aes_decrypt_ctx dcx; /* AES 128 Decryption context */
167 int flags; /* Has final packet been sent */
168 int ttl; /* Remaining TTL for queries on this one */
169 int thread; /* We have a calling thread */
170 int retranstimer; /* How long to wait before retransmissions */
171 int autokillid; /* ID to kill connection if answer doesn't come back fast enough */
172 int autokilltimeout; /* Recommended timeout for autokill */
173 unsigned short strans; /* Our transaction identifier */
174 unsigned short dtrans; /* Their transaction identifer */
175 unsigned char iseqno; /* Next expected received seqno */
176 unsigned char oiseqno; /* Last received incoming seqno */
177 unsigned char oseqno; /* Next transmitted seqno */
178 unsigned char aseqno; /* Last acknowledge seqno */
179 struct dundi_packet *packets; /* Packets to be retransmitted */
180 struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
181 struct dundi_transaction *next; /* Next with respect to the parent */
182 struct dundi_request *parent; /* Parent request (if there is one) */
183 struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
186 struct dundi_request {
187 char dcontext[AST_MAX_EXTENSION];
188 char number[AST_MAX_EXTENSION];
191 struct dundi_result *dr;
192 struct dundi_entity_info *dei;
193 struct dundi_hint_metadata *hmd;
199 unsigned long crc32; /* CRC-32 of all but root EID's in avoid list */
200 struct dundi_transaction *trans; /* Transactions */
201 struct dundi_request *next;
204 static struct dundi_mapping {
205 char dcontext[AST_MAX_EXTENSION];
206 char lcontext[AST_MAX_EXTENSION];
211 char dest[AST_MAX_EXTENSION];
212 struct dundi_mapping *next;
215 static struct dundi_peer {
217 struct sockaddr_in addr; /* Address of DUNDi peer */
218 struct permission *permit;
219 struct permission *include;
220 struct permission *precachesend;
221 struct permission *precachereceive;
230 unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
231 unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
232 unsigned long us_keycrc32; /* CRC-32 of our key */
233 aes_encrypt_ctx us_ecx; /* Cached AES 128 Encryption context */
234 aes_decrypt_ctx us_dcx; /* Cached AES 128 Decryption context */
235 unsigned long them_keycrc32;/* CRC-32 of our key */
236 aes_encrypt_ctx them_ecx; /* Cached AES 128 Encryption context */
237 aes_decrypt_ctx them_dcx; /* Cached AES 128 Decryption context */
238 time_t keyexpire; /* When to expire/recreate key */
240 int lookuptimes[DUNDI_TIMING_HISTORY];
241 char *lookups[DUNDI_TIMING_HISTORY];
243 struct dundi_transaction *regtrans; /* Registration transaction */
244 struct dundi_transaction *qualtrans; /* Qualify transaction */
245 struct dundi_transaction *keypending;
246 int model; /* Pull model */
247 int pcmodel; /* Push/precache model */
248 int dynamic; /* Are we dynamic? */
249 int lastms; /* Last measured latency */
250 int maxms; /* Max permissible latency */
251 struct timeval qualtx; /* Time of transmit */
252 struct dundi_peer *next;
255 static struct dundi_precache_queue *pcq;
257 AST_MUTEX_DEFINE_STATIC(peerlock);
258 AST_MUTEX_DEFINE_STATIC(pclock);
260 static int dundi_xmit(struct dundi_packet *pack);
262 static void dundi_debug_output(const char *data)
265 ast_verbose("%s", data);
268 static void dundi_error_output(const char *data)
270 ast_log(LOG_WARNING, "%s", data);
273 static int has_permission(struct permission *ps, char *cont)
277 if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
284 static char *tech2str(int tech)
287 case DUNDI_PROTO_NONE:
289 case DUNDI_PROTO_IAX:
291 case DUNDI_PROTO_SIP:
293 case DUNDI_PROTO_H323:
300 static int str2tech(char *str)
302 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
303 return DUNDI_PROTO_IAX;
304 else if (!strcasecmp(str, "SIP"))
305 return DUNDI_PROTO_SIP;
306 else if (!strcasecmp(str, "H323"))
307 return DUNDI_PROTO_H323;
312 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[]);
313 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
314 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
315 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
317 /* Look for an exact match first */
318 struct dundi_transaction *trans;
321 if (!inaddrcmp(&trans->addr, sin) &&
322 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
323 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
325 trans->dtrans = ntohs(hdr->strans) & 32767;
328 trans = trans->allnext;
331 switch(hdr->cmdresp & 0x7f) {
332 case DUNDI_COMMAND_DPDISCOVER:
333 case DUNDI_COMMAND_EIDQUERY:
334 case DUNDI_COMMAND_PRECACHERQ:
335 case DUNDI_COMMAND_REGREQ:
336 case DUNDI_COMMAND_NULL:
337 case DUNDI_COMMAND_ENCRYPT:
339 /* Create new transaction */
340 trans = create_transaction(NULL);
342 memcpy(&trans->addr, sin, sizeof(trans->addr));
343 trans->dtrans = ntohs(hdr->strans) & 32767;
345 ast_log(LOG_WARNING, "Out of memory!\n");
355 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
357 static int dundi_ack(struct dundi_transaction *trans, int final)
359 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
361 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
364 struct dundi_packet pack;
365 struct dundi_hdr hdr;
367 struct dundi_transaction trans;
368 /* Never respond to an INVALID with another INVALID */
369 if (h->cmdresp == DUNDI_COMMAND_INVALID)
371 memset(&tmp, 0, sizeof(tmp));
372 memset(&trans, 0, sizeof(trans));
373 memcpy(&trans.addr, sin, sizeof(trans.addr));
374 tmp.hdr.strans = h->dtrans;
375 tmp.hdr.dtrans = h->strans;
376 tmp.hdr.iseqno = h->oseqno;
377 tmp.hdr.oseqno = h->iseqno;
378 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
379 tmp.hdr.cmdflags = 0;
380 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
381 tmp.pack.datalen = sizeof(struct dundi_hdr);
382 tmp.pack.parent = &trans;
383 dundi_xmit(&tmp.pack);
386 static void reset_global_eid(void)
388 #if defined(SIOCGIFHWADDR)
393 s = socket(AF_INET, SOCK_STREAM, 0);
397 memset(&ifr, 0, sizeof(ifr));
398 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
399 if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
400 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
401 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);
407 #if defined(ifa_broadaddr)
409 struct ifaddrs *ifap;
411 if (getifaddrs(&ifap) == 0) {
413 for (p = ifap; p; p = p->ifa_next) {
414 if (p->ifa_addr->sa_family == AF_LINK) {
415 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
418 sdp->sdl_data + sdp->sdl_nlen, 6);
419 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);
428 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
431 static int get_trans_id(void)
433 struct dundi_transaction *t;
434 int stid = (rand() % 32766) + 1;
439 if (t->strans == tid)
445 tid = (tid % 32766) + 1;
446 } while (tid != stid);
450 static int reset_transaction(struct dundi_transaction *trans)
453 tid = get_trans_id();
462 trans->flags &= ~FLAG_FINAL;
466 static struct dundi_peer *find_peer(dundi_eid *eid)
468 struct dundi_peer *cur;
473 if (!dundi_eid_cmp(&cur->eid,eid))
480 static void build_iv(unsigned char *iv)
482 /* XXX Would be nice to be more random XXX */
483 unsigned int *fluffy;
485 fluffy = (unsigned int *)(iv);
490 struct dundi_query_state {
491 dundi_eid *eids[DUNDI_MAX_STACK + 1];
492 int directs[DUNDI_MAX_STACK + 1];
494 char called_context[AST_MAX_EXTENSION];
495 char called_number[AST_MAX_EXTENSION];
496 struct dundi_mapping *maps;
499 struct dundi_transaction *trans;
506 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)
510 if (!ast_strlen_zero(map->lcontext)) {
512 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
513 flags |= DUNDI_FLAG_EXISTS;
514 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
515 flags |= DUNDI_FLAG_CANMATCH;
516 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
517 flags |= DUNDI_FLAG_MATCHMORE;
518 if (ast_ignore_pattern(map->lcontext, called_number))
519 flags |= DUNDI_FLAG_IGNOREPAT;
521 /* Clearly we can't say 'don't ask' anymore if we found anything... */
523 hmd->flags &= ~DUNDI_HINT_DONT_ASK;
525 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
526 /* Skip partial answers */
527 flags &= ~(DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
530 struct varshead headp;
531 struct ast_var_t *newvariable;
532 flags |= map->options & 0xffff;
533 dr[anscnt].flags = flags;
534 dr[anscnt].techint = map->tech;
535 dr[anscnt].weight = map->weight;
536 dr[anscnt].expiration = DUNDI_DEFAULT_CACHE_TIME;
537 strncpy(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
538 dr[anscnt].eid = *us_eid;
539 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
540 if (flags & DUNDI_FLAG_EXISTS) {
541 AST_LIST_HEAD_INIT(&headp);
542 newvariable = ast_var_assign("NUMBER", called_number);
543 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
544 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
545 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
546 newvariable = ast_var_assign("SECRET", cursecret);
547 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
548 newvariable = ast_var_assign("IPADDR", ipaddr);
549 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
550 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
551 while (!AST_LIST_EMPTY(&headp)) { /* List Deletion. */
552 newvariable = AST_LIST_FIRST(&headp);
553 AST_LIST_REMOVE_HEAD(&headp, entries);
554 ast_var_delete(newvariable);
557 dr[anscnt].dest[0] = '\0';
560 /* No answers... Find the fewest number of digits from the
561 number for which we have no answer. */
562 char tmp[AST_MAX_EXTENSION]="";
563 for (x=0;x<AST_MAX_EXTENSION;x++) {
564 tmp[x] = called_number[x];
567 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
568 /* Oops found something we can't match. If this is longer
569 than the running hint, we have to consider it */
570 if (strlen(tmp) > strlen(hmd->exten)) {
571 strncpy(hmd->exten, tmp, sizeof(hmd->exten) - 1);
581 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
583 static void *dundi_lookup_thread(void *data)
585 struct dundi_query_state *st = data;
586 struct dundi_result dr[MAX_RESULTS];
587 struct dundi_ie_data ied;
588 struct dundi_hint_metadata hmd;
593 int expiration = DUNDI_DEFAULT_CACHE_TIME;
595 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
596 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
597 memset(&ied, 0, sizeof(ied));
598 memset(&dr, 0, sizeof(dr));
599 memset(&hmd, 0, sizeof(hmd));
600 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
601 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
602 for (x=0;x<st->nummaps;x++)
603 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
606 for (x=0;x<ouranswers;x++) {
607 if (dr[x].weight < max)
612 /* If we do not have a canonical result, keep looking */
613 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);
615 /* Append answer in result */
618 if ((res < -1) && (!ouranswers))
619 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
622 ast_mutex_lock(&peerlock);
623 /* Truncate if "don't ask" isn't present */
624 if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
626 if (st->trans->flags & FLAG_DEAD) {
627 ast_log(LOG_DEBUG, "Our transaction went away!\n");
628 st->trans->thread = 0;
629 destroy_trans(st->trans, 0);
631 for (x=0;x<ouranswers;x++) {
633 if (dr[x].expiration && (expiration > dr[x].expiration))
634 expiration = dr[x].expiration;
635 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
637 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
638 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
639 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
640 st->trans->thread = 0;
642 ast_mutex_unlock(&peerlock);
647 static void *dundi_precache_thread(void *data)
649 struct dundi_query_state *st = data;
650 struct dundi_ie_data ied;
651 struct dundi_hint_metadata hmd;
654 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
655 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
656 memset(&ied, 0, sizeof(ied));
658 /* Now produce precache */
659 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
661 ast_mutex_lock(&peerlock);
662 /* Truncate if "don't ask" isn't present */
663 if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
665 if (st->trans->flags & FLAG_DEAD) {
666 ast_log(LOG_DEBUG, "Our transaction went away!\n");
667 st->trans->thread = 0;
668 destroy_trans(st->trans, 0);
670 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
671 st->trans->thread = 0;
673 ast_mutex_unlock(&peerlock);
678 static inline int calc_ms(struct timeval *start)
681 gettimeofday(&tv, NULL);
682 return ((tv.tv_sec - start->tv_sec) * 1000 + (tv.tv_usec - start->tv_usec) / 1000);
685 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[]);
687 static void *dundi_query_thread(void *data)
689 struct dundi_query_state *st = data;
690 struct dundi_entity_info dei;
691 struct dundi_ie_data ied;
692 struct dundi_hint_metadata hmd;
695 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
696 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
697 memset(&ied, 0, sizeof(ied));
698 memset(&dei, 0, sizeof(dei));
699 memset(&hmd, 0, sizeof(hmd));
700 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
702 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
703 strncpy(dei.orgunit, dept, sizeof(dei.orgunit));
704 strncpy(dei.org, org, sizeof(dei.org));
705 strncpy(dei.locality, locality, sizeof(dei.locality));
706 strncpy(dei.stateprov, stateprov, sizeof(dei.stateprov));
707 strncpy(dei.country, country, sizeof(dei.country));
708 strncpy(dei.email, email, sizeof(dei.email));
709 strncpy(dei.phone, phone, sizeof(dei.phone));
712 /* If we do not have a canonical result, keep looking */
713 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
715 ast_mutex_lock(&peerlock);
716 if (st->trans->flags & FLAG_DEAD) {
717 ast_log(LOG_DEBUG, "Our transaction went away!\n");
718 st->trans->thread = 0;
719 destroy_trans(st->trans, 0);
722 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
723 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
724 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
725 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
726 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
727 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
728 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
729 if (!ast_strlen_zero(dei.ipaddr))
730 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
732 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
733 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
734 st->trans->thread = 0;
736 ast_mutex_unlock(&peerlock);
741 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
743 struct dundi_query_state *st;
747 struct dundi_ie_data ied;
750 pthread_t lookupthread;
752 if (ies->eidcount > 1) {
753 /* Since it is a requirement that the first EID is the authenticating host
754 and the last EID is the root, it is permissible that the first and last EID
755 could be the same. In that case, we should go ahead copy only the "root" section
756 since we will not need it for authentication. */
757 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
760 totallen = sizeof(struct dundi_query_state);
761 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
762 st = malloc(totallen);
764 memset(st, 0, totallen);
765 strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
766 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
768 st->ttl = ies->ttl - 1;
772 for (x=skipfirst;ies->eids[x];x++) {
773 st->eids[x-skipfirst] = (dundi_eid *)s;
774 *st->eids[x-skipfirst] = *ies->eids[x];
775 s += sizeof(dundi_eid);
777 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);
778 pthread_attr_init(&attr);
779 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
781 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
783 ast_log(LOG_WARNING, "Unable to create thread!\n");
785 memset(&ied, 0, sizeof(ied));
786 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
787 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
791 ast_log(LOG_WARNING, "Out of memory!\n");
792 memset(&ied, 0, sizeof(ied));
793 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
794 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
800 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
805 char eidpeer_str[20];
806 char eidroot_str[20];
811 expiration = DUNDI_DEFAULT_CACHE_TIME;
813 /* Only cache hint if "don't ask" is there... */
814 if (!(ntohs(hint->flags)& DUNDI_HINT_DONT_ASK))
817 unaffected = ntohs(hint->flags) & DUNDI_HINT_UNAFFECTED;
819 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
820 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
821 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
822 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
825 timeout += expiration;
826 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
828 ast_db_put("dundi/cache", key1, data);
829 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
830 ast_db_put("dundi/cache", key2, data);
831 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
835 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
841 char eidpeer_str[20];
842 char eidroot_str[20];
846 expiration = DUNDI_DEFAULT_CACHE_TIME;
848 /* Keep pushes a little longer, cut pulls a little short */
855 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
856 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
857 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
858 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
859 /* Build request string */
861 timeout += expiration;
862 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
863 for (x=start;x<req->respcount;x++) {
864 /* Skip anything with an illegal pipe in it */
865 if (strchr(req->dr[x].dest, '|'))
867 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
868 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
869 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
871 ast_db_put("dundi/cache", key1, data);
872 ast_db_put("dundi/cache", key2, data);
876 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
878 struct dundi_query_state *st;
881 struct dundi_ie_data ied;
883 struct dundi_result dr2[MAX_RESULTS];
884 struct dundi_request dr;
885 struct dundi_hint_metadata hmd;
887 struct dundi_mapping *cur;
891 pthread_t lookupthread;
894 memset(&dr2, 0, sizeof(dr2));
895 memset(&dr, 0, sizeof(dr));
896 memset(&hmd, 0, sizeof(hmd));
898 /* Forge request structure to hold answers for cache */
899 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
901 dr.maxcount = MAX_RESULTS;
902 dr.expiration = DUNDI_DEFAULT_CACHE_TIME;
904 dr.pfds[0] = dr.pfds[1] = -1;
906 strncpy(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
907 strncpy(dr.number, ies->called_number, sizeof(dr.number) - 1);
909 for (x=0;x<ies->anscount;x++) {
910 if (trans->parent->respcount < trans->parent->maxcount) {
911 /* Make sure it's not already there */
912 for (z=0;z<trans->parent->respcount;z++) {
913 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
914 !strcmp(trans->parent->dr[z].dest, ies->answers[x]->data))
917 if (z == trans->parent->respcount) {
918 /* Copy into parent responses */
919 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
920 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
921 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
922 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
923 if (ies->expiration > 0)
924 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
926 trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
927 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
928 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
929 &ies->answers[x]->eid);
930 strncpy(trans->parent->dr[trans->parent->respcount].dest, ies->answers[x]->data,
931 sizeof(trans->parent->dr[trans->parent->respcount].dest));
932 strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
933 sizeof(trans->parent->dr[trans->parent->respcount].tech));
934 trans->parent->respcount++;
935 trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
936 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
937 /* Update weight if appropriate */
938 trans->parent->dr[z].weight = ies->answers[x]->weight;
941 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
942 trans->parent->number, trans->parent->dcontext);
945 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
946 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
948 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
950 totallen = sizeof(struct dundi_query_state);
951 /* Count matching map entries */
955 if (!strcasecmp(cur->dcontext, ccontext))
960 /* If no maps, return -1 immediately */
964 if (ies->eidcount > 1) {
965 /* Since it is a requirement that the first EID is the authenticating host
966 and the last EID is the root, it is permissible that the first and last EID
967 could be the same. In that case, we should go ahead copy only the "root" section
968 since we will not need it for authentication. */
969 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
973 /* Prepare to run a query and then propagate that as necessary */
974 totallen += mapcount * sizeof(struct dundi_mapping);
975 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
976 st = malloc(totallen);
978 memset(st, 0, totallen);
979 strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
980 strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
982 st->ttl = ies->ttl - 1;
983 st->nocache = ies->cbypass;
987 for (x=skipfirst;ies->eids[x];x++) {
988 st->eids[x-skipfirst] = (dundi_eid *)s;
989 *st->eids[x-skipfirst] = *ies->eids[x];
990 st->directs[x-skipfirst] = ies->eid_direct[x];
991 s += sizeof(dundi_eid);
993 /* Append mappings */
995 st->maps = (struct dundi_mapping *)s;
998 if (!strcasecmp(cur->dcontext, ccontext)) {
1001 st->maps[x].next = NULL;
1007 st->nummaps = mapcount;
1008 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1009 pthread_attr_init(&attr);
1010 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1012 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
1014 ast_log(LOG_WARNING, "Unable to create thread!\n");
1016 memset(&ied, 0, sizeof(ied));
1017 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1018 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1022 ast_log(LOG_WARNING, "Out of memory!\n");
1023 memset(&ied, 0, sizeof(ied));
1024 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1025 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1031 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1033 struct dundi_query_state *st;
1036 struct dundi_ie_data ied;
1038 struct dundi_mapping *cur;
1042 pthread_t lookupthread;
1043 pthread_attr_t attr;
1044 totallen = sizeof(struct dundi_query_state);
1045 /* Count matching map entries */
1049 if (!strcasecmp(cur->dcontext, ccontext))
1053 /* If no maps, return -1 immediately */
1057 if (ies->eidcount > 1) {
1058 /* Since it is a requirement that the first EID is the authenticating host
1059 and the last EID is the root, it is permissible that the first and last EID
1060 could be the same. In that case, we should go ahead copy only the "root" section
1061 since we will not need it for authentication. */
1062 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1066 totallen += mapcount * sizeof(struct dundi_mapping);
1067 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1068 st = malloc(totallen);
1070 memset(st, 0, totallen);
1071 strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
1072 strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
1074 st->ttl = ies->ttl - 1;
1075 st->nocache = ies->cbypass;
1079 for (x=skipfirst;ies->eids[x];x++) {
1080 st->eids[x-skipfirst] = (dundi_eid *)s;
1081 *st->eids[x-skipfirst] = *ies->eids[x];
1082 st->directs[x-skipfirst] = ies->eid_direct[x];
1083 s += sizeof(dundi_eid);
1085 /* Append mappings */
1087 st->maps = (struct dundi_mapping *)s;
1090 if (!strcasecmp(cur->dcontext, ccontext)) {
1093 st->maps[x].next = NULL;
1099 st->nummaps = mapcount;
1100 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1101 pthread_attr_init(&attr);
1102 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1104 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
1106 ast_log(LOG_WARNING, "Unable to create thread!\n");
1108 memset(&ied, 0, sizeof(ied));
1109 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1110 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1114 ast_log(LOG_WARNING, "Out of memory!\n");
1115 memset(&ied, 0, sizeof(ied));
1116 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1117 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1123 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1126 char *ptr, *term, *src;
1135 /* Build request string */
1136 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1138 if (sscanf(ptr, "%ld|%n", &timeout, &length) == 1) {
1139 expiration = timeout - now;
1140 if (expiration > 0) {
1141 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
1143 while((sscanf(ptr, "%d/%d/%d/%n", &flags, &weight, &tech, &length) == 3)) {
1145 term = strchr(ptr, '|');
1148 src = strrchr(ptr, '/');
1154 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1155 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags), eid_str_full);
1156 /* Make sure it's not already there */
1157 for (z=0;z<req->respcount;z++) {
1158 if ((req->dr[z].techint == tech) &&
1159 !strcmp(req->dr[z].dest, ptr))
1162 if (z == req->respcount) {
1163 /* Copy into parent responses */
1164 req->dr[req->respcount].flags = flags;
1165 req->dr[req->respcount].weight = weight;
1166 req->dr[req->respcount].techint = tech;
1167 req->dr[req->respcount].expiration = expiration;
1168 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1169 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1170 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1171 strncpy(req->dr[req->respcount].dest, ptr,
1172 sizeof(req->dr[req->respcount].dest));
1173 strncpy(req->dr[req->respcount].tech, tech2str(tech),
1174 sizeof(req->dr[req->respcount].tech));
1176 req->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
1177 } else if (req->dr[z].weight > weight)
1178 req->dr[z].weight = weight;
1182 /* We found *something* cached */
1183 if (expiration < *lowexpiration)
1184 *lowexpiration = expiration;
1187 ast_db_del("dundi/cache", key);
1189 ast_db_del("dundi/cache", key);
1195 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1199 char eidroot_str[20];
1203 char eid_str_full[20];
1208 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1209 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1210 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1211 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1212 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1213 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1214 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1215 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1216 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1218 if (!req->respcount) {
1220 /* Look and see if we have a hint that would preclude us from looking at this
1221 peer for this number. */
1222 if (!(tmp[x] = req->number[x]))
1225 /* Check for hints */
1226 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1227 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1228 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1229 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1230 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1231 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1233 if (strlen(tmp) > strlen(req->hmd->exten)) {
1234 /* Update meta data if appropriate */
1235 strncpy(req->hmd->exten, tmp, sizeof(req->hmd->exten) - 1);
1245 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1247 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1249 if (!trans->addr.sin_addr.s_addr)
1250 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1251 trans->us_eid = p->us_eid;
1252 trans->them_eid = p->eid;
1253 /* Enable encryption if appropriate */
1254 if (!ast_strlen_zero(p->inkey))
1255 trans->flags |= FLAG_ENCRYPT;
1257 trans->autokilltimeout = p->maxms;
1258 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1259 if (p->lastms > 1) {
1260 trans->retranstimer = p->lastms * 2;
1261 /* Keep it from being silly */
1262 if (trans->retranstimer < 150)
1263 trans->retranstimer = 150;
1265 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1266 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1268 trans->autokilltimeout = global_autokilltimeout;
1271 static int do_register_expire(void *data)
1273 struct dundi_peer *peer = data;
1275 /* Called with peerlock already held */
1276 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1277 peer->registerexpire = -1;
1279 memset(&peer->addr, 0, sizeof(peer->addr));
1283 static int update_key(struct dundi_peer *peer)
1285 unsigned char key[16];
1286 struct ast_key *ekey, *skey;
1289 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1291 aes_encrypt_key128(key, &peer->us_ecx);
1292 aes_decrypt_key128(key, &peer->us_dcx);
1293 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1295 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1296 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1299 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1301 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1302 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1305 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1306 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1309 if ((res = ast_sign_bin(skey, peer->txenckey, 128, peer->txenckey + 128))) {
1310 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1313 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1314 peer->sentfullkey = 0;
1316 time(&peer->keyexpire);
1317 peer->keyexpire += dundi_key_ttl;
1322 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
1324 unsigned char curblock[16];
1326 memcpy(curblock, iv, sizeof(curblock));
1329 curblock[x] ^= src[x];
1330 aes_encrypt(curblock, dst, ecx);
1331 memcpy(curblock, dst, sizeof(curblock));
1338 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
1340 unsigned char lastblock[16];
1342 memcpy(lastblock, iv, sizeof(lastblock));
1344 aes_decrypt(src, dst, dcx);
1346 dst[x] ^= lastblock[x];
1347 memcpy(lastblock, src, sizeof(lastblock));
1355 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)
1357 int space = *dstlen;
1358 unsigned long bytes;
1359 struct dundi_hdr *h;
1360 unsigned char *decrypt_space;
1361 decrypt_space = alloca(srclen);
1364 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1366 h = (struct dundi_hdr *)dst;
1369 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1370 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1374 *dstlen = bytes + 6;
1375 /* Return new header */
1379 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1381 unsigned char *compress_space;
1384 unsigned long bytes;
1385 struct dundi_ie_data ied;
1386 struct dundi_peer *peer;
1387 unsigned char iv[16];
1388 len = pack->datalen + pack->datalen / 100 + 42;
1389 compress_space = alloca(len);
1390 if (compress_space) {
1391 memset(compress_space, 0, len);
1392 /* We care about everthing save the first 6 bytes of header */
1394 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1396 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1399 memset(&ied, 0, sizeof(ied));
1400 /* Say who we are */
1401 if (!pack->h->iseqno && !pack->h->oseqno) {
1402 /* Need the key in the first copy */
1403 if (!(peer = find_peer(&trans->them_eid)))
1405 if (update_key(peer))
1407 if (!peer->sentfullkey)
1408 trans->flags |= FLAG_SENDFULLKEY;
1409 /* Append key data */
1410 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1411 if (trans->flags & FLAG_SENDFULLKEY) {
1412 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1413 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1415 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1417 /* Setup contexts */
1418 trans->ecx = peer->us_ecx;
1419 trans->dcx = peer->us_dcx;
1421 /* We've sent the full key */
1422 peer->sentfullkey = 1;
1424 /* Build initialization vector */
1426 /* Add the field, rounded up to 16 bytes */
1427 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1429 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1430 ast_log(LOG_NOTICE, "Final packet too large!\n");
1433 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1434 ied.pos += ((bytes + 15) / 16) * 16;
1435 /* Reconstruct header */
1436 pack->datalen = sizeof(struct dundi_hdr);
1437 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1438 pack->h->cmdflags = 0;
1439 memcpy(pack->h->ies, ied.buf, ied.pos);
1440 pack->datalen += ied.pos;
1446 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1448 unsigned char dst[128];
1450 struct ast_key *key, *skey;
1453 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1454 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1457 } else if (!newkey || !newsig)
1459 if (!memcmp(peer->rxenckey, newkey, 128) &&
1460 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1461 /* By definition, a match */
1465 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1467 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1468 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1472 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1474 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1475 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1479 /* First check signature */
1480 res = ast_check_signature_bin(skey, newkey, 128, newsig);
1484 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1487 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1490 /* Decrypted, passes signature */
1491 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1492 memcpy(peer->rxenckey, newkey, 128);
1493 memcpy(peer->rxenckey + 128, newsig, 128);
1494 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1495 aes_decrypt_key128(dst, &peer->them_dcx);
1496 aes_encrypt_key128(dst, &peer->them_ecx);
1500 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1502 /* Handle canonical command / response */
1503 int final = hdr->cmdresp & 0x80;
1504 int cmd = hdr->cmdresp & 0x7f;
1509 unsigned char *bufcpy;
1510 struct dundi_ie_data ied;
1511 struct dundi_ies ies;
1512 struct dundi_peer *peer;
1515 memset(&ied, 0, sizeof(ied));
1516 memset(&ies, 0, sizeof(ies));
1518 bufcpy = alloca(datalen);
1521 /* Make a copy for parsing */
1522 memcpy(bufcpy, hdr->ies, datalen);
1523 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1524 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1525 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1530 case DUNDI_COMMAND_DPDISCOVER:
1531 case DUNDI_COMMAND_EIDQUERY:
1532 case DUNDI_COMMAND_PRECACHERQ:
1533 if (cmd == DUNDI_COMMAND_EIDQUERY)
1534 resp = DUNDI_COMMAND_EIDRESPONSE;
1535 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1536 resp = DUNDI_COMMAND_PRECACHERP;
1538 resp = DUNDI_COMMAND_DPRESPONSE;
1539 /* A dialplan or entity discover -- qualify by highest level entity */
1540 peer = find_peer(ies.eids[0]);
1542 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1543 dundi_send(trans, resp, 0, 1, &ied);
1546 trans->us_eid = peer->us_eid;
1547 if (strlen(peer->inkey)) {
1548 hasauth = encrypted;
1552 /* Okay we're authentiated and all, now we check if they're authorized */
1553 if (!ies.called_context)
1554 ies.called_context = "e164";
1555 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1556 res = dundi_answer_entity(trans, &ies, ies.called_context);
1558 if (!ies.called_number || ast_strlen_zero(ies.called_number)) {
1559 /* They're not permitted to access that context */
1560 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1561 dundi_send(trans, resp, 0, 1, &ied);
1562 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1563 (peer->model & DUNDI_MODEL_INBOUND) &&
1564 has_permission(peer->permit, ies.called_context)) {
1565 res = dundi_answer_query(trans, &ies, ies.called_context);
1567 /* There is no such dundi context */
1568 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1569 dundi_send(trans, resp, 0, 1, &ied);
1571 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1572 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1573 has_permission(peer->include, ies.called_context)) {
1574 res = dundi_prop_precache(trans, &ies, ies.called_context);
1576 /* There is no such dundi context */
1577 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1578 dundi_send(trans, resp, 0, 1, &ied);
1581 /* They're not permitted to access that context */
1582 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1583 dundi_send(trans, resp, 0, 1, &ied);
1587 /* They're not permitted to access that context */
1588 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1589 dundi_send(trans, resp, 0, 1, &ied);
1593 case DUNDI_COMMAND_REGREQ:
1594 /* A register request -- should only have one entity */
1595 peer = find_peer(ies.eids[0]);
1596 if (!peer || !peer->dynamic) {
1597 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1598 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1601 trans->us_eid = peer->us_eid;
1602 if (!ast_strlen_zero(peer->inkey)) {
1603 hasauth = encrypted;
1607 int expire = default_expiration;
1608 char iabuf[INET_ADDRSTRLEN];
1611 if (peer->registerexpire > -1)
1612 ast_sched_del(sched, peer->registerexpire);
1613 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1614 ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
1615 snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
1616 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1617 if (inaddrcmp(&peer->addr, &trans->addr)) {
1618 if (option_verbose > 2)
1619 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));
1623 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1624 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1625 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1627 qualify_peer(peer, 1);
1631 case DUNDI_COMMAND_DPRESPONSE:
1632 /* A dialplan response, lets see what we got... */
1633 if (ies.cause < 1) {
1634 /* Success of some sort */
1635 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1636 if (trans->flags & FLAG_ENCRYPT) {
1637 authpass = encrypted;
1641 /* Pass back up answers */
1642 if (trans->parent && trans->parent->dr) {
1643 y = trans->parent->respcount;
1644 for (x=0;x<ies.anscount;x++) {
1645 if (trans->parent->respcount < trans->parent->maxcount) {
1646 /* Make sure it's not already there */
1647 for (z=0;z<trans->parent->respcount;z++) {
1648 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1649 !strcmp(trans->parent->dr[z].dest, ies.answers[x]->data))
1652 if (z == trans->parent->respcount) {
1653 /* Copy into parent responses */
1654 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1655 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1656 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1657 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1658 if (ies.expiration > 0)
1659 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1661 trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
1662 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1663 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1664 &ies.answers[x]->eid);
1665 strncpy(trans->parent->dr[trans->parent->respcount].dest, ies.answers[x]->data,
1666 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1667 strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1668 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1669 trans->parent->respcount++;
1670 trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
1671 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1672 /* Update weight if appropriate */
1673 trans->parent->dr[z].weight = ies.answers[x]->weight;
1676 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1677 trans->parent->number, trans->parent->dcontext);
1679 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1680 the cache know if this request was unaffected by our entity list. */
1681 cache_save(&trans->them_eid, trans->parent, y,
1682 ies.hint ? ntohs(ies.hint->flags) & DUNDI_HINT_UNAFFECTED : 0, ies.expiration, 0);
1684 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1685 if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
1686 trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
1687 if (ntohs(ies.hint->flags) & DUNDI_HINT_DONT_ASK) {
1688 if (strlen(ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1689 strncpy(trans->parent->hmd->exten, ies.hint->data,
1690 sizeof(trans->parent->hmd->exten) - 1);
1693 trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
1696 if (ies.expiration > 0) {
1697 if (trans->parent->expiration > ies.expiration) {
1698 trans->parent->expiration = ies.expiration;
1702 /* Close connection if not final */
1704 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1708 /* Auth failure, check for data */
1710 /* Cancel if they didn't already */
1711 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1715 case DUNDI_COMMAND_EIDRESPONSE:
1716 /* A dialplan response, lets see what we got... */
1717 if (ies.cause < 1) {
1718 /* Success of some sort */
1719 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1720 if (trans->flags & FLAG_ENCRYPT) {
1721 authpass = encrypted;
1725 /* Pass back up answers */
1726 if (trans->parent && trans->parent->dei && ies.q_org) {
1727 if (!trans->parent->respcount) {
1728 trans->parent->respcount++;
1730 strncpy(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit) - 1);
1732 strncpy(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org) - 1);
1734 strncpy(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality) - 1);
1735 if (ies.q_stateprov)
1736 strncpy(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov) - 1);
1738 strncpy(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country) - 1);
1740 strncpy(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email) - 1);
1742 strncpy(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone) - 1);
1744 strncpy(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr) - 1);
1745 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1746 /* If it's them, update our address */
1747 ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
1748 trans->addr.sin_addr);
1752 if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
1753 trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
1756 /* Close connection if not final */
1758 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1762 /* Auth failure, check for data */
1764 /* Cancel if they didn't already */
1765 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1769 case DUNDI_COMMAND_REGRESPONSE:
1770 /* A dialplan response, lets see what we got... */
1771 if (ies.cause < 1) {
1773 /* Success of some sort */
1774 if (trans->flags & FLAG_ENCRYPT) {
1775 hasauth = encrypted;
1780 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1782 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1783 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1786 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),
1787 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1788 /* Close connection if not final */
1790 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1793 /* Auth failure, cancel if they didn't for some reason */
1795 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1799 case DUNDI_COMMAND_INVALID:
1800 case DUNDI_COMMAND_NULL:
1801 case DUNDI_COMMAND_PRECACHERP:
1802 /* Do nothing special */
1804 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1806 case DUNDI_COMMAND_ENCREJ:
1807 if ((trans->flags & FLAG_SENDFULLKEY) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
1808 /* No really, it's over at this point */
1810 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1812 /* Send with full key */
1813 trans->flags |= FLAG_SENDFULLKEY;
1815 /* Ooops, we got a final message, start by sending ACK... */
1816 dundi_ack(trans, hdr->cmdresp & 0x80);
1817 trans->aseqno = trans->iseqno;
1818 /* Now, we gotta create a new transaction */
1819 if (!reset_transaction(trans)) {
1820 /* Make sure handle_frame doesn't destroy us */
1821 hdr->cmdresp &= 0x7f;
1822 /* Parse the message we transmitted */
1823 memset(&ies, 0, sizeof(ies));
1824 dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
1825 /* Reconstruct outgoing encrypted packet */
1826 memset(&ied, 0, sizeof(ied));
1827 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1828 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1829 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1831 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1832 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
1833 peer->sentfullkey = 1;
1838 case DUNDI_COMMAND_ENCRYPT:
1840 /* No nested encryption! */
1841 if ((trans->iseqno == 1) && !trans->oseqno) {
1842 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1843 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1844 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1846 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1850 apply_peer(trans, peer);
1851 /* Key passed, use new contexts for this session */
1852 trans->ecx = peer->them_ecx;
1853 trans->dcx = peer->them_dcx;
1855 if ((trans->flags & FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1856 struct dundi_hdr *dhdr;
1857 unsigned char decoded[MAX_PACKET_SIZE];
1859 ddatalen = sizeof(decoded);
1860 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1862 /* Handle decrypted response */
1864 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1865 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1866 /* Carry back final flag */
1867 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1870 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1874 /* Turn off encryption */
1875 trans->flags &= ~FLAG_ENCRYPT;
1876 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1880 /* Send unknown command if we don't know it, with final flag IFF it's the
1881 first command in the dialog and only if we haven't recieved final notification */
1883 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1884 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1890 static void destroy_packet(struct dundi_packet *pack, int needfree);
1891 static void destroy_packets(struct dundi_packet *p)
1893 struct dundi_packet *prev;
1897 if (prev->retransid > -1)
1898 ast_sched_del(sched, prev->retransid);
1904 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1906 /* Ack transmitted packet corresponding to iseqno */
1907 struct dundi_packet *pack;
1908 pack = trans->packets;
1910 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1911 destroy_packet(pack, 0);
1912 if (trans->lasttrans) {
1913 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1914 destroy_packets(trans->lasttrans);
1916 trans->lasttrans = pack;
1917 if (trans->autokillid > -1)
1918 ast_sched_del(sched, trans->autokillid);
1919 trans->autokillid = -1;
1927 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1929 struct dundi_transaction *trans;
1930 trans = find_transaction(h, sin);
1932 dundi_reject(h, sin);
1935 /* Got a transaction, see where this header fits in */
1936 if (h->oseqno == trans->iseqno) {
1937 /* Just what we were looking for... Anything but ack increments iseqno */
1938 if (ack_trans(trans, h->iseqno) && (trans->flags & FLAG_FINAL)) {
1939 /* If final, we're done */
1940 destroy_trans(trans, 0);
1943 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1944 trans->oiseqno = trans->iseqno;
1946 handle_command_response(trans, h, datalen, 0);
1948 if (trans->aseqno != trans->iseqno) {
1949 dundi_ack(trans, h->cmdresp & 0x80);
1950 trans->aseqno = trans->iseqno;
1952 /* Delete any saved last transmissions */
1953 destroy_packets(trans->lasttrans);
1954 trans->lasttrans = NULL;
1955 if (h->cmdresp & 0x80) {
1956 /* Final -- destroy now */
1957 destroy_trans(trans, 0);
1959 } else if (h->oseqno == trans->oiseqno) {
1960 /* Last incoming sequence number -- send ACK without processing */
1961 dundi_ack(trans, 0);
1963 /* Out of window -- simply drop */
1964 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
1969 static int socket_read(int *id, int fd, short events, void *cbdata)
1971 struct sockaddr_in sin;
1973 struct dundi_hdr *h;
1974 unsigned char buf[MAX_PACKET_SIZE];
1977 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1979 if (errno != ECONNREFUSED)
1980 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1983 if (res < sizeof(struct dundi_hdr)) {
1984 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
1988 h = (struct dundi_hdr *)buf;
1990 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
1991 ast_mutex_lock(&peerlock);
1992 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
1993 ast_mutex_unlock(&peerlock);
1997 static void build_secret(char *secret, int seclen)
2003 ast_base64encode(secret ,tmp, sizeof(tmp), seclen);
2004 /* Eliminate potential bad characters */
2005 while((s = strchr(secret, ';'))) *s = '+';
2006 while((s = strchr(secret, '/'))) *s = '+';
2007 while((s = strchr(secret, ':'))) *s = '+';
2008 while((s = strchr(secret, '@'))) *s = '+';
2012 static void save_secret(const char *newkey, const char *oldkey)
2016 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2018 snprintf(tmp, sizeof(tmp), "%s", newkey);
2019 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2020 ast_db_put(secretpath, "secret", tmp);
2021 snprintf(tmp, sizeof(tmp), "%ld", rotatetime);
2022 ast_db_put(secretpath, "secretexpiry", tmp);
2025 static void load_password(void)
2032 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2033 if (sscanf(tmp, "%ld", &expired) == 1) {
2034 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2035 current = strchr(tmp, ';');
2042 if ((time(NULL) - expired) < 0) {
2043 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2044 expired = time(NULL) + DUNDI_SECRET_TIME;
2045 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2054 /* Current key is still valid, just setup rotatation properly */
2055 strncpy(cursecret, current, sizeof(cursecret) - 1);
2056 rotatetime = expired;
2058 /* Current key is out of date, rotate or eliminate all together */
2059 build_secret(cursecret, sizeof(cursecret));
2060 save_secret(cursecret, last);
2064 static void check_password(void)
2071 printf("%ld/%ld\n", now, rotatetime);
2073 if ((now - rotatetime) >= 0) {
2074 /* Time to rotate keys */
2075 strncpy(oldsecret, cursecret, sizeof(oldsecret) - 1);
2076 build_secret(cursecret, sizeof(cursecret));
2077 save_secret(cursecret, oldsecret);
2081 static void *network_thread(void *ignore)
2083 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2084 from the network, and queue them for delivery to the channels */
2086 /* Establish I/O callback for socket read */
2087 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2089 res = ast_sched_wait(sched);
2090 if ((res > 1000) || (res < 0))
2092 res = ast_io_wait(io, res);
2094 ast_mutex_lock(&peerlock);
2095 ast_sched_runq(sched);
2096 ast_mutex_unlock(&peerlock);
2103 static void *process_precache(void *ign)
2105 struct dundi_precache_queue *qe;
2107 char context[256]="";
2108 char number[256]="";
2113 ast_mutex_lock(&pclock);
2115 if (!pcq->expiration) {
2116 /* Gone... Remove... */
2120 } else if (pcq->expiration < now) {
2121 /* Process this entry */
2122 pcq->expiration = 0;
2123 strncpy(context, pcq->context, sizeof(context) - 1);
2124 strncpy(number, pcq->number, sizeof(number) - 1);
2128 ast_mutex_unlock(&pclock);
2130 dundi_precache(context, number);
2137 static int start_network_thread(void)
2139 ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
2140 ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
2144 static int dundi_do_debug(int fd, int argc, char *argv[])
2147 return RESULT_SHOWUSAGE;
2149 ast_cli(fd, "DUNDi Debugging Enabled\n");
2150 return RESULT_SUCCESS;
2153 static int dundi_do_store_history(int fd, int argc, char *argv[])
2156 return RESULT_SHOWUSAGE;
2157 global_storehistory = 1;
2158 ast_cli(fd, "DUNDi History Storage Enabled\n");
2159 return RESULT_SUCCESS;
2162 static int dundi_flush(int fd, int argc, char *argv[])
2165 if ((argc < 2) || (argc > 3))
2166 return RESULT_SHOWUSAGE;
2168 if (!strcasecmp(argv[2], "stats"))
2171 return RESULT_SHOWUSAGE;
2174 /* Flush statistics */
2175 struct dundi_peer *p;
2177 ast_mutex_lock(&peerlock);
2180 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2182 free(p->lookups[x]);
2183 p->lookups[x] = NULL;
2184 p->lookuptimes[x] = 0;
2189 ast_mutex_unlock(&peerlock);
2191 ast_db_deltree("dundi/cache", NULL);
2192 ast_cli(fd, "DUNDi Cache Flushed\n");
2194 return RESULT_SUCCESS;
2197 static int dundi_no_debug(int fd, int argc, char *argv[])
2200 return RESULT_SHOWUSAGE;
2202 ast_cli(fd, "DUNDi Debugging Disabled\n");
2203 return RESULT_SUCCESS;
2206 static int dundi_no_store_history(int fd, int argc, char *argv[])
2209 return RESULT_SHOWUSAGE;
2210 global_storehistory = 0;
2211 ast_cli(fd, "DUNDi History Storage Disabled\n");
2212 return RESULT_SUCCESS;
2215 static char *model2str(int model)
2218 case DUNDI_MODEL_INBOUND:
2220 case DUNDI_MODEL_OUTBOUND:
2222 case DUNDI_MODEL_SYMMETRIC:
2229 static char *complete_peer_helper(char *line, char *word, int pos, int state, int rpos)
2233 struct dundi_peer *p;
2237 ast_mutex_lock(&peerlock);
2240 if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
2241 if (++which > state)
2247 ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
2250 ast_mutex_unlock(&peerlock);
2254 static char *complete_peer_4(char *line, char *word, int pos, int state)
2256 return complete_peer_helper(line, word, pos, state, 3);
2259 static int rescomp(const void *a, const void *b)
2261 const struct dundi_result *resa, *resb;
2264 if (resa->weight < resb->weight)
2266 if (resa->weight > resb->weight)
2271 static void sort_results(struct dundi_result *results, int count)
2273 qsort(results, count, sizeof(results[0]), rescomp);
2276 static int dundi_do_lookup(int fd, int argc, char *argv[])
2284 struct dundi_result dr[MAX_RESULTS];
2285 struct timeval start;
2286 if ((argc < 3) || (argc > 4))
2287 return RESULT_SHOWUSAGE;
2289 if (!strcasecmp(argv[3], "bypass"))
2292 return RESULT_SHOWUSAGE;
2294 strncpy(tmp, argv[2], sizeof(tmp) - 1);
2295 context = strchr(tmp, '@');
2300 gettimeofday(&start, NULL);
2301 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2304 ast_cli(fd, "DUNDi lookup returned error.\n");
2306 ast_cli(fd, "DUNDi lookup returned no results.\n");
2308 sort_results(dr, res);
2309 for (x=0;x<res;x++) {
2310 ast_cli(fd, "%3d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
2311 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2313 ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
2314 return RESULT_SUCCESS;
2317 static int dundi_do_precache(int fd, int argc, char *argv[])
2322 struct timeval start;
2323 if ((argc < 3) || (argc > 3))
2324 return RESULT_SHOWUSAGE;
2325 strncpy(tmp, argv[2], sizeof(tmp) - 1);
2326 context = strchr(tmp, '@');
2331 gettimeofday(&start, NULL);
2332 res = dundi_precache(context, tmp);
2335 ast_cli(fd, "DUNDi precache returned error.\n");
2337 ast_cli(fd, "DUNDi precache returned no error.\n");
2338 ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
2339 return RESULT_SUCCESS;
2342 static int dundi_do_query(int fd, int argc, char *argv[])
2348 struct dundi_entity_info dei;
2349 if ((argc < 3) || (argc > 3))
2350 return RESULT_SHOWUSAGE;
2351 if (dundi_str_to_eid(&eid, argv[2])) {
2352 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2353 return RESULT_SHOWUSAGE;
2355 strncpy(tmp, argv[2], sizeof(tmp) - 1);
2356 context = strchr(tmp, '@');
2361 res = dundi_query_eid(&dei, context, eid);
2363 ast_cli(fd, "DUNDi Query EID returned error.\n");
2365 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2367 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2368 ast_cli(fd, "Department: %s\n", dei.orgunit);
2369 ast_cli(fd, "Organization: %s\n", dei.org);
2370 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2371 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2372 ast_cli(fd, "Country: %s\n", dei.country);
2373 ast_cli(fd, "E-mail: %s\n", dei.email);
2374 ast_cli(fd, "Phone: %s\n", dei.phone);
2375 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2377 return RESULT_SUCCESS;
2380 static int dundi_show_peer(int fd, int argc, char *argv[])
2382 struct dundi_peer *peer;
2383 struct permission *p;
2385 char iabuf[INET_ADDRSTRLEN];
2390 return RESULT_SHOWUSAGE;
2391 ast_mutex_lock(&peerlock);
2394 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2399 switch(peer->order) {
2404 order = "Secondary";
2410 order = "Quartiary";
2415 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2416 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2417 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
2418 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2419 ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
2420 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2421 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2422 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2423 if (peer->include) {
2424 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2428 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2432 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2436 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2440 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2441 if (peer->lookups[x]) {
2443 ast_cli(fd, "Last few query times:\n");
2444 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2449 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2451 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2452 ast_mutex_unlock(&peerlock);
2453 return RESULT_SUCCESS;
2456 static int dundi_show_peers(int fd, int argc, char *argv[])
2458 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2459 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2460 struct dundi_peer *peer;
2461 char iabuf[INET_ADDRSTRLEN];
2462 int registeredonly=0;
2465 if ((argc != 3) && (argc != 4) && (argc != 5))
2466 return RESULT_SHOWUSAGE;
2468 if (!strcasecmp(argv[3], "registered")) {
2471 return RESULT_SHOWUSAGE;
2473 ast_mutex_lock(&peerlock);
2474 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2475 for (peer = peers;peer;peer = peer->next) {
2476 char status[20] = "";
2477 int print_line = -1;
2478 char srch[2000] = "";
2479 if (registeredonly && !peer->addr.sin_addr.s_addr)
2482 if (peer->lastms < 0)
2483 strncpy(status, "UNREACHABLE", sizeof(status) - 1);
2484 else if (peer->lastms > peer->maxms)
2485 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2486 else if (peer->lastms)
2487 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2489 strncpy(status, "UNKNOWN", sizeof(status) - 1);
2491 strncpy(status, "Unmonitored", sizeof(status) - 1);
2493 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2495 strcpy(avgms, "Unavail");
2496 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2497 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2498 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2501 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2503 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2505 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2513 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2514 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2515 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2518 ast_mutex_unlock(&peerlock);
2519 return RESULT_SUCCESS;
2524 static int dundi_show_trans(int fd, int argc, char *argv[])
2526 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2527 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2528 struct dundi_transaction *trans;
2529 char iabuf[INET_ADDRSTRLEN];
2531 return RESULT_SHOWUSAGE;
2532 ast_mutex_lock(&peerlock);
2533 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2534 for (trans = alltrans;trans;trans = trans->allnext) {
2535 ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr),
2536 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2538 ast_mutex_unlock(&peerlock);
2539 return RESULT_SUCCESS;
2544 static int dundi_show_entityid(int fd, int argc, char *argv[])
2548 return RESULT_SHOWUSAGE;
2549 ast_mutex_lock(&peerlock);
2550 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2551 ast_mutex_unlock(&peerlock);
2552 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2553 return RESULT_SUCCESS;
2556 static int dundi_show_requests(int fd, int argc, char *argv[])
2558 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2559 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2560 struct dundi_request *req;
2563 return RESULT_SHOWUSAGE;
2564 ast_mutex_lock(&peerlock);
2565 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2566 for (req = requests;req;req = req->next) {
2567 ast_cli(fd, FORMAT, req->number, req->dcontext,
2568 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2570 ast_mutex_unlock(&peerlock);
2571 return RESULT_SUCCESS;
2576 /* Grok-a-dial DUNDi */
2578 static int dundi_show_mappings(int fd, int argc, char *argv[])
2580 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2581 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2582 struct dundi_mapping *map;
2585 return RESULT_SHOWUSAGE;
2586 ast_mutex_lock(&peerlock);
2587 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2588 for (map = mappings;map;map = map->next) {
2589 ast_cli(fd, FORMAT, map->dcontext, map->weight,
2590 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2591 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2593 ast_mutex_unlock(&peerlock);
2594 return RESULT_SUCCESS;
2599 static int dundi_show_precache(int fd, int argc, char *argv[])
2601 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2602 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2603 struct dundi_precache_queue *qe;
2608 return RESULT_SHOWUSAGE;
2610 ast_mutex_lock(&pclock);
2611 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2612 for (qe = pcq;qe;qe = qe->next) {
2613 s = qe->expiration - now;
2618 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2620 ast_mutex_unlock(&pclock);
2621 return RESULT_SUCCESS;
2626 static char debug_usage[] =
2627 "Usage: dundi debug\n"
2628 " Enables dumping of DUNDi packets for debugging purposes\n";
2630 static char no_debug_usage[] =
2631 "Usage: dundi no debug\n"
2632 " Disables dumping of DUNDi packets for debugging purposes\n";
2634 static char store_history_usage[] =
2635 "Usage: dundi store history\n"
2636 " Enables storing of DUNDi requests and times for debugging\n"
2639 static char no_store_history_usage[] =
2640 "Usage: dundi no store history\n"
2641 " Disables storing of DUNDi requests and times for debugging\n"
2644 static char show_peers_usage[] =
2645 "Usage: dundi show peers\n"
2646 " Lists all known DUNDi peers.\n";
2648 static char show_trans_usage[] =
2649 "Usage: dundi show trans\n"
2650 " Lists all known DUNDi transactions.\n";
2652 static char show_mappings_usage[] =
2653 "Usage: dundi show mappings\n"
2654 " Lists all known DUNDi mappings.\n";
2656 static char show_precache_usage[] =
2657 "Usage: dundi show precache\n"
2658 " Lists all known DUNDi scheduled precache updates.\n";
2660 static char show_entityid_usage[] =
2661 "Usage: dundi show entityid\n"
2662 " Displays the global entityid for this host.\n";
2664 static char show_peer_usage[] =
2665 "Usage: dundi show peer [peer]\n"
2666 " Provide a detailed description of a specifid DUNDi peer.\n";
2668 static char show_requests_usage[] =
2669 "Usage: dundi show requests\n"
2670 " Lists all known pending DUNDi requests.\n";
2672 static char lookup_usage[] =
2673 "Usage: dundi lookup <number>[@context] [bypass]\n"
2674 " Lookup the given number within the given DUNDi context\n"
2675 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2676 "keyword is specified.\n";
2678 static char precache_usage[] =
2679 "Usage: dundi precache <number>[@context]\n"
2680 " Lookup the given number within the given DUNDi context\n"
2681 "(or e164 if none is specified) and precaches the results to any\n"
2682 "upstream DUNDi push servers.\n";
2684 static char query_usage[] =
2685 "Usage: dundi query <entity>[@context]\n"
2686 " Attempts to retrieve contact information for a specific\n"
2687 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2688 "e164 if none is specified).\n";
2690 static char flush_usage[] =
2691 "Usage: dundi flush [stats]\n"
2692 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2693 "'stats' is present, clears timer statistics instead of normal\n"
2696 static struct ast_cli_entry cli_debug =
2697 { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2699 static struct ast_cli_entry cli_store_history =
2700 { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2702 static struct ast_cli_entry cli_no_store_history =
2703 { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2705 static struct ast_cli_entry cli_flush =
2706 { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2708 static struct ast_cli_entry cli_no_debug =
2709 { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2711 static struct ast_cli_entry cli_show_peers =
2712 { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2714 static struct ast_cli_entry cli_show_trans =
2715 { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2717 static struct ast_cli_entry cli_show_entityid =
2718 { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2720 static struct ast_cli_entry cli_show_mappings =
2721 { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2723 static struct ast_cli_entry cli_show_precache =
2724 { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
2726 static struct ast_cli_entry cli_show_requests =
2727 { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2729 static struct ast_cli_entry cli_show_peer =
2730 { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2732 static struct ast_cli_entry cli_lookup =
2733 { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2735 static struct ast_cli_entry cli_precache =
2736 { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
2738 static struct ast_cli_entry cli_queryeid =
2739 { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2741 STANDARD_LOCAL_USER;
2745 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2747 struct dundi_transaction *trans;
2750 /* Don't allow creation of transactions to non-registered peers */
2751 if (p && !p->addr.sin_addr.s_addr)
2753 tid = get_trans_id();
2756 trans = malloc(sizeof(struct dundi_transaction));
2758 memset(trans, 0, sizeof(struct dundi_transaction));
2759 if (global_storehistory) {
2760 gettimeofday(&trans->start, NULL);
2761 trans->flags |= FLAG_STOREHIST;
2763 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2764 trans->autokillid = -1;
2766 apply_peer(trans, p);
2767 if (!p->sentfullkey)
2768 trans->flags |= FLAG_SENDFULLKEY;
2770 trans->strans = tid;
2771 trans->allnext = alltrans;
2777 static int dundi_xmit(struct dundi_packet *pack)
2780 char iabuf[INET_ADDRSTRLEN];
2782 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2783 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2785 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2786 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2787 ntohs(pack->parent->addr.sin_port), strerror(errno));
2794 static void destroy_packet(struct dundi_packet *pack, int needfree)
2796 struct dundi_packet *prev, *cur;
2799 cur = pack->parent->packets;
2803 prev->next = cur->next;
2805 pack->parent->packets = cur->next;
2812 if (pack->retransid > -1)
2813 ast_sched_del(sched, pack->retransid);
2817 pack->retransid = -1;
2822 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2824 struct dundi_transaction *cur, *prev;
2825 struct dundi_peer *peer;
2831 if (trans->flags & (FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2834 if (peer->regtrans == trans)
2835 peer->regtrans = NULL;
2836 if (peer->keypending == trans)
2837 peer->keypending = NULL;
2838 if (peer->qualtrans == trans) {
2840 if (peer->lastms > -1)
2841 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2844 gettimeofday(&tv, NULL);
2845 ms = (tv.tv_sec - peer->qualtx.tv_sec) * 1000 +
2846 (tv.tv_usec - peer->qualtx.tv_usec) / 1000;
2849 if (ms < peer->maxms) {
2850 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2851 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2852 } else if (peer->lastms < peer->maxms) {
2853 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);
2857 peer->qualtrans = NULL;
2859 if (trans->flags & FLAG_STOREHIST) {
2860 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2861 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2864 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2865 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2866 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2867 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2868 peer->lookups[x] = peer->lookups[x-1];
2869 if (peer->lookups[x]) {
2870 peer->avgms += peer->lookuptimes[x];
2874 peer->lookuptimes[0] = calc_ms(&trans->start);
2875 peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2876 if (peer->lookups[0]) {
2877 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2878 peer->avgms += peer->lookuptimes[0];
2889 if (trans->parent) {
2890 /* Unlink from parent if appropriate */
2892 cur = trans->parent->trans;
2896 prev->next = trans->next;
2898 trans->parent->trans = trans->next;
2904 if (!trans->parent->trans) {
2905 /* Wake up sleeper */
2906 if (trans->parent->pfds[1] > -1) {
2907 write(trans->parent->pfds[1], "killa!", 6);
2911 /* Unlink from all trans */