2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Distributed Universal Number Discovery (DUNDi)
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33 #include "asterisk/network.h"
34 #include <sys/ioctl.h>
36 #include <sys/signal.h>
40 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
41 #include <net/if_dl.h>
45 #include "asterisk/file.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/config.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/module.h"
51 #include "asterisk/frame.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/lock.h"
54 #include "asterisk/md5.h"
55 #include "asterisk/dundi.h"
56 #include "asterisk/sched.h"
57 #include "asterisk/io.h"
58 #include "asterisk/utils.h"
59 #include "asterisk/netsock.h"
60 #include "asterisk/crypto.h"
61 #include "asterisk/astdb.h"
62 #include "asterisk/acl.h"
63 #include "asterisk/aes.h"
64 #include "asterisk/app.h"
66 #include "dundi-parser.h"
68 #define MAX_RESULTS 64
70 #define MAX_PACKET_SIZE 8192
72 #define MAX_WEIGHT 59999
74 #define DUNDI_MODEL_INBOUND (1 << 0)
75 #define DUNDI_MODEL_OUTBOUND (1 << 1)
76 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
78 /*! Keep times of last 10 lookups */
79 #define DUNDI_TIMING_HISTORY 10
82 FLAG_ISREG = (1 << 0), /*!< Transaction is register request */
83 FLAG_DEAD = (1 << 1), /*!< Transaction is dead */
84 FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */
85 FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */
86 FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted wiht ECX/DCX */
87 FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */
88 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
99 static struct io_context *io;
100 static struct sched_context *sched;
101 static int netsocket = -1;
102 static pthread_t netthreadid = AST_PTHREADT_NULL;
103 static pthread_t precachethreadid = AST_PTHREADT_NULL;
104 static unsigned int tos = 0;
105 static int dundidebug = 0;
106 static int authdebug = 0;
107 static int dundi_ttl = DUNDI_DEFAULT_TTL;
108 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
109 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
110 static int global_autokilltimeout = 0;
111 static dundi_eid global_eid;
112 static int default_expiration = 60;
113 static int global_storehistory = 0;
114 static char dept[80];
116 static char locality[80];
117 static char stateprov[80];
118 static char country[80];
119 static char email[80];
120 static char phone[80];
121 static char secretpath[80];
122 static char cursecret[80];
123 static char ipaddr[80];
124 static time_t rotatetime;
125 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
126 static int dundi_shutdown = 0;
129 AST_LIST_ENTRY(permission) list;
134 struct dundi_packet {
135 AST_LIST_ENTRY(dundi_packet) list;
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 AST_LIST_ENTRY(dundi_precache_queue) list;
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 ast_aes_encrypt_key ecx; /*!< AES 128 Encryption context */
166 ast_aes_decrypt_key dcx; /*!< AES 128 Decryption context */
167 unsigned 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 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
180 struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
181 struct dundi_request *parent; /*!< Parent request (if there is one) */
182 AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
183 AST_LIST_ENTRY(dundi_transaction) all; /*!< 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 AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */
201 AST_LIST_ENTRY(dundi_request) list;
204 struct dundi_mapping {
205 char dcontext[AST_MAX_EXTENSION];
206 char lcontext[AST_MAX_EXTENSION];
212 char dest[AST_MAX_EXTENSION];
213 AST_LIST_ENTRY(dundi_mapping) list;
218 struct sockaddr_in addr; /*!< Address of DUNDi peer */
219 AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
220 struct permissionlist include;
229 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
230 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
231 unsigned long us_keycrc32; /*!< CRC-32 of our key */
232 ast_aes_encrypt_key us_ecx; /*!< Cached AES 128 Encryption context */
233 ast_aes_decrypt_key us_dcx; /*!< Cached AES 128 Decryption context */
234 unsigned long them_keycrc32; /*!< CRC-32 of our key */
235 ast_aes_encrypt_key them_ecx; /*!< Cached AES 128 Encryption context */
236 ast_aes_decrypt_key them_dcx; /*!< Cached AES 128 Decryption context */
237 time_t keyexpire; /*!< When to expire/recreate key */
239 int lookuptimes[DUNDI_TIMING_HISTORY];
240 char *lookups[DUNDI_TIMING_HISTORY];
242 struct dundi_transaction *regtrans; /*!< Registration transaction */
243 struct dundi_transaction *qualtrans; /*!< Qualify transaction */
244 int model; /*!< Pull model */
245 int pcmodel; /*!< Push/precache model */
246 /*! Dynamic peers register with us */
247 unsigned int dynamic:1;
248 int lastms; /*!< Last measured latency */
249 int maxms; /*!< Max permissible latency */
250 struct timeval qualtx; /*!< Time of transmit */
251 AST_LIST_ENTRY(dundi_peer) list;
254 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
255 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
256 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
257 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
258 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
261 * \brief Wildcard peer
263 * This peer is created if the [*] entry is specified in dundi.conf
265 static struct dundi_peer *any_peer;
267 static int dundi_xmit(struct dundi_packet *pack);
269 static void dundi_debug_output(const char *data)
272 ast_verbose("%s", data);
275 static void dundi_error_output(const char *data)
277 ast_log(LOG_WARNING, "%s", data);
280 static int has_permission(struct permissionlist *permlist, char *cont)
282 struct permission *perm;
285 AST_LIST_TRAVERSE(permlist, perm, list) {
286 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
293 static char *tech2str(int tech)
296 case DUNDI_PROTO_NONE:
298 case DUNDI_PROTO_IAX:
300 case DUNDI_PROTO_SIP:
302 case DUNDI_PROTO_H323:
309 static int str2tech(char *str)
311 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
312 return DUNDI_PROTO_IAX;
313 else if (!strcasecmp(str, "SIP"))
314 return DUNDI_PROTO_SIP;
315 else if (!strcasecmp(str, "H323"))
316 return DUNDI_PROTO_H323;
321 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[]);
322 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
323 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
324 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
326 struct dundi_transaction *trans;
328 /* Look for an exact match first */
329 AST_LIST_TRAVERSE(&alltrans, trans, all) {
330 if (!inaddrcmp(&trans->addr, sin) &&
331 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
332 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
334 trans->dtrans = ntohs(hdr->strans) & 32767;
339 switch(hdr->cmdresp & 0x7f) {
340 case DUNDI_COMMAND_DPDISCOVER:
341 case DUNDI_COMMAND_EIDQUERY:
342 case DUNDI_COMMAND_PRECACHERQ:
343 case DUNDI_COMMAND_REGREQ:
344 case DUNDI_COMMAND_NULL:
345 case DUNDI_COMMAND_ENCRYPT:
348 /* Create new transaction */
349 if (!(trans = create_transaction(NULL)))
351 memcpy(&trans->addr, sin, sizeof(trans->addr));
352 trans->dtrans = ntohs(hdr->strans) & 32767;
360 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
362 static int dundi_ack(struct dundi_transaction *trans, int final)
364 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
366 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
369 struct dundi_packet pack;
370 struct dundi_hdr hdr;
372 struct dundi_transaction trans;
373 /* Never respond to an INVALID with another INVALID */
374 if (h->cmdresp == DUNDI_COMMAND_INVALID)
376 memset(&tmp, 0, sizeof(tmp));
377 memset(&trans, 0, sizeof(trans));
378 memcpy(&trans.addr, sin, sizeof(trans.addr));
379 tmp.hdr.strans = h->dtrans;
380 tmp.hdr.dtrans = h->strans;
381 tmp.hdr.iseqno = h->oseqno;
382 tmp.hdr.oseqno = h->iseqno;
383 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
384 tmp.hdr.cmdflags = 0;
385 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
386 tmp.pack.datalen = sizeof(struct dundi_hdr);
387 tmp.pack.parent = &trans;
388 dundi_xmit(&tmp.pack);
391 static int get_trans_id(void)
393 struct dundi_transaction *t;
394 int stid = (ast_random() % 32766) + 1;
398 AST_LIST_TRAVERSE(&alltrans, t, all) {
399 if (t->strans == tid)
404 tid = (tid % 32766) + 1;
405 } while (tid != stid);
410 static int reset_transaction(struct dundi_transaction *trans)
413 tid = get_trans_id();
422 ast_clear_flag(trans, FLAG_FINAL);
426 static struct dundi_peer *find_peer(dundi_eid *eid)
428 struct dundi_peer *cur = NULL;
433 AST_LIST_TRAVERSE(&peers, cur, list) {
434 if (!ast_eid_cmp(&cur->eid,eid))
438 if (!cur && any_peer)
444 static void build_iv(unsigned char *iv)
446 /* XXX Would be nice to be more random XXX */
447 unsigned int *fluffy;
449 fluffy = (unsigned int *)(iv);
451 fluffy[x] = ast_random();
454 struct dundi_query_state {
455 dundi_eid *eids[DUNDI_MAX_STACK + 1];
456 int directs[DUNDI_MAX_STACK + 1];
458 char called_context[AST_MAX_EXTENSION];
459 char called_number[AST_MAX_EXTENSION];
460 struct dundi_mapping *maps;
463 struct dundi_transaction *trans;
470 static int get_mapping_weight(struct dundi_mapping *map)
475 if (map->weightstr) {
476 pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
477 if (sscanf(buf, "%d", &map->_weight) != 1)
478 map->_weight = MAX_WEIGHT;
484 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)
486 struct ast_flags flags = {0};
488 if (!ast_strlen_zero(map->lcontext)) {
489 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
490 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
491 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
492 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
493 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
494 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
495 if (ast_ignore_pattern(map->lcontext, called_number))
496 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
498 /* Clearly we can't say 'don't ask' anymore if we found anything... */
499 if (ast_test_flag(&flags, AST_FLAGS_ALL))
500 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
502 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
503 /* Skip partial answers */
504 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
506 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
507 struct varshead headp;
508 struct ast_var_t *newvariable;
509 ast_set_flag(&flags, map->options & 0xffff);
510 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
511 dr[anscnt].techint = map->tech;
512 dr[anscnt].weight = get_mapping_weight(map);
513 dr[anscnt].expiration = dundi_cache_time;
514 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
515 dr[anscnt].eid = *us_eid;
516 ast_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
517 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
518 AST_LIST_HEAD_INIT_NOLOCK(&headp);
519 newvariable = ast_var_assign("NUMBER", called_number);
520 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
521 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
522 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
523 newvariable = ast_var_assign("SECRET", cursecret);
524 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
525 newvariable = ast_var_assign("IPADDR", ipaddr);
526 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
527 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
528 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
529 ast_var_delete(newvariable);
531 dr[anscnt].dest[0] = '\0';
534 /* No answers... Find the fewest number of digits from the
535 number for which we have no answer. */
536 char tmp[AST_MAX_EXTENSION + 1] = "";
537 for (x = 0; x < (sizeof(tmp) - 1); x++) {
538 tmp[x] = called_number[x];
541 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
542 /* Oops found something we can't match. If this is longer
543 than the running hint, we have to consider it */
544 if (strlen(tmp) > strlen(hmd->exten)) {
545 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
555 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
557 static void *dundi_lookup_thread(void *data)
559 struct dundi_query_state *st = data;
560 struct dundi_result dr[MAX_RESULTS];
561 struct dundi_ie_data ied;
562 struct dundi_hint_metadata hmd;
567 int expiration = dundi_cache_time;
569 ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
570 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
571 memset(&ied, 0, sizeof(ied));
572 memset(&dr, 0, sizeof(dr));
573 memset(&hmd, 0, sizeof(hmd));
574 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
575 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
576 for (x=0;x<st->nummaps;x++)
577 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
580 for (x=0;x<ouranswers;x++) {
581 if (dr[x].weight < max)
586 /* If we do not have a canonical result, keep looking */
587 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);
589 /* Append answer in result */
592 if ((res < -1) && (!ouranswers))
593 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
596 AST_LIST_LOCK(&peers);
597 /* Truncate if "don't ask" isn't present */
598 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
600 if (ast_test_flag(st->trans, FLAG_DEAD)) {
601 ast_debug(1, "Our transaction went away!\n");
602 st->trans->thread = 0;
603 destroy_trans(st->trans, 0);
605 for (x=0;x<ouranswers;x++) {
607 if (dr[x].expiration && (expiration > dr[x].expiration))
608 expiration = dr[x].expiration;
609 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
611 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
612 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
613 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
614 st->trans->thread = 0;
616 AST_LIST_UNLOCK(&peers);
621 static void *dundi_precache_thread(void *data)
623 struct dundi_query_state *st = data;
624 struct dundi_ie_data ied;
625 struct dundi_hint_metadata hmd;
628 ast_debug(1, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
629 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
630 memset(&ied, 0, sizeof(ied));
632 /* Now produce precache */
633 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
635 AST_LIST_LOCK(&peers);
636 /* Truncate if "don't ask" isn't present */
637 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
639 if (ast_test_flag(st->trans, FLAG_DEAD)) {
640 ast_debug(1, "Our transaction went away!\n");
641 st->trans->thread = 0;
642 destroy_trans(st->trans, 0);
644 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
645 st->trans->thread = 0;
647 AST_LIST_UNLOCK(&peers);
652 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[]);
654 static void *dundi_query_thread(void *data)
656 struct dundi_query_state *st = data;
657 struct dundi_entity_info dei;
658 struct dundi_ie_data ied;
659 struct dundi_hint_metadata hmd;
663 ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
664 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
665 memset(&ied, 0, sizeof(ied));
666 memset(&dei, 0, sizeof(dei));
667 memset(&hmd, 0, sizeof(hmd));
668 if (!ast_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
670 ast_debug(1, "Neat, someone look for us!\n");
671 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
672 ast_copy_string(dei.org, org, sizeof(dei.org));
673 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
674 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
675 ast_copy_string(dei.country, country, sizeof(dei.country));
676 ast_copy_string(dei.email, email, sizeof(dei.email));
677 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
680 /* If we do not have a canonical result, keep looking */
681 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
683 AST_LIST_LOCK(&peers);
684 if (ast_test_flag(st->trans, FLAG_DEAD)) {
685 ast_debug(1, "Our transaction went away!\n");
686 st->trans->thread = 0;
687 destroy_trans(st->trans, 0);
690 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
691 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
692 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
693 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
694 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
695 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
696 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
697 if (!ast_strlen_zero(dei.ipaddr))
698 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
700 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
701 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
702 st->trans->thread = 0;
704 AST_LIST_UNLOCK(&peers);
709 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
711 struct dundi_query_state *st;
717 pthread_t lookupthread;
719 if (ies->eidcount > 1) {
720 /* Since it is a requirement that the first EID is the authenticating host
721 and the last EID is the root, it is permissible that the first and last EID
722 could be the same. In that case, we should go ahead copy only the "root" section
723 since we will not need it for authentication. */
724 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
727 totallen = sizeof(struct dundi_query_state);
728 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
729 st = ast_calloc(1, totallen);
731 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
732 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
734 st->ttl = ies->ttl - 1;
738 for (x=skipfirst;ies->eids[x];x++) {
739 st->eids[x-skipfirst] = (dundi_eid *)s;
740 *st->eids[x-skipfirst] = *ies->eids[x];
741 s += sizeof(dundi_eid);
743 ast_debug(1, "Answering EID query for '%s@%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
746 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
747 struct dundi_ie_data ied = { 0, };
749 ast_log(LOG_WARNING, "Unable to create thread!\n");
751 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
752 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
756 struct dundi_ie_data ied = { 0, };
757 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
758 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
764 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
769 char eidpeer_str[20];
770 char eidroot_str[20];
775 expiration = dundi_cache_time;
777 /* Only cache hint if "don't ask" is there... */
778 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
781 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
783 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
784 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
785 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
786 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
789 timeout += expiration;
790 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
792 ast_db_put("dundi/cache", key1, data);
793 ast_debug(1, "Caching hint at '%s'\n", key1);
794 ast_db_put("dundi/cache", key2, data);
795 ast_debug(1, "Caching hint at '%s'\n", key2);
799 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
805 char eidpeer_str[20];
806 char eidroot_str[20];
810 expiration = dundi_cache_time;
812 /* Keep pushes a little longer, cut pulls a little short */
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), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
822 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
823 /* Build request string */
825 timeout += expiration;
826 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
827 for (x=start;x<req->respcount;x++) {
828 /* Skip anything with an illegal pipe in it */
829 if (strchr(req->dr[x].dest, '|'))
831 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
832 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
833 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
835 ast_db_put("dundi/cache", key1, data);
836 ast_db_put("dundi/cache", key2, data);
840 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
842 struct dundi_query_state *st;
845 struct dundi_ie_data ied;
847 struct dundi_result dr2[MAX_RESULTS];
848 struct dundi_request dr;
849 struct dundi_hint_metadata hmd;
851 struct dundi_mapping *cur;
855 pthread_t lookupthread;
857 memset(&dr2, 0, sizeof(dr2));
858 memset(&dr, 0, sizeof(dr));
859 memset(&hmd, 0, sizeof(hmd));
861 /* Forge request structure to hold answers for cache */
862 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
864 dr.maxcount = MAX_RESULTS;
865 dr.expiration = dundi_cache_time;
867 dr.pfds[0] = dr.pfds[1] = -1;
869 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
870 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
872 for (x=0;x<ies->anscount;x++) {
873 if (trans->parent->respcount < trans->parent->maxcount) {
874 /* Make sure it's not already there */
875 for (z=0;z<trans->parent->respcount;z++) {
876 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
877 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
880 if (z == trans->parent->respcount) {
881 /* Copy into parent responses */
882 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
883 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
884 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
885 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
886 if (ies->expiration > 0)
887 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
889 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
890 ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
891 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
892 &ies->answers[x]->eid);
893 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
894 sizeof(trans->parent->dr[trans->parent->respcount].dest));
895 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
896 sizeof(trans->parent->dr[trans->parent->respcount].tech));
897 trans->parent->respcount++;
898 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
899 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
900 /* Update weight if appropriate */
901 trans->parent->dr[z].weight = ies->answers[x]->weight;
904 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
905 trans->parent->number, trans->parent->dcontext);
908 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
909 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
911 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
913 totallen = sizeof(struct dundi_query_state);
914 /* Count matching map entries */
916 AST_LIST_TRAVERSE(&mappings, cur, list) {
917 if (!strcasecmp(cur->dcontext, ccontext))
921 /* If no maps, return -1 immediately */
925 if (ies->eidcount > 1) {
926 /* Since it is a requirement that the first EID is the authenticating host
927 and the last EID is the root, it is permissible that the first and last EID
928 could be the same. In that case, we should go ahead copy only the "root" section
929 since we will not need it for authentication. */
930 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
934 /* Prepare to run a query and then propagate that as necessary */
935 totallen += mapcount * sizeof(struct dundi_mapping);
936 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
937 st = ast_calloc(1, totallen);
939 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
940 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
942 st->ttl = ies->ttl - 1;
943 st->nocache = ies->cbypass;
947 for (x=skipfirst;ies->eids[x];x++) {
948 st->eids[x-skipfirst] = (dundi_eid *)s;
949 *st->eids[x-skipfirst] = *ies->eids[x];
950 st->directs[x-skipfirst] = ies->eid_direct[x];
951 s += sizeof(dundi_eid);
953 /* Append mappings */
955 st->maps = (struct dundi_mapping *)s;
956 AST_LIST_TRAVERSE(&mappings, cur, list) {
957 if (!strcasecmp(cur->dcontext, ccontext)) {
960 st->maps[x].list.next = NULL;
965 st->nummaps = mapcount;
966 ast_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
968 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
970 ast_log(LOG_WARNING, "Unable to create thread!\n");
972 memset(&ied, 0, sizeof(ied));
973 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
974 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
978 ast_log(LOG_WARNING, "Out of memory!\n");
979 memset(&ied, 0, sizeof(ied));
980 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
981 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
987 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
989 struct dundi_query_state *st;
992 struct dundi_ie_data ied;
994 struct dundi_mapping *cur;
998 pthread_t lookupthread;
999 totallen = sizeof(struct dundi_query_state);
1000 /* Count matching map entries */
1001 AST_LIST_TRAVERSE(&mappings, cur, list) {
1002 if (!strcasecmp(cur->dcontext, ccontext))
1005 /* If no maps, return -1 immediately */
1009 if (ies->eidcount > 1) {
1010 /* Since it is a requirement that the first EID is the authenticating host
1011 and the last EID is the root, it is permissible that the first and last EID
1012 could be the same. In that case, we should go ahead copy only the "root" section
1013 since we will not need it for authentication. */
1014 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1018 totallen += mapcount * sizeof(struct dundi_mapping);
1019 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1020 st = ast_calloc(1, totallen);
1022 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1023 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1025 st->ttl = ies->ttl - 1;
1026 st->nocache = ies->cbypass;
1030 for (x=skipfirst;ies->eids[x];x++) {
1031 st->eids[x-skipfirst] = (dundi_eid *)s;
1032 *st->eids[x-skipfirst] = *ies->eids[x];
1033 st->directs[x-skipfirst] = ies->eid_direct[x];
1034 s += sizeof(dundi_eid);
1036 /* Append mappings */
1038 st->maps = (struct dundi_mapping *)s;
1039 AST_LIST_TRAVERSE(&mappings, cur, list) {
1040 if (!strcasecmp(cur->dcontext, ccontext)) {
1043 st->maps[x].list.next = NULL;
1048 st->nummaps = mapcount;
1049 ast_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1051 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
1053 ast_log(LOG_WARNING, "Unable to create thread!\n");
1055 memset(&ied, 0, sizeof(ied));
1056 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1057 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1061 ast_log(LOG_WARNING, "Out of memory!\n");
1062 memset(&ied, 0, sizeof(ied));
1063 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1064 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1070 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1073 char *ptr, *term, *src;
1075 struct ast_flags flags;
1081 /* Build request string */
1082 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1085 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1086 int expiration = timeout - now;
1087 if (expiration > 0) {
1088 ast_debug(1, "Found cache expiring in %d seconds!\n", expiration);
1090 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1092 term = strchr(ptr, '|');
1095 src = strrchr(ptr, '/');
1101 ast_debug(1, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1102 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1103 /* Make sure it's not already there */
1104 for (z=0;z<req->respcount;z++) {
1105 if ((req->dr[z].techint == tech) &&
1106 !strcmp(req->dr[z].dest, ptr))
1109 if (z == req->respcount) {
1110 /* Copy into parent responses */
1111 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1112 req->dr[req->respcount].weight = weight;
1113 req->dr[req->respcount].techint = tech;
1114 req->dr[req->respcount].expiration = expiration;
1115 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1116 ast_eid_to_str(req->dr[req->respcount].eid_str,
1117 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1118 ast_copy_string(req->dr[req->respcount].dest, ptr,
1119 sizeof(req->dr[req->respcount].dest));
1120 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1121 sizeof(req->dr[req->respcount].tech));
1123 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1124 } else if (req->dr[z].weight > weight)
1125 req->dr[z].weight = weight;
1129 /* We found *something* cached */
1130 if (expiration < *lowexpiration)
1131 *lowexpiration = expiration;
1134 ast_db_del("dundi/cache", key);
1136 ast_db_del("dundi/cache", key);
1142 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1146 char eidroot_str[20];
1150 char eid_str_full[20];
1155 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1156 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1157 ast_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1158 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1159 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1160 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1161 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1162 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1163 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1165 if (!req->respcount) {
1167 /* Look and see if we have a hint that would preclude us from looking at this
1168 peer for this number. */
1169 if (!(tmp[x] = req->number[x]))
1172 /* Check for hints */
1173 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1174 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1175 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1176 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1177 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1178 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1180 if (strlen(tmp) > strlen(req->hmd->exten)) {
1181 /* Update meta data if appropriate */
1182 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1192 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1194 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1196 if (!trans->addr.sin_addr.s_addr)
1197 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1198 trans->us_eid = p->us_eid;
1199 trans->them_eid = p->eid;
1200 /* Enable encryption if appropriate */
1201 if (!ast_strlen_zero(p->inkey))
1202 ast_set_flag(trans, FLAG_ENCRYPT);
1204 trans->autokilltimeout = p->maxms;
1205 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1206 if (p->lastms > 1) {
1207 trans->retranstimer = p->lastms * 2;
1208 /* Keep it from being silly */
1209 if (trans->retranstimer < 150)
1210 trans->retranstimer = 150;
1212 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1213 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1215 trans->autokilltimeout = global_autokilltimeout;
1218 /*! \note Called with the peers list already locked */
1219 static int do_register_expire(const void *data)
1221 struct dundi_peer *peer = (struct dundi_peer *)data;
1223 ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1224 peer->registerexpire = -1;
1226 memset(&peer->addr, 0, sizeof(peer->addr));
1230 static int update_key(struct dundi_peer *peer)
1232 unsigned char key[16];
1233 struct ast_key *ekey, *skey;
1236 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1238 ast_aes_encrypt_key(key, &peer->us_ecx);
1239 ast_aes_decrypt_key(key, &peer->us_dcx);
1240 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1242 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1243 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1246 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1248 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1249 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1252 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1253 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1256 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1257 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1260 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1261 peer->sentfullkey = 0;
1263 time(&peer->keyexpire);
1264 peer->keyexpire += dundi_key_ttl;
1269 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
1271 unsigned char curblock[16];
1273 memcpy(curblock, iv, sizeof(curblock));
1276 curblock[x] ^= src[x];
1277 ast_aes_encrypt(curblock, dst, ecx);
1278 memcpy(curblock, dst, sizeof(curblock));
1285 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
1287 unsigned char lastblock[16];
1289 memcpy(lastblock, iv, sizeof(lastblock));
1291 ast_aes_decrypt(src, dst, dcx);
1293 dst[x] ^= lastblock[x];
1294 memcpy(lastblock, src, sizeof(lastblock));
1302 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)
1304 int space = *dstlen;
1305 unsigned long bytes;
1306 struct dundi_hdr *h;
1307 unsigned char *decrypt_space;
1308 decrypt_space = alloca(srclen);
1311 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1313 h = (struct dundi_hdr *)dst;
1316 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1317 ast_debug(1, "Ouch, uncompress failed :(\n");
1321 *dstlen = bytes + 6;
1322 /* Return new header */
1326 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1328 unsigned char *compress_space;
1331 unsigned long bytes;
1332 struct dundi_ie_data ied;
1333 struct dundi_peer *peer;
1334 unsigned char iv[16];
1335 len = pack->datalen + pack->datalen / 100 + 42;
1336 compress_space = alloca(len);
1337 if (compress_space) {
1338 memset(compress_space, 0, len);
1339 /* We care about everthing save the first 6 bytes of header */
1341 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1343 ast_debug(1, "Ouch, compression failed!\n");
1346 memset(&ied, 0, sizeof(ied));
1347 /* Say who we are */
1348 if (!pack->h->iseqno && !pack->h->oseqno) {
1349 /* Need the key in the first copy */
1350 if (!(peer = find_peer(&trans->them_eid)))
1352 if (update_key(peer))
1354 if (!peer->sentfullkey)
1355 ast_set_flag(trans, FLAG_SENDFULLKEY);
1356 /* Append key data */
1357 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1358 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1359 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1360 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1362 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1364 /* Setup contexts */
1365 trans->ecx = peer->us_ecx;
1366 trans->dcx = peer->us_dcx;
1368 /* We've sent the full key */
1369 peer->sentfullkey = 1;
1371 /* Build initialization vector */
1373 /* Add the field, rounded up to 16 bytes */
1374 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1376 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1377 ast_log(LOG_NOTICE, "Final packet too large!\n");
1380 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1381 ied.pos += ((bytes + 15) / 16) * 16;
1382 /* Reconstruct header */
1383 pack->datalen = sizeof(struct dundi_hdr);
1384 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1385 pack->h->cmdflags = 0;
1386 memcpy(pack->h->ies, ied.buf, ied.pos);
1387 pack->datalen += ied.pos;
1393 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1395 unsigned char dst[128];
1397 struct ast_key *key, *skey;
1399 ast_debug(1, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1400 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1403 } else if (!newkey || !newsig)
1405 if (!memcmp(peer->rxenckey, newkey, 128) &&
1406 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1407 /* By definition, a match */
1411 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1413 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1414 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1418 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1420 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1421 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1425 /* First check signature */
1426 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1430 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1433 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1436 /* Decrypted, passes signature */
1437 ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
1438 memcpy(peer->rxenckey, newkey, 128);
1439 memcpy(peer->rxenckey + 128, newsig, 128);
1440 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1441 ast_aes_decrypt_key(dst, &peer->them_dcx);
1442 ast_aes_encrypt_key(dst, &peer->them_ecx);
1446 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1448 struct permission *cur, *perm;
1450 memcpy(peer_dst, peer_src, sizeof(*peer_dst));
1452 memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1453 memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1455 AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1456 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1459 perm->allow = cur->allow;
1460 strcpy(perm->name, cur->name);
1462 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1465 AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1466 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1469 perm->allow = cur->allow;
1470 strcpy(perm->name, cur->name);
1472 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1476 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1478 /* Handle canonical command / response */
1479 int final = hdr->cmdresp & 0x80;
1480 int cmd = hdr->cmdresp & 0x7f;
1485 unsigned char *bufcpy;
1486 struct dundi_ie_data ied;
1487 struct dundi_ies ies;
1488 struct dundi_peer *peer = NULL;
1491 memset(&ied, 0, sizeof(ied));
1492 memset(&ies, 0, sizeof(ies));
1494 bufcpy = alloca(datalen);
1497 /* Make a copy for parsing */
1498 memcpy(bufcpy, hdr->ies, datalen);
1499 ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1500 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1501 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1506 case DUNDI_COMMAND_DPDISCOVER:
1507 case DUNDI_COMMAND_EIDQUERY:
1508 case DUNDI_COMMAND_PRECACHERQ:
1509 if (cmd == DUNDI_COMMAND_EIDQUERY)
1510 resp = DUNDI_COMMAND_EIDRESPONSE;
1511 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1512 resp = DUNDI_COMMAND_PRECACHERP;
1514 resp = DUNDI_COMMAND_DPRESPONSE;
1515 /* A dialplan or entity discover -- qualify by highest level entity */
1516 peer = find_peer(ies.eids[0]);
1518 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1519 dundi_send(trans, resp, 0, 1, &ied);
1522 trans->us_eid = peer->us_eid;
1523 if (strlen(peer->inkey)) {
1524 hasauth = encrypted;
1528 /* Okay we're authentiated and all, now we check if they're authorized */
1529 if (!ies.called_context)
1530 ies.called_context = "e164";
1531 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1532 res = dundi_answer_entity(trans, &ies, ies.called_context);
1534 if (ast_strlen_zero(ies.called_number)) {
1535 /* They're not permitted to access that context */
1536 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1537 dundi_send(trans, resp, 0, 1, &ied);
1538 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1539 (peer->model & DUNDI_MODEL_INBOUND) &&
1540 has_permission(&peer->permit, ies.called_context)) {
1541 res = dundi_answer_query(trans, &ies, ies.called_context);
1543 /* There is no such dundi context */
1544 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1545 dundi_send(trans, resp, 0, 1, &ied);
1547 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1548 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1549 has_permission(&peer->include, ies.called_context)) {
1550 res = dundi_prop_precache(trans, &ies, ies.called_context);
1552 /* There is no such dundi context */
1553 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1554 dundi_send(trans, resp, 0, 1, &ied);
1557 /* They're not permitted to access that context */
1558 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1559 dundi_send(trans, resp, 0, 1, &ied);
1563 /* They're not permitted to access that context */
1564 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1565 dundi_send(trans, resp, 0, 1, &ied);
1569 case DUNDI_COMMAND_REGREQ:
1570 /* A register request -- should only have one entity */
1571 peer = find_peer(ies.eids[0]);
1573 /* if the peer is not found and we have a valid 'any_peer' setting */
1574 if (any_peer && peer == any_peer) {
1575 /* copy any_peer into a new peer object */
1576 peer = ast_calloc(1, sizeof(*peer));
1578 deep_copy_peer(peer, any_peer);
1580 /* set EID to remote EID */
1581 peer->eid = *ies.eids[0];
1583 AST_LIST_LOCK(&peers);
1584 AST_LIST_INSERT_HEAD(&peers, peer, list);
1585 AST_LIST_UNLOCK(&peers);
1589 if (!peer || !peer->dynamic) {
1590 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1591 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1594 trans->us_eid = peer->us_eid;
1595 if (!ast_strlen_zero(peer->inkey)) {
1596 hasauth = encrypted;
1600 int expire = default_expiration;
1603 AST_SCHED_DEL(sched, peer->registerexpire);
1604 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1605 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
1606 ntohs(trans->addr.sin_port), expire);
1607 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1608 if (inaddrcmp(&peer->addr, &trans->addr)) {
1609 ast_verb(3, "Registered DUNDi peer '%s' at '%s:%d'\n",
1610 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1611 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
1615 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1616 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1617 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1619 qualify_peer(peer, 1);
1623 case DUNDI_COMMAND_DPRESPONSE:
1624 /* A dialplan response, lets see what we got... */
1625 if (ies.cause < 1) {
1626 /* Success of some sort */
1627 ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1628 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1629 authpass = encrypted;
1633 /* Pass back up answers */
1634 if (trans->parent && trans->parent->dr) {
1635 y = trans->parent->respcount;
1636 for (x=0;x<ies.anscount;x++) {
1637 if (trans->parent->respcount < trans->parent->maxcount) {
1638 /* Make sure it's not already there */
1639 for (z=0;z<trans->parent->respcount;z++) {
1640 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1641 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1644 if (z == trans->parent->respcount) {
1645 /* Copy into parent responses */
1646 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1647 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1648 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1649 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1650 if (ies.expiration > 0)
1651 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1653 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1654 ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1655 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1656 &ies.answers[x]->eid);
1657 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1658 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1659 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1660 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1661 trans->parent->respcount++;
1662 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1663 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1664 /* Update weight if appropriate */
1665 trans->parent->dr[z].weight = ies.answers[x]->weight;
1668 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1669 trans->parent->number, trans->parent->dcontext);
1671 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1672 the cache know if this request was unaffected by our entity list. */
1673 cache_save(&trans->them_eid, trans->parent, y,
1674 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1676 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1677 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1678 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1679 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1680 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1681 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1682 sizeof(trans->parent->hmd->exten));
1685 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1688 if (ies.expiration > 0) {
1689 if (trans->parent->expiration > ies.expiration) {
1690 trans->parent->expiration = ies.expiration;
1694 /* Close connection if not final */
1696 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1700 /* Auth failure, check for data */
1702 /* Cancel if they didn't already */
1703 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1707 case DUNDI_COMMAND_EIDRESPONSE:
1708 /* A dialplan response, lets see what we got... */
1709 if (ies.cause < 1) {
1710 /* Success of some sort */
1711 ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
1712 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1713 authpass = encrypted;
1717 /* Pass back up answers */
1718 if (trans->parent && trans->parent->dei && ies.q_org) {
1719 if (!trans->parent->respcount) {
1720 trans->parent->respcount++;
1722 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1724 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1726 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1727 if (ies.q_stateprov)
1728 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1730 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1732 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1734 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1736 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1737 if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1738 /* If it's them, update our address */
1739 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
1743 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1744 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1747 /* Close connection if not final */
1749 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1753 /* Auth failure, check for data */
1755 /* Cancel if they didn't already */
1756 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1760 case DUNDI_COMMAND_REGRESPONSE:
1761 /* A dialplan response, lets see what we got... */
1762 if (ies.cause < 1) {
1764 /* Success of some sort */
1765 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1766 hasauth = encrypted;
1771 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1773 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1774 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1777 ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
1778 ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1779 /* Close connection if not final */
1781 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1784 /* Auth failure, cancel if they didn't for some reason */
1786 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1790 case DUNDI_COMMAND_INVALID:
1791 case DUNDI_COMMAND_NULL:
1792 case DUNDI_COMMAND_PRECACHERP:
1793 /* Do nothing special */
1795 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1797 case DUNDI_COMMAND_ENCREJ:
1798 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1799 /* No really, it's over at this point */
1801 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1803 /* Send with full key */
1804 ast_set_flag(trans, FLAG_SENDFULLKEY);
1806 /* Ooops, we got a final message, start by sending ACK... */
1807 dundi_ack(trans, hdr->cmdresp & 0x80);
1808 trans->aseqno = trans->iseqno;
1809 /* Now, we gotta create a new transaction */
1810 if (!reset_transaction(trans)) {
1811 /* Make sure handle_frame doesn't destroy us */
1812 hdr->cmdresp &= 0x7f;
1813 /* Parse the message we transmitted */
1814 memset(&ies, 0, sizeof(ies));
1815 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1816 /* Reconstruct outgoing encrypted packet */
1817 memset(&ied, 0, sizeof(ied));
1818 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1819 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1820 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1822 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1823 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
1824 peer->sentfullkey = 1;
1829 case DUNDI_COMMAND_ENCRYPT:
1831 /* No nested encryption! */
1832 if ((trans->iseqno == 1) && !trans->oseqno) {
1833 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1834 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1835 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1837 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1841 apply_peer(trans, peer);
1842 /* Key passed, use new contexts for this session */
1843 trans->ecx = peer->them_ecx;
1844 trans->dcx = peer->them_dcx;
1846 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1847 struct dundi_hdr *dhdr;
1848 unsigned char decoded[MAX_PACKET_SIZE];
1850 ddatalen = sizeof(decoded);
1851 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1853 /* Handle decrypted response */
1855 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1856 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1857 /* Carry back final flag */
1858 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1861 ast_debug(1, "Ouch, decrypt failed :(\n");
1866 /* Turn off encryption */
1867 ast_clear_flag(trans, FLAG_ENCRYPT);
1868 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1872 /* Send unknown command if we don't know it, with final flag IFF it's the
1873 first command in the dialog and only if we haven't recieved final notification */
1875 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1876 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1882 static void destroy_packet(struct dundi_packet *pack, int needfree);
1883 static void destroy_packets(struct packetlist *p)
1885 struct dundi_packet *pack;
1887 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1888 AST_SCHED_DEL(sched, pack->retransid);
1894 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1896 struct dundi_packet *pack;
1898 /* Ack transmitted packet corresponding to iseqno */
1899 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
1900 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1901 destroy_packet(pack, 0);
1902 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
1903 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1904 destroy_packets(&trans->lasttrans);
1906 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
1907 AST_SCHED_DEL(sched, trans->autokillid);
1915 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1917 struct dundi_transaction *trans;
1918 trans = find_transaction(h, sin);
1920 dundi_reject(h, sin);
1923 /* Got a transaction, see where this header fits in */
1924 if (h->oseqno == trans->iseqno) {
1925 /* Just what we were looking for... Anything but ack increments iseqno */
1926 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1927 /* If final, we're done */
1928 destroy_trans(trans, 0);
1931 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1932 trans->oiseqno = trans->iseqno;
1934 handle_command_response(trans, h, datalen, 0);
1936 if (trans->aseqno != trans->iseqno) {
1937 dundi_ack(trans, h->cmdresp & 0x80);
1938 trans->aseqno = trans->iseqno;
1940 /* Delete any saved last transmissions */
1941 destroy_packets(&trans->lasttrans);
1942 if (h->cmdresp & 0x80) {
1943 /* Final -- destroy now */
1944 destroy_trans(trans, 0);
1946 } else if (h->oseqno == trans->oiseqno) {
1947 /* Last incoming sequence number -- send ACK without processing */
1948 dundi_ack(trans, 0);
1950 /* Out of window -- simply drop */
1951 ast_debug(1, "Dropping packet out of window!\n");
1956 static int socket_read(int *id, int fd, short events, void *cbdata)
1958 struct sockaddr_in sin;
1960 struct dundi_hdr *h;
1961 char buf[MAX_PACKET_SIZE];
1962 socklen_t len = sizeof(sin);
1964 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1966 if (errno != ECONNREFUSED)
1967 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1970 if (res < sizeof(struct dundi_hdr)) {
1971 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
1975 h = (struct dundi_hdr *) buf;
1977 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
1978 AST_LIST_LOCK(&peers);
1979 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
1980 AST_LIST_UNLOCK(&peers);
1984 static void build_secret(char *secret, int seclen)
1986 unsigned char tmp[16];
1990 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
1991 /* Eliminate potential bad characters */
1992 while((s = strchr(secret, ';'))) *s = '+';
1993 while((s = strchr(secret, '/'))) *s = '+';
1994 while((s = strchr(secret, ':'))) *s = '+';
1995 while((s = strchr(secret, '@'))) *s = '+';
1999 static void save_secret(const char *newkey, const char *oldkey)
2003 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2005 snprintf(tmp, sizeof(tmp), "%s", newkey);
2006 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2007 ast_db_put(secretpath, "secret", tmp);
2008 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2009 ast_db_put(secretpath, "secretexpiry", tmp);
2012 static void load_password(void)
2019 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2020 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2021 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2022 current = strchr(tmp, ';');
2029 if ((time(NULL) - expired) < 0) {
2030 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2031 expired = time(NULL) + DUNDI_SECRET_TIME;
2032 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2041 /* Current key is still valid, just setup rotatation properly */
2042 ast_copy_string(cursecret, current, sizeof(cursecret));
2043 rotatetime = expired;
2045 /* Current key is out of date, rotate or eliminate all together */
2046 build_secret(cursecret, sizeof(cursecret));
2047 save_secret(cursecret, last);
2051 static void check_password(void)
2058 printf("%ld/%ld\n", now, rotatetime);
2060 if ((now - rotatetime) >= 0) {
2061 /* Time to rotate keys */
2062 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2063 build_secret(cursecret, sizeof(cursecret));
2064 save_secret(cursecret, oldsecret);
2068 static void *network_thread(void *ignore)
2070 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2071 from the network, and queue them for delivery to the channels */
2073 /* Establish I/O callback for socket read */
2074 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2076 while (!dundi_shutdown) {
2077 res = ast_sched_wait(sched);
2078 if ((res > 1000) || (res < 0))
2080 res = ast_io_wait(io, res);
2082 AST_LIST_LOCK(&peers);
2083 ast_sched_runq(sched);
2084 AST_LIST_UNLOCK(&peers);
2089 netthreadid = AST_PTHREADT_NULL;
2094 static void *process_precache(void *ign)
2096 struct dundi_precache_queue *qe;
2102 while (!dundi_shutdown) {
2105 AST_LIST_LOCK(&pcq);
2106 if ((qe = AST_LIST_FIRST(&pcq))) {
2107 if (!qe->expiration) {
2108 /* Gone... Remove... */
2109 AST_LIST_REMOVE_HEAD(&pcq, list);
2111 } else if (qe->expiration < now) {
2112 /* Process this entry */
2114 ast_copy_string(context, qe->context, sizeof(context));
2115 ast_copy_string(number, qe->number, sizeof(number));
2119 AST_LIST_UNLOCK(&pcq);
2121 dundi_precache(context, number);
2126 precachethreadid = AST_PTHREADT_NULL;
2131 static int start_network_thread(void)
2133 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2134 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2138 static char *dundi_do_debug_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2142 e->command = "dundi [no] debug";
2144 "Usage: dundi [no] debug\n"
2145 " Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2150 if (a->argc < 2 || a->argc > 3)
2151 return CLI_SHOWUSAGE;
2154 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2157 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2162 static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2166 e->command = "dundi set debug {on|off}";
2168 "Usage: dundi set debug {on|off}\n"
2169 " Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2175 if (a->argc != e->args)
2176 return CLI_SHOWUSAGE;
2178 if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2180 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2183 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2188 static char *dundi_do_store_history_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2192 e->command = "dundi [no] store history";
2194 "Usage: dundi [no] store history\n"
2195 " Enables/Disables storing of DUNDi requests and times for debugging\n"
2201 if (a->argc < 3 || a->argc > 4)
2202 return CLI_SHOWUSAGE;
2205 global_storehistory = 1;
2206 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2208 global_storehistory = 0;
2209 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2214 static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2218 e->command = "dundi store history {on|off}";
2220 "Usage: dundi store history {on|off}\n"
2221 " Enables/Disables storing of DUNDi requests and times for debugging\n"
2228 if (a->argc != e->args)
2229 return CLI_SHOWUSAGE;
2231 if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2232 global_storehistory = 1;
2233 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2235 global_storehistory = 0;
2236 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2241 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2246 e->command = "dundi flush [stats]";
2248 "Usage: dundi flush [stats]\n"
2249 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2250 "'stats' is present, clears timer statistics instead of normal\n"
2256 if ((a->argc < 2) || (a->argc > 3))
2257 return CLI_SHOWUSAGE;
2259 if (!strcasecmp(a->argv[2], "stats"))
2262 return CLI_SHOWUSAGE;
2265 /* Flush statistics */
2266 struct dundi_peer *p;
2268 AST_LIST_LOCK(&peers);
2269 AST_LIST_TRAVERSE(&peers, p, list) {
2270 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2272 ast_free(p->lookups[x]);
2273 p->lookups[x] = NULL;
2274 p->lookuptimes[x] = 0;
2278 AST_LIST_UNLOCK(&peers);
2280 ast_db_deltree("dundi/cache", NULL);
2281 ast_cli(a->fd, "DUNDi Cache Flushed\n");
2286 static char *model2str(int model)
2289 case DUNDI_MODEL_INBOUND:
2291 case DUNDI_MODEL_OUTBOUND:
2293 case DUNDI_MODEL_SYMMETRIC:
2300 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2304 struct dundi_peer *p;
2309 AST_LIST_LOCK(&peers);
2311 AST_LIST_TRAVERSE(&peers, p, list) {
2312 const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2313 if (!strncasecmp(word, s, len) && ++which > state) {
2314 ret = ast_strdup(s);
2318 AST_LIST_UNLOCK(&peers);
2322 static int rescomp(const void *a, const void *b)
2324 const struct dundi_result *resa, *resb;
2327 if (resa->weight < resb->weight)
2329 if (resa->weight > resb->weight)
2334 static void sort_results(struct dundi_result *results, int count)
2336 qsort(results, count, sizeof(results[0]), rescomp);
2339 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2347 struct dundi_result dr[MAX_RESULTS];
2348 struct timeval start;
2351 e->command = "dundi lookup";
2353 "Usage: dundi lookup <number>[@context] [bypass]\n"
2354 " Lookup the given number within the given DUNDi context\n"
2355 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2356 "keyword is specified.\n";
2362 if ((a->argc < 3) || (a->argc > 4))
2363 return CLI_SHOWUSAGE;
2365 if (!strcasecmp(a->argv[3], "bypass"))
2368 return CLI_SHOWUSAGE;
2370 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2371 context = strchr(tmp, '@');
2376 start = ast_tvnow();
2377 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2380 ast_cli(a->fd, "DUNDi lookup returned error.\n");
2382 ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2384 sort_results(dr, res);
2385 for (x=0;x<res;x++) {
2386 ast_cli(a->fd, "%3d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
2387 ast_cli(a->fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2389 ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2393 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2398 struct timeval start;
2401 e->command = "dundi precache";
2403 "Usage: dundi precache <number>[@context]\n"
2404 " Lookup the given number within the given DUNDi context\n"
2405 "(or e164 if none is specified) and precaches the results to any\n"
2406 "upstream DUNDi push servers.\n";
2411 if ((a->argc < 3) || (a->argc > 3))
2412 return CLI_SHOWUSAGE;
2413 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2414 context = strchr(tmp, '@');
2419 start = ast_tvnow();
2420 res = dundi_precache(context, tmp);
2423 ast_cli(a->fd, "DUNDi precache returned error.\n");
2425 ast_cli(a->fd, "DUNDi precache returned no error.\n");
2426 ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2430 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2436 struct dundi_entity_info dei;
2439 e->command = "dundi query";
2441 "Usage: dundi query <entity>[@context]\n"
2442 " Attempts to retrieve contact information for a specific\n"
2443 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2444 "e164 if none is specified).\n";
2449 if ((a->argc < 3) || (a->argc > 3))
2450 return CLI_SHOWUSAGE;
2451 if (ast_str_to_eid(&eid, a->argv[2])) {
2452 ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2453 return CLI_SHOWUSAGE;
2455 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2456 context = strchr(tmp, '@');
2461 res = dundi_query_eid(&dei, context, eid);
2463 ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2465 ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2467 ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2468 ast_cli(a->fd, "Department: %s\n", dei.orgunit);
2469 ast_cli(a->fd, "Organization: %s\n", dei.org);
2470 ast_cli(a->fd, "City/Locality: %s\n", dei.locality);
2471 ast_cli(a->fd, "State/Province: %s\n", dei.stateprov);
2472 ast_cli(a->fd, "Country: %s\n", dei.country);
2473 ast_cli(a->fd, "E-mail: %s\n", dei.email);
2474 ast_cli(a->fd, "Phone: %s\n", dei.phone);
2475 ast_cli(a->fd, "IP Address: %s\n", dei.ipaddr);
2480 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2482 struct dundi_peer *peer;
2483 struct permission *p;
2489 e->command = "dundi show peer";
2491 "Usage: dundi show peer [peer]\n"
2492 " Provide a detailed description of a specifid DUNDi peer.\n";
2495 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2498 return CLI_SHOWUSAGE;
2499 AST_LIST_LOCK(&peers);
2500 AST_LIST_TRAVERSE(&peers, peer, list) {
2501 if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2505 switch(peer->order) {
2510 order = "Secondary";
2516 order = "Quartiary";
2521 ast_cli(a->fd, "Peer: %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2522 ast_cli(a->fd, "Model: %s\n", model2str(peer->model));
2523 ast_cli(a->fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2524 ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2525 ast_cli(a->fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2526 ast_cli(a->fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2527 ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2528 if (!AST_LIST_EMPTY(&peer->include))
2529 ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2530 AST_LIST_TRAVERSE(&peer->include, p, list)
2531 ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2532 if (!AST_LIST_EMPTY(&peer->permit))
2533 ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2534 AST_LIST_TRAVERSE(&peer->permit, p, list)
2535 ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2537 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2538 if (peer->lookups[x]) {
2540 ast_cli(a->fd, "Last few query times:\n");
2541 ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2546 ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2548 ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2549 AST_LIST_UNLOCK(&peers);
2553 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2555 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2556 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2557 struct dundi_peer *peer;
2558 int registeredonly=0;
2561 int online_peers = 0;
2562 int offline_peers = 0;
2563 int unmonitored_peers = 0;
2564 int total_peers = 0;
2567 e->command = "dundi show peers [registered|include|exclude|begin]";
2569 "Usage: dundi show peers [registered|include|exclude|begin]\n"
2570 " Lists all known DUNDi peers.\n"
2571 " If 'registered' is present, only registered peers are shown.\n";
2577 if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5))
2578 return CLI_SHOWUSAGE;
2579 if ((a->argc == 4)) {
2580 if (!strcasecmp(a->argv[3], "registered")) {
2583 return CLI_SHOWUSAGE;
2585 AST_LIST_LOCK(&peers);
2586 ast_cli(a->fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2587 AST_LIST_TRAVERSE(&peers, peer, list) {
2589 int print_line = -1;
2592 if (registeredonly && !peer->addr.sin_addr.s_addr)
2595 if (peer->lastms < 0) {
2596 strcpy(status, "UNREACHABLE");
2599 else if (peer->lastms > peer->maxms) {
2600 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2603 else if (peer->lastms) {
2604 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2608 strcpy(status, "UNKNOWN");
2612 strcpy(status, "Unmonitored");
2613 unmonitored_peers++;
2616 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2618 strcpy(avgms, "Unavail");
2619 snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2620 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2621 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2624 if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2626 } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2628 } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2636 ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2637 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2638 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2641 ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2642 AST_LIST_UNLOCK(&peers);
2648 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2650 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2651 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2652 struct dundi_transaction *trans;
2655 e->command = "dundi show trans";
2657 "Usage: dundi show trans\n"
2658 " Lists all known DUNDi transactions.\n";
2664 return CLI_SHOWUSAGE;
2665 AST_LIST_LOCK(&peers);
2666 ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2667 AST_LIST_TRAVERSE(&alltrans, trans, all) {
2668 ast_cli(a->fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
2669 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2671 AST_LIST_UNLOCK(&peers);
2677 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2682 e->command = "dundi show entityid";
2684 "Usage: dundi show entityid\n"
2685 " Displays the global entityid for this host.\n";
2691 return CLI_SHOWUSAGE;
2692 AST_LIST_LOCK(&peers);
2693 ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2694 AST_LIST_UNLOCK(&peers);
2695 ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2699 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2701 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2702 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2703 struct dundi_request *req;
2707 e->command = "dundi show requests";
2709 "Usage: dundi show requests\n"
2710 " Lists all known pending DUNDi requests.\n";
2716 return CLI_SHOWUSAGE;
2717 AST_LIST_LOCK(&peers);
2718 ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2719 AST_LIST_TRAVERSE(&requests, req, list) {
2720 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2721 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2723 AST_LIST_UNLOCK(&peers);
2729 /* Grok-a-dial DUNDi */
2731 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2733 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2734 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2735 struct dundi_mapping *map;
2740 e->command = "dundi show mappings";
2742 "Usage: dundi show mappings\n"
2743 " Lists all known DUNDi mappings.\n";
2749 return CLI_SHOWUSAGE;
2750 AST_LIST_LOCK(&peers);
2751 ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2752 AST_LIST_TRAVERSE(&mappings, map, list) {
2753 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map));
2754 ast_cli(a->fd, FORMAT, map->dcontext, weight,
2755 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2756 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2758 AST_LIST_UNLOCK(&peers);
2764 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2766 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2767 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2768 struct dundi_precache_queue *qe;
2773 e->command = "dundi show precache";
2775 "Usage: dundi show precache\n"
2776 " Lists all known DUNDi scheduled precache updates.\n";
2782 return CLI_SHOWUSAGE;
2784 ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2785 AST_LIST_LOCK(&pcq);
2786 AST_LIST_TRAVERSE(&pcq, qe, list) {
2787 s = qe->expiration - now;
2792 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2794 AST_LIST_UNLOCK(&pcq);
2801 static struct ast_cli_entry cli_dundi_do_debug_deprecated = AST_CLI_DEFINE(dundi_do_debug_deprecated, "Enable/Disable DUNDi debugging");
2802 static struct ast_cli_entry cli_dundi_do_store_history_deprecated = AST_CLI_DEFINE(dundi_do_store_history_deprecated, "Enable/Disable DUNDi historic records");
2803 static struct ast_cli_entry cli_dundi[] = {
2804 AST_CLI_DEFINE(dundi_set_debug, "Enable/Disable DUNDi debugging", .deprecate_cmd = &cli_dundi_do_debug_deprecated),
2805 AST_CLI_DEFINE(dundi_store_history, "Enable/Disable DUNDi historic records", .deprecate_cmd = &cli_dundi_do_store_history_deprecated),
2806 AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
2807 AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
2808 AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
2809 AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
2810 AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
2811 AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
2812 AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
2813 AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
2814 AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
2815 AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
2816 AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
2819 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2821 struct dundi_transaction *trans;
2824 /* Don't allow creation of transactions to non-registered peers */
2825 if (p && !p->addr.sin_addr.s_addr)
2827 tid = get_trans_id();
2830 if (!(trans = ast_calloc(1, sizeof(*trans))))
2833 if (global_storehistory) {
2834 trans->start = ast_tvnow();
2835 ast_set_flag(trans, FLAG_STOREHIST);
2837 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2838 trans->autokillid = -1;
2840 apply_peer(trans, p);
2841 if (!p->sentfullkey)
2842 ast_set_flag(trans, FLAG_SENDFULLKEY);
2844 trans->strans = tid;
2845 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
2850 static int dundi_xmit(struct dundi_packet *pack)
2854 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2855 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2857 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2858 ast_inet_ntoa(pack->parent->addr.sin_addr),
2859 ntohs(pack->parent->addr.sin_port), strerror(errno));
2866 static void destroy_packet(struct dundi_packet *pack, int needfree)
2869 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
2870 AST_SCHED_DEL(sched, pack->retransid);
2875 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2877 struct dundi_peer *peer;
2882 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2883 AST_LIST_TRAVERSE(&peers, peer, list) {
2884 if (peer->regtrans == trans)
2885 peer->regtrans = NULL;
2886 if (peer->qualtrans == trans) {
2888 if (peer->lastms > -1)
2889 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2892 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2895 if (ms < peer->maxms) {
2896 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2897 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2898 } else if (peer->lastms < peer->maxms) {
2899 ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
2903 peer->qualtrans = NULL;
2905 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2906 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2907 if (!ast_eid_cmp(&trans->them_eid, &peer->eid)) {
2910 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2911 ast_free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2912 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2913 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2914 peer->lookups[x] = peer->lookups[x-1];
2915 if (peer->lookups[x]) {
2916 peer->avgms += peer->lookuptimes[x];
2920 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2921 peer->lookups[0] = ast_malloc(strlen(trans->parent->number)&nbs