change the required dependency for codec_dahdi to only be satisfied by DAHDI and...
[asterisk-bristuff.git] / pbx / pbx_dundi.c
blob94b6e702d6125db2a84c5280f6ed05858f915865
1 /*
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.
19 /*! \file
21 * \brief Distributed Universal Number Discovery (DUNDi)
24 /*** MODULEINFO
25 <depend>zlib</depend>
26 ***/
28 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <sys/socket.h>
38 #include <string.h>
39 #include <errno.h>
40 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
41 #include <sys/types.h>
42 #include <netinet/in_systm.h>
43 #endif
44 #include <netinet/ip.h>
45 #include <sys/ioctl.h>
46 #include <netinet/in.h>
47 #include <net/if.h>
48 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
49 #include <net/if_dl.h>
50 #include <ifaddrs.h>
51 #endif
52 #include <zlib.h>
53 #include <sys/signal.h>
54 #include <pthread.h>
56 #include "asterisk/file.h"
57 #include "asterisk/logger.h"
58 #include "asterisk/channel.h"
59 #include "asterisk/config.h"
60 #include "asterisk/options.h"
61 #include "asterisk/pbx.h"
62 #include "asterisk/module.h"
63 #include "asterisk/frame.h"
64 #include "asterisk/file.h"
65 #include "asterisk/cli.h"
66 #include "asterisk/lock.h"
67 #include "asterisk/md5.h"
68 #include "asterisk/dundi.h"
69 #include "asterisk/sched.h"
70 #include "asterisk/io.h"
71 #include "asterisk/utils.h"
72 #include "asterisk/crypto.h"
73 #include "asterisk/astdb.h"
74 #include "asterisk/acl.h"
75 #include "asterisk/aes.h"
77 #include "dundi-parser.h"
79 #define MAX_RESULTS 64
81 #define MAX_PACKET_SIZE 8192
83 #define DUNDI_MODEL_INBOUND (1 << 0)
84 #define DUNDI_MODEL_OUTBOUND (1 << 1)
85 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
87 /*! Keep times of last 10 lookups */
88 #define DUNDI_TIMING_HISTORY 10
90 #define FLAG_ISREG (1 << 0) /*!< Transaction is register request */
91 #define FLAG_DEAD (1 << 1) /*!< Transaction is dead */
92 #define FLAG_FINAL (1 << 2) /*!< Transaction has final message sent */
93 #define FLAG_ISQUAL (1 << 3) /*!< Transaction is a qualification */
94 #define FLAG_ENCRYPT (1 << 4) /*!< Transaction is encrypted wiht ECX/DCX */
95 #define FLAG_SENDFULLKEY (1 << 5) /*!< Send full key on transaction */
96 #define FLAG_STOREHIST (1 << 6) /*!< Record historic performance */
98 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
100 #if 0
101 #define DUNDI_SECRET_TIME 15 /* Testing only */
102 #else
103 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
104 #endif
106 #define KEY_OUT 0
107 #define KEY_IN 1
109 static struct io_context *io;
110 static struct sched_context *sched;
111 static int netsocket = -1;
112 static pthread_t netthreadid = AST_PTHREADT_NULL;
113 static pthread_t precachethreadid = AST_PTHREADT_NULL;
114 static int tos = 0;
115 static int dundidebug = 0;
116 static int authdebug = 0;
117 static int dundi_ttl = DUNDI_DEFAULT_TTL;
118 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
119 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
120 static int global_autokilltimeout = 0;
121 static dundi_eid global_eid;
122 static int default_expiration = 60;
123 static int global_storehistory = 0;
124 static char dept[80];
125 static char org[80];
126 static char locality[80];
127 static char stateprov[80];
128 static char country[80];
129 static char email[80];
130 static char phone[80];
131 static char secretpath[80];
132 static char cursecret[80];
133 static char ipaddr[80];
134 static time_t rotatetime;
135 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
136 static int dundi_shutdown = 0;
138 struct permission {
139 AST_LIST_ENTRY(permission) list;
140 int allow;
141 char name[0];
144 struct dundi_packet {
145 AST_LIST_ENTRY(dundi_packet) list;
146 struct dundi_hdr *h;
147 int datalen;
148 struct dundi_transaction *parent;
149 int retransid;
150 int retrans;
151 unsigned char data[0];
154 struct dundi_hint_metadata {
155 unsigned short flags;
156 char exten[AST_MAX_EXTENSION];
159 struct dundi_precache_queue {
160 AST_LIST_ENTRY(dundi_precache_queue) list;
161 char *context;
162 time_t expiration;
163 char number[0];
166 struct dundi_request;
168 struct dundi_transaction {
169 struct sockaddr_in addr; /*!< Other end of transaction */
170 struct timeval start; /*!< When this transaction was created */
171 dundi_eid eids[DUNDI_MAX_STACK + 1];
172 int eidcount; /*!< Number of eids in eids */
173 dundi_eid us_eid; /*!< Our EID, to them */
174 dundi_eid them_eid; /*!< Their EID, to us */
175 aes_encrypt_ctx ecx; /*!< AES 128 Encryption context */
176 aes_decrypt_ctx dcx; /*!< AES 128 Decryption context */
177 unsigned int flags; /*!< Has final packet been sent */
178 int ttl; /*!< Remaining TTL for queries on this one */
179 int thread; /*!< We have a calling thread */
180 int retranstimer; /*!< How long to wait before retransmissions */
181 int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
182 int autokilltimeout; /*!< Recommended timeout for autokill */
183 unsigned short strans; /*!< Our transaction identifier */
184 unsigned short dtrans; /*!< Their transaction identifer */
185 unsigned char iseqno; /*!< Next expected received seqno */
186 unsigned char oiseqno; /*!< Last received incoming seqno */
187 unsigned char oseqno; /*!< Next transmitted seqno */
188 unsigned char aseqno; /*!< Last acknowledge seqno */
189 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
190 struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
191 struct dundi_request *parent; /*!< Parent request (if there is one) */
192 AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
193 AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
196 struct dundi_request {
197 char dcontext[AST_MAX_EXTENSION];
198 char number[AST_MAX_EXTENSION];
199 dundi_eid query_eid;
200 dundi_eid root_eid;
201 struct dundi_result *dr;
202 struct dundi_entity_info *dei;
203 struct dundi_hint_metadata *hmd;
204 int maxcount;
205 int respcount;
206 int expiration;
207 int cbypass;
208 int pfds[2];
209 unsigned long crc32; /*!< CRC-32 of all but root EID's in avoid list */
210 AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */
211 AST_LIST_ENTRY(dundi_request) list;
214 struct dundi_mapping {
215 char dcontext[AST_MAX_EXTENSION];
216 char lcontext[AST_MAX_EXTENSION];
217 int weight;
218 int options;
219 int tech;
220 int dead;
221 char dest[AST_MAX_EXTENSION];
222 AST_LIST_ENTRY(dundi_mapping) list;
225 struct dundi_peer {
226 dundi_eid eid;
227 struct sockaddr_in addr; /*!< Address of DUNDi peer */
228 AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
229 struct permissionlist include;
230 dundi_eid us_eid;
231 char inkey[80];
232 char outkey[80];
233 int dead;
234 int registerid;
235 int qualifyid;
236 int sentfullkey;
237 int order;
238 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
239 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
240 unsigned long us_keycrc32; /*!< CRC-32 of our key */
241 aes_encrypt_ctx us_ecx; /*!< Cached AES 128 Encryption context */
242 aes_decrypt_ctx us_dcx; /*!< Cached AES 128 Decryption context */
243 unsigned long them_keycrc32; /*!< CRC-32 of our key */
244 aes_encrypt_ctx them_ecx; /*!< Cached AES 128 Encryption context */
245 aes_decrypt_ctx them_dcx; /*!< Cached AES 128 Decryption context */
246 time_t keyexpire; /*!< When to expire/recreate key */
247 int registerexpire;
248 int lookuptimes[DUNDI_TIMING_HISTORY];
249 char *lookups[DUNDI_TIMING_HISTORY];
250 int avgms;
251 struct dundi_transaction *regtrans; /*!< Registration transaction */
252 struct dundi_transaction *qualtrans; /*!< Qualify transaction */
253 int model; /*!< Pull model */
254 int pcmodel; /*!< Push/precache model */
255 /*! Dynamic peers register with us */
256 unsigned int dynamic:1;
257 int lastms; /*!< Last measured latency */
258 int maxms; /*!< Max permissible latency */
259 struct timeval qualtx; /*!< Time of transmit */
260 AST_LIST_ENTRY(dundi_peer) list;
263 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
264 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
265 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
266 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
267 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
270 * \brief Wildcard peer
272 * This peer is created if the [*] entry is specified in dundi.conf
274 static struct dundi_peer *any_peer;
276 static int dundi_xmit(struct dundi_packet *pack);
278 static void dundi_debug_output(const char *data)
280 if (dundidebug)
281 ast_verbose("%s", data);
284 static void dundi_error_output(const char *data)
286 ast_log(LOG_WARNING, "%s", data);
289 static int has_permission(struct permissionlist *permlist, char *cont)
291 struct permission *perm;
292 int res = 0;
294 AST_LIST_TRAVERSE(permlist, perm, list) {
295 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
296 res = perm->allow;
299 return res;
302 static char *tech2str(int tech)
304 switch(tech) {
305 case DUNDI_PROTO_NONE:
306 return "None";
307 case DUNDI_PROTO_IAX:
308 return "IAX2";
309 case DUNDI_PROTO_SIP:
310 return "SIP";
311 case DUNDI_PROTO_H323:
312 return "H323";
313 default:
314 return "Unknown";
318 static int str2tech(char *str)
320 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
321 return DUNDI_PROTO_IAX;
322 else if (!strcasecmp(str, "SIP"))
323 return DUNDI_PROTO_SIP;
324 else if (!strcasecmp(str, "H323"))
325 return DUNDI_PROTO_H323;
326 else
327 return -1;
330 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[]);
331 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
332 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
333 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
335 struct dundi_transaction *trans;
337 /* Look for an exact match first */
338 AST_LIST_TRAVERSE(&alltrans, trans, all) {
339 if (!inaddrcmp(&trans->addr, sin) &&
340 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
341 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
342 if (hdr->strans)
343 trans->dtrans = ntohs(hdr->strans) & 32767;
344 break;
347 if (!trans) {
348 switch(hdr->cmdresp & 0x7f) {
349 case DUNDI_COMMAND_DPDISCOVER:
350 case DUNDI_COMMAND_EIDQUERY:
351 case DUNDI_COMMAND_PRECACHERQ:
352 case DUNDI_COMMAND_REGREQ:
353 case DUNDI_COMMAND_NULL:
354 case DUNDI_COMMAND_ENCRYPT:
355 if (hdr->strans) {
356 /* Create new transaction */
357 trans = create_transaction(NULL);
358 if (trans) {
359 memcpy(&trans->addr, sin, sizeof(trans->addr));
360 trans->dtrans = ntohs(hdr->strans) & 32767;
361 } else
362 ast_log(LOG_WARNING, "Out of memory!\n");
364 break;
365 default:
366 break;
369 return trans;
372 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
374 static int dundi_ack(struct dundi_transaction *trans, int final)
376 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
378 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
380 struct {
381 struct dundi_packet pack;
382 struct dundi_hdr hdr;
383 } tmp;
384 struct dundi_transaction trans;
385 /* Never respond to an INVALID with another INVALID */
386 if (h->cmdresp == DUNDI_COMMAND_INVALID)
387 return;
388 memset(&tmp, 0, sizeof(tmp));
389 memset(&trans, 0, sizeof(trans));
390 memcpy(&trans.addr, sin, sizeof(trans.addr));
391 tmp.hdr.strans = h->dtrans;
392 tmp.hdr.dtrans = h->strans;
393 tmp.hdr.iseqno = h->oseqno;
394 tmp.hdr.oseqno = h->iseqno;
395 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
396 tmp.hdr.cmdflags = 0;
397 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
398 tmp.pack.datalen = sizeof(struct dundi_hdr);
399 tmp.pack.parent = &trans;
400 dundi_xmit(&tmp.pack);
403 static void reset_global_eid(void)
405 #if defined(SIOCGIFHWADDR)
406 int x,s;
407 char eid_str[20];
408 struct ifreq ifr;
410 s = socket(AF_INET, SOCK_STREAM, 0);
411 if (s > 0) {
412 x = 0;
413 for(x=0;x<10;x++) {
414 memset(&ifr, 0, sizeof(ifr));
415 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
416 if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
417 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
418 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s' using 'siocgifhwaddr'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
419 close(s);
420 return;
423 close(s);
425 #else
426 #if defined(ifa_broadaddr) && !defined(SOLARIS)
427 char eid_str[20];
428 struct ifaddrs *ifap;
430 if (getifaddrs(&ifap) == 0) {
431 struct ifaddrs *p;
432 for (p = ifap; p; p = p->ifa_next) {
433 if ((p->ifa_addr->sa_family == AF_LINK) && !(p->ifa_flags & IFF_LOOPBACK) && (p->ifa_flags & IFF_RUNNING)) {
434 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
435 memcpy(&(global_eid.eid), sdp->sdl_data + sdp->sdl_nlen, 6);
436 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s' using 'getifaddrs'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), p->ifa_name);
437 freeifaddrs(ifap);
438 return;
441 freeifaddrs(ifap);
443 #endif
444 #endif
445 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID. You will have to set it manually.\n");
448 static int get_trans_id(void)
450 struct dundi_transaction *t;
451 int stid = (ast_random() % 32766) + 1;
452 int tid = stid;
454 do {
455 AST_LIST_TRAVERSE(&alltrans, t, all) {
456 if (t->strans == tid)
457 break;
459 if (!t)
460 return tid;
461 tid = (tid % 32766) + 1;
462 } while (tid != stid);
464 return 0;
467 static int reset_transaction(struct dundi_transaction *trans)
469 int tid;
470 tid = get_trans_id();
471 if (tid < 1)
472 return -1;
473 trans->strans = tid;
474 trans->dtrans = 0;
475 trans->iseqno = 0;
476 trans->oiseqno = 0;
477 trans->oseqno = 0;
478 trans->aseqno = 0;
479 ast_clear_flag(trans, FLAG_FINAL);
480 return 0;
483 static struct dundi_peer *find_peer(dundi_eid *eid)
485 struct dundi_peer *cur = NULL;
487 if (!eid)
488 eid = &empty_eid;
490 AST_LIST_TRAVERSE(&peers, cur, list) {
491 if (!dundi_eid_cmp(&cur->eid,eid))
492 break;
495 if (!cur && any_peer)
496 cur = any_peer;
498 return cur;
501 static void build_iv(unsigned char *iv)
503 /* XXX Would be nice to be more random XXX */
504 unsigned int *fluffy;
505 int x;
506 fluffy = (unsigned int *)(iv);
507 for (x=0;x<4;x++)
508 fluffy[x] = ast_random();
511 struct dundi_query_state {
512 dundi_eid *eids[DUNDI_MAX_STACK + 1];
513 int directs[DUNDI_MAX_STACK + 1];
514 dundi_eid reqeid;
515 char called_context[AST_MAX_EXTENSION];
516 char called_number[AST_MAX_EXTENSION];
517 struct dundi_mapping *maps;
518 int nummaps;
519 int nocache;
520 struct dundi_transaction *trans;
521 void *chal;
522 int challen;
523 int ttl;
524 char fluffy[0];
527 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)
529 struct ast_flags flags = {0};
530 int x;
531 if (!ast_strlen_zero(map->lcontext)) {
532 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
533 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
534 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
535 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
536 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
537 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
538 if (ast_ignore_pattern(map->lcontext, called_number))
539 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
541 /* Clearly we can't say 'don't ask' anymore if we found anything... */
542 if (ast_test_flag(&flags, AST_FLAGS_ALL))
543 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
545 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
546 /* Skip partial answers */
547 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
549 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
550 struct varshead headp;
551 struct ast_var_t *newvariable;
552 ast_set_flag(&flags, map->options & 0xffff);
553 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
554 dr[anscnt].techint = map->tech;
555 dr[anscnt].weight = map->weight;
556 dr[anscnt].expiration = dundi_cache_time;
557 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
558 dr[anscnt].eid = *us_eid;
559 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
560 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
561 AST_LIST_HEAD_INIT_NOLOCK(&headp);
562 newvariable = ast_var_assign("NUMBER", called_number);
563 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
564 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
565 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
566 newvariable = ast_var_assign("SECRET", cursecret);
567 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
568 newvariable = ast_var_assign("IPADDR", ipaddr);
569 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
570 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
571 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
572 ast_var_delete(newvariable);
573 } else
574 dr[anscnt].dest[0] = '\0';
575 anscnt++;
576 } else {
577 /* No answers... Find the fewest number of digits from the
578 number for which we have no answer. */
579 char tmp[AST_MAX_EXTENSION + 1] = "";
580 for (x = 0; x < (sizeof(tmp) - 1); x++) {
581 tmp[x] = called_number[x];
582 if (!tmp[x])
583 break;
584 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
585 /* Oops found something we can't match. If this is longer
586 than the running hint, we have to consider it */
587 if (strlen(tmp) > strlen(hmd->exten)) {
588 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
590 break;
595 return anscnt;
598 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
600 static void *dundi_lookup_thread(void *data)
602 struct dundi_query_state *st = data;
603 struct dundi_result dr[MAX_RESULTS];
604 struct dundi_ie_data ied;
605 struct dundi_hint_metadata hmd;
606 char eid_str[20];
607 int res, x;
608 int ouranswers=0;
609 int max = 999999;
610 int expiration = dundi_cache_time;
612 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
613 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
614 memset(&ied, 0, sizeof(ied));
615 memset(&dr, 0, sizeof(dr));
616 memset(&hmd, 0, sizeof(hmd));
617 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
618 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
619 for (x=0;x<st->nummaps;x++)
620 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
621 if (ouranswers < 0)
622 ouranswers = 0;
623 for (x=0;x<ouranswers;x++) {
624 if (dr[x].weight < max)
625 max = dr[x].weight;
628 if (max) {
629 /* If we do not have a canonical result, keep looking */
630 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);
631 if (res > 0) {
632 /* Append answer in result */
633 ouranswers += res;
634 } else {
635 if ((res < -1) && (!ouranswers))
636 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
639 AST_LIST_LOCK(&peers);
640 /* Truncate if "don't ask" isn't present */
641 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
642 hmd.exten[0] = '\0';
643 if (ast_test_flag(st->trans, FLAG_DEAD)) {
644 ast_log(LOG_DEBUG, "Our transaction went away!\n");
645 st->trans->thread = 0;
646 destroy_trans(st->trans, 0);
647 } else {
648 for (x=0;x<ouranswers;x++) {
649 /* Add answers */
650 if (dr[x].expiration && (expiration > dr[x].expiration))
651 expiration = dr[x].expiration;
652 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
654 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
655 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
656 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
657 st->trans->thread = 0;
659 AST_LIST_UNLOCK(&peers);
660 free(st);
661 return NULL;
664 static void *dundi_precache_thread(void *data)
666 struct dundi_query_state *st = data;
667 struct dundi_ie_data ied;
668 struct dundi_hint_metadata hmd;
669 char eid_str[20];
671 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
672 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
673 memset(&ied, 0, sizeof(ied));
675 /* Now produce precache */
676 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
678 AST_LIST_LOCK(&peers);
679 /* Truncate if "don't ask" isn't present */
680 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
681 hmd.exten[0] = '\0';
682 if (ast_test_flag(st->trans, FLAG_DEAD)) {
683 ast_log(LOG_DEBUG, "Our transaction went away!\n");
684 st->trans->thread = 0;
685 destroy_trans(st->trans, 0);
686 } else {
687 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
688 st->trans->thread = 0;
690 AST_LIST_UNLOCK(&peers);
691 free(st);
692 return NULL;
695 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[]);
697 static void *dundi_query_thread(void *data)
699 struct dundi_query_state *st = data;
700 struct dundi_entity_info dei;
701 struct dundi_ie_data ied;
702 struct dundi_hint_metadata hmd;
703 char eid_str[20];
704 int res;
705 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
706 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
707 memset(&ied, 0, sizeof(ied));
708 memset(&dei, 0, sizeof(dei));
709 memset(&hmd, 0, sizeof(hmd));
710 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
711 /* Ooh, it's us! */
712 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
713 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
714 ast_copy_string(dei.org, org, sizeof(dei.org));
715 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
716 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
717 ast_copy_string(dei.country, country, sizeof(dei.country));
718 ast_copy_string(dei.email, email, sizeof(dei.email));
719 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
720 res = 1;
721 } else {
722 /* If we do not have a canonical result, keep looking */
723 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
725 AST_LIST_LOCK(&peers);
726 if (ast_test_flag(st->trans, FLAG_DEAD)) {
727 ast_log(LOG_DEBUG, "Our transaction went away!\n");
728 st->trans->thread = 0;
729 destroy_trans(st->trans, 0);
730 } else {
731 if (res) {
732 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
733 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
734 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
735 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
736 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
737 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
738 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
739 if (!ast_strlen_zero(dei.ipaddr))
740 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
742 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
743 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
744 st->trans->thread = 0;
746 AST_LIST_UNLOCK(&peers);
747 free(st);
748 return NULL;
751 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
753 struct dundi_query_state *st;
754 int totallen;
755 int x;
756 int skipfirst=0;
757 struct dundi_ie_data ied;
758 char eid_str[20];
759 char *s;
760 pthread_t lookupthread;
761 pthread_attr_t attr;
762 if (ies->eidcount > 1) {
763 /* Since it is a requirement that the first EID is the authenticating host
764 and the last EID is the root, it is permissible that the first and last EID
765 could be the same. In that case, we should go ahead copy only the "root" section
766 since we will not need it for authentication. */
767 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
768 skipfirst = 1;
770 totallen = sizeof(struct dundi_query_state);
771 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
772 st = ast_calloc(1, totallen);
773 if (st) {
774 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
775 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
776 st->trans = trans;
777 st->ttl = ies->ttl - 1;
778 if (st->ttl < 0)
779 st->ttl = 0;
780 s = st->fluffy;
781 for (x=skipfirst;ies->eids[x];x++) {
782 st->eids[x-skipfirst] = (dundi_eid *)s;
783 *st->eids[x-skipfirst] = *ies->eids[x];
784 s += sizeof(dundi_eid);
786 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);
787 pthread_attr_init(&attr);
788 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
789 trans->thread = 1;
790 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
791 trans->thread = 0;
792 ast_log(LOG_WARNING, "Unable to create thread!\n");
793 free(st);
794 memset(&ied, 0, sizeof(ied));
795 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
796 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
797 pthread_attr_destroy(&attr);
798 return -1;
800 pthread_attr_destroy(&attr);
801 } else {
802 ast_log(LOG_WARNING, "Out of memory!\n");
803 memset(&ied, 0, sizeof(ied));
804 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
805 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
806 return -1;
808 return 0;
811 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
813 int unaffected;
814 char key1[256];
815 char key2[256];
816 char eidpeer_str[20];
817 char eidroot_str[20];
818 char data[80];
819 time_t timeout;
821 if (expiration < 0)
822 expiration = dundi_cache_time;
824 /* Only cache hint if "don't ask" is there... */
825 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
826 return 0;
828 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
830 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
831 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
832 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
833 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
835 time(&timeout);
836 timeout += expiration;
837 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
839 ast_db_put("dundi/cache", key1, data);
840 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
841 ast_db_put("dundi/cache", key2, data);
842 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
843 return 0;
846 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
848 int x;
849 char key1[256];
850 char key2[256];
851 char data[1024];
852 char eidpeer_str[20];
853 char eidroot_str[20];
854 time_t timeout;
856 if (expiration < 1)
857 expiration = dundi_cache_time;
859 /* Keep pushes a little longer, cut pulls a little short */
860 if (push)
861 expiration += 10;
862 else
863 expiration -= 10;
864 if (expiration < 1)
865 expiration = 1;
866 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
867 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
868 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
869 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
870 /* Build request string */
871 time(&timeout);
872 timeout += expiration;
873 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
874 for (x=start;x<req->respcount;x++) {
875 /* Skip anything with an illegal pipe in it */
876 if (strchr(req->dr[x].dest, '|'))
877 continue;
878 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
879 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
880 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
882 ast_db_put("dundi/cache", key1, data);
883 ast_db_put("dundi/cache", key2, data);
884 return 0;
887 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
889 struct dundi_query_state *st;
890 int totallen;
891 int x,z;
892 struct dundi_ie_data ied;
893 char *s;
894 struct dundi_result dr2[MAX_RESULTS];
895 struct dundi_request dr;
896 struct dundi_hint_metadata hmd;
898 struct dundi_mapping *cur;
899 int mapcount;
900 int skipfirst = 0;
902 pthread_t lookupthread;
903 pthread_attr_t attr;
905 memset(&dr2, 0, sizeof(dr2));
906 memset(&dr, 0, sizeof(dr));
907 memset(&hmd, 0, sizeof(hmd));
909 /* Forge request structure to hold answers for cache */
910 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
911 dr.dr = dr2;
912 dr.maxcount = MAX_RESULTS;
913 dr.expiration = dundi_cache_time;
914 dr.hmd = &hmd;
915 dr.pfds[0] = dr.pfds[1] = -1;
916 trans->parent = &dr;
917 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
918 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
920 for (x=0;x<ies->anscount;x++) {
921 if (trans->parent->respcount < trans->parent->maxcount) {
922 /* Make sure it's not already there */
923 for (z=0;z<trans->parent->respcount;z++) {
924 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
925 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
926 break;
928 if (z == trans->parent->respcount) {
929 /* Copy into parent responses */
930 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
931 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
932 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
933 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
934 if (ies->expiration > 0)
935 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
936 else
937 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
938 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
939 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
940 &ies->answers[x]->eid);
941 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
942 sizeof(trans->parent->dr[trans->parent->respcount].dest));
943 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
944 sizeof(trans->parent->dr[trans->parent->respcount].tech));
945 trans->parent->respcount++;
946 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
947 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
948 /* Update weight if appropriate */
949 trans->parent->dr[z].weight = ies->answers[x]->weight;
951 } else
952 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
953 trans->parent->number, trans->parent->dcontext);
956 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
957 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
958 if (ies->hint)
959 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
961 totallen = sizeof(struct dundi_query_state);
962 /* Count matching map entries */
963 mapcount = 0;
964 AST_LIST_TRAVERSE(&mappings, cur, list) {
965 if (!strcasecmp(cur->dcontext, ccontext))
966 mapcount++;
969 /* If no maps, return -1 immediately */
970 if (!mapcount)
971 return -1;
973 if (ies->eidcount > 1) {
974 /* Since it is a requirement that the first EID is the authenticating host
975 and the last EID is the root, it is permissible that the first and last EID
976 could be the same. In that case, we should go ahead copy only the "root" section
977 since we will not need it for authentication. */
978 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
979 skipfirst = 1;
982 /* Prepare to run a query and then propagate that as necessary */
983 totallen += mapcount * sizeof(struct dundi_mapping);
984 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
985 st = ast_calloc(1, totallen);
986 if (st) {
987 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
988 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
989 st->trans = trans;
990 st->ttl = ies->ttl - 1;
991 st->nocache = ies->cbypass;
992 if (st->ttl < 0)
993 st->ttl = 0;
994 s = st->fluffy;
995 for (x=skipfirst;ies->eids[x];x++) {
996 st->eids[x-skipfirst] = (dundi_eid *)s;
997 *st->eids[x-skipfirst] = *ies->eids[x];
998 st->directs[x-skipfirst] = ies->eid_direct[x];
999 s += sizeof(dundi_eid);
1001 /* Append mappings */
1002 x = 0;
1003 st->maps = (struct dundi_mapping *)s;
1004 AST_LIST_TRAVERSE(&mappings, cur, list) {
1005 if (!strcasecmp(cur->dcontext, ccontext)) {
1006 if (x < mapcount) {
1007 st->maps[x] = *cur;
1008 st->maps[x].list.next = NULL;
1009 x++;
1013 st->nummaps = mapcount;
1014 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1015 pthread_attr_init(&attr);
1016 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1017 trans->thread = 1;
1018 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
1019 trans->thread = 0;
1020 ast_log(LOG_WARNING, "Unable to create thread!\n");
1021 free(st);
1022 memset(&ied, 0, sizeof(ied));
1023 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1024 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1025 pthread_attr_destroy(&attr);
1026 return -1;
1028 pthread_attr_destroy(&attr);
1029 } else {
1030 ast_log(LOG_WARNING, "Out of memory!\n");
1031 memset(&ied, 0, sizeof(ied));
1032 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1033 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1034 return -1;
1036 return 0;
1039 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1041 struct dundi_query_state *st;
1042 int totallen;
1043 int x;
1044 struct dundi_ie_data ied;
1045 char *s;
1046 struct dundi_mapping *cur;
1047 int mapcount = 0;
1048 int skipfirst = 0;
1050 pthread_t lookupthread;
1051 pthread_attr_t attr;
1052 totallen = sizeof(struct dundi_query_state);
1053 /* Count matching map entries */
1054 AST_LIST_TRAVERSE(&mappings, cur, list) {
1055 if (!strcasecmp(cur->dcontext, ccontext))
1056 mapcount++;
1058 /* If no maps, return -1 immediately */
1059 if (!mapcount)
1060 return -1;
1062 if (ies->eidcount > 1) {
1063 /* Since it is a requirement that the first EID is the authenticating host
1064 and the last EID is the root, it is permissible that the first and last EID
1065 could be the same. In that case, we should go ahead copy only the "root" section
1066 since we will not need it for authentication. */
1067 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1068 skipfirst = 1;
1071 totallen += mapcount * sizeof(struct dundi_mapping);
1072 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1073 st = ast_calloc(1, totallen);
1074 if (st) {
1075 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1076 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1077 st->trans = trans;
1078 st->ttl = ies->ttl - 1;
1079 st->nocache = ies->cbypass;
1080 if (st->ttl < 0)
1081 st->ttl = 0;
1082 s = st->fluffy;
1083 for (x=skipfirst;ies->eids[x];x++) {
1084 st->eids[x-skipfirst] = (dundi_eid *)s;
1085 *st->eids[x-skipfirst] = *ies->eids[x];
1086 st->directs[x-skipfirst] = ies->eid_direct[x];
1087 s += sizeof(dundi_eid);
1089 /* Append mappings */
1090 x = 0;
1091 st->maps = (struct dundi_mapping *)s;
1092 AST_LIST_TRAVERSE(&mappings, cur, list) {
1093 if (!strcasecmp(cur->dcontext, ccontext)) {
1094 if (x < mapcount) {
1095 st->maps[x] = *cur;
1096 st->maps[x].list.next = NULL;
1097 x++;
1101 st->nummaps = mapcount;
1102 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1103 pthread_attr_init(&attr);
1104 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1105 trans->thread = 1;
1106 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
1107 trans->thread = 0;
1108 ast_log(LOG_WARNING, "Unable to create thread!\n");
1109 free(st);
1110 memset(&ied, 0, sizeof(ied));
1111 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1112 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1113 pthread_attr_destroy(&attr);
1114 return -1;
1116 pthread_attr_destroy(&attr);
1117 } else {
1118 ast_log(LOG_WARNING, "Out of memory!\n");
1119 memset(&ied, 0, sizeof(ied));
1120 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1121 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1122 return -1;
1124 return 0;
1127 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1129 char data[1024];
1130 char *ptr, *term, *src;
1131 int tech;
1132 struct ast_flags flags;
1133 int weight;
1134 int length;
1135 int z;
1136 char fs[256];
1138 /* Build request string */
1139 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1140 time_t timeout;
1141 ptr = data;
1142 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1143 int expiration = timeout - now;
1144 if (expiration > 0) {
1145 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
1146 ptr += length + 1;
1147 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1148 ptr += length;
1149 term = strchr(ptr, '|');
1150 if (term) {
1151 *term = '\0';
1152 src = strrchr(ptr, '/');
1153 if (src) {
1154 *src = '\0';
1155 src++;
1156 } else
1157 src = "";
1158 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1159 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1160 /* Make sure it's not already there */
1161 for (z=0;z<req->respcount;z++) {
1162 if ((req->dr[z].techint == tech) &&
1163 !strcmp(req->dr[z].dest, ptr))
1164 break;
1166 if (z == req->respcount) {
1167 /* Copy into parent responses */
1168 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1169 req->dr[req->respcount].weight = weight;
1170 req->dr[req->respcount].techint = tech;
1171 req->dr[req->respcount].expiration = expiration;
1172 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1173 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1174 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1175 ast_copy_string(req->dr[req->respcount].dest, ptr,
1176 sizeof(req->dr[req->respcount].dest));
1177 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1178 sizeof(req->dr[req->respcount].tech));
1179 req->respcount++;
1180 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1181 } else if (req->dr[z].weight > weight)
1182 req->dr[z].weight = weight;
1183 ptr = term + 1;
1186 /* We found *something* cached */
1187 if (expiration < *lowexpiration)
1188 *lowexpiration = expiration;
1189 return 1;
1190 } else
1191 ast_db_del("dundi/cache", key);
1192 } else
1193 ast_db_del("dundi/cache", key);
1196 return 0;
1199 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1201 char key[256];
1202 char eid_str[20];
1203 char eidroot_str[20];
1204 time_t now;
1205 int res=0;
1206 int res2=0;
1207 char eid_str_full[20];
1208 char tmp[256]="";
1209 int x;
1211 time(&now);
1212 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1213 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1214 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1215 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1216 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1217 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1218 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1219 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1220 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1221 x = 0;
1222 if (!req->respcount) {
1223 while(!res2) {
1224 /* Look and see if we have a hint that would preclude us from looking at this
1225 peer for this number. */
1226 if (!(tmp[x] = req->number[x]))
1227 break;
1228 x++;
1229 /* Check for hints */
1230 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1231 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1232 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1233 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1234 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1235 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1236 if (res2) {
1237 if (strlen(tmp) > strlen(req->hmd->exten)) {
1238 /* Update meta data if appropriate */
1239 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1243 res |= res2;
1246 return res;
1249 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1251 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1253 if (!trans->addr.sin_addr.s_addr)
1254 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1255 trans->us_eid = p->us_eid;
1256 trans->them_eid = p->eid;
1257 /* Enable encryption if appropriate */
1258 if (!ast_strlen_zero(p->inkey))
1259 ast_set_flag(trans, FLAG_ENCRYPT);
1260 if (p->maxms) {
1261 trans->autokilltimeout = p->maxms;
1262 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1263 if (p->lastms > 1) {
1264 trans->retranstimer = p->lastms * 2;
1265 /* Keep it from being silly */
1266 if (trans->retranstimer < 150)
1267 trans->retranstimer = 150;
1269 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1270 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1271 } else
1272 trans->autokilltimeout = global_autokilltimeout;
1275 /*! \note Called with the peers list already locked */
1276 static int do_register_expire(const void *data)
1278 struct dundi_peer *peer = (struct dundi_peer *)data;
1279 char eid_str[20];
1280 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1281 peer->registerexpire = -1;
1282 peer->lastms = 0;
1283 memset(&peer->addr, 0, sizeof(peer->addr));
1284 return 0;
1287 static int update_key(struct dundi_peer *peer)
1289 unsigned char key[16];
1290 struct ast_key *ekey, *skey;
1291 char eid_str[20];
1292 int res;
1293 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1294 build_iv(key);
1295 aes_encrypt_key128(key, &peer->us_ecx);
1296 aes_decrypt_key128(key, &peer->us_dcx);
1297 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1298 if (!ekey) {
1299 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1300 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1301 return -1;
1303 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1304 if (!skey) {
1305 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1306 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1307 return -1;
1309 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1310 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1311 return -1;
1313 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1314 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1315 return -1;
1317 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1318 peer->sentfullkey = 0;
1319 /* Looks good */
1320 time(&peer->keyexpire);
1321 peer->keyexpire += dundi_key_ttl;
1323 return 0;
1326 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
1328 unsigned char curblock[16];
1329 int x;
1330 memcpy(curblock, iv, sizeof(curblock));
1331 while(len > 0) {
1332 for (x=0;x<16;x++)
1333 curblock[x] ^= src[x];
1334 aes_encrypt(curblock, dst, ecx);
1335 memcpy(curblock, dst, sizeof(curblock));
1336 dst += 16;
1337 src += 16;
1338 len -= 16;
1340 return 0;
1342 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
1344 unsigned char lastblock[16];
1345 int x;
1346 memcpy(lastblock, iv, sizeof(lastblock));
1347 while(len > 0) {
1348 aes_decrypt(src, dst, dcx);
1349 for (x=0;x<16;x++)
1350 dst[x] ^= lastblock[x];
1351 memcpy(lastblock, src, sizeof(lastblock));
1352 dst += 16;
1353 src += 16;
1354 len -= 16;
1356 return 0;
1359 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)
1361 int space = *dstlen;
1362 unsigned long bytes;
1363 struct dundi_hdr *h;
1364 unsigned char *decrypt_space;
1365 decrypt_space = alloca(srclen);
1366 if (!decrypt_space)
1367 return NULL;
1368 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1369 /* Setup header */
1370 h = (struct dundi_hdr *)dst;
1371 *h = *ohdr;
1372 bytes = space - 6;
1373 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1374 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1375 return NULL;
1377 /* Update length */
1378 *dstlen = bytes + 6;
1379 /* Return new header */
1380 return h;
1383 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1385 unsigned char *compress_space;
1386 int len;
1387 int res;
1388 unsigned long bytes;
1389 struct dundi_ie_data ied;
1390 struct dundi_peer *peer;
1391 unsigned char iv[16];
1392 len = pack->datalen + pack->datalen / 100 + 42;
1393 compress_space = alloca(len);
1394 if (compress_space) {
1395 memset(compress_space, 0, len);
1396 /* We care about everthing save the first 6 bytes of header */
1397 bytes = len;
1398 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1399 if (res != Z_OK) {
1400 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1401 return -1;
1403 memset(&ied, 0, sizeof(ied));
1404 /* Say who we are */
1405 if (!pack->h->iseqno && !pack->h->oseqno) {
1406 /* Need the key in the first copy */
1407 if (!(peer = find_peer(&trans->them_eid)))
1408 return -1;
1409 if (update_key(peer))
1410 return -1;
1411 if (!peer->sentfullkey)
1412 ast_set_flag(trans, FLAG_SENDFULLKEY);
1413 /* Append key data */
1414 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1415 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1416 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1417 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1418 } else {
1419 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1421 /* Setup contexts */
1422 trans->ecx = peer->us_ecx;
1423 trans->dcx = peer->us_dcx;
1425 /* We've sent the full key */
1426 peer->sentfullkey = 1;
1428 /* Build initialization vector */
1429 build_iv(iv);
1430 /* Add the field, rounded up to 16 bytes */
1431 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1432 /* Copy the data */
1433 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1434 ast_log(LOG_NOTICE, "Final packet too large!\n");
1435 return -1;
1437 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1438 ied.pos += ((bytes + 15) / 16) * 16;
1439 /* Reconstruct header */
1440 pack->datalen = sizeof(struct dundi_hdr);
1441 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1442 pack->h->cmdflags = 0;
1443 memcpy(pack->h->ies, ied.buf, ied.pos);
1444 pack->datalen += ied.pos;
1445 return 0;
1447 return -1;
1450 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1452 unsigned char dst[128];
1453 int res;
1454 struct ast_key *key, *skey;
1455 char eid_str[20];
1456 if (option_debug)
1457 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1458 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1459 /* A match */
1460 return 1;
1461 } else if (!newkey || !newsig)
1462 return 0;
1463 if (!memcmp(peer->rxenckey, newkey, 128) &&
1464 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1465 /* By definition, a match */
1466 return 1;
1468 /* Decrypt key */
1469 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1470 if (!key) {
1471 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1472 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1473 return -1;
1476 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1477 if (!skey) {
1478 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1479 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1480 return -1;
1483 /* First check signature */
1484 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1485 if (res)
1486 return 0;
1488 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1489 if (res != 16) {
1490 if (res >= 0)
1491 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1492 return 0;
1494 /* Decrypted, passes signature */
1495 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1496 memcpy(peer->rxenckey, newkey, 128);
1497 memcpy(peer->rxenckey + 128, newsig, 128);
1498 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1499 aes_decrypt_key128(dst, &peer->them_dcx);
1500 aes_encrypt_key128(dst, &peer->them_ecx);
1501 return 1;
1504 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1506 struct permission *cur, *perm;
1508 memcpy(peer_dst, peer_src, sizeof(*peer_dst));
1510 memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1511 memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1513 AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1514 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1515 continue;
1517 perm->allow = cur->allow;
1518 strcpy(perm->name, cur->name);
1520 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1523 AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1524 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1525 continue;
1527 perm->allow = cur->allow;
1528 strcpy(perm->name, cur->name);
1530 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1534 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1536 /* Handle canonical command / response */
1537 int final = hdr->cmdresp & 0x80;
1538 int cmd = hdr->cmdresp & 0x7f;
1539 int x,y,z;
1540 int resp;
1541 int res;
1542 int authpass=0;
1543 unsigned char *bufcpy;
1544 #ifdef LOW_MEMORY
1545 struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
1546 #else
1547 struct dundi_ie_data _ied = {
1548 .pos = 0,
1550 struct dundi_ie_data *ied = &_ied;
1551 #endif
1552 struct dundi_ies ies = {
1553 .eidcount = 0,
1555 struct dundi_peer *peer = NULL;
1556 char eid_str[20];
1557 char eid_str2[20];
1558 int retval = -1;
1560 if (!ied) {
1561 return -1;
1564 if (datalen) {
1565 bufcpy = alloca(datalen);
1566 if (!bufcpy) {
1567 goto return_cleanup;
1569 /* Make a copy for parsing */
1570 memcpy(bufcpy, hdr->ies, datalen);
1571 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1572 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1573 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1574 goto return_cleanup;
1577 switch(cmd) {
1578 case DUNDI_COMMAND_DPDISCOVER:
1579 case DUNDI_COMMAND_EIDQUERY:
1580 case DUNDI_COMMAND_PRECACHERQ:
1581 if (cmd == DUNDI_COMMAND_EIDQUERY)
1582 resp = DUNDI_COMMAND_EIDRESPONSE;
1583 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1584 resp = DUNDI_COMMAND_PRECACHERP;
1585 else
1586 resp = DUNDI_COMMAND_DPRESPONSE;
1587 /* A dialplan or entity discover -- qualify by highest level entity */
1588 peer = find_peer(ies.eids[0]);
1589 if (!peer) {
1590 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1591 dundi_send(trans, resp, 0, 1, ied);
1592 } else {
1593 int hasauth = 0;
1594 trans->us_eid = peer->us_eid;
1595 if (strlen(peer->inkey)) {
1596 hasauth = encrypted;
1597 } else
1598 hasauth = 1;
1599 if (hasauth) {
1600 /* Okay we're authentiated and all, now we check if they're authorized */
1601 if (!ies.called_context)
1602 ies.called_context = "e164";
1603 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1604 res = dundi_answer_entity(trans, &ies, ies.called_context);
1605 } else {
1606 if (ast_strlen_zero(ies.called_number)) {
1607 /* They're not permitted to access that context */
1608 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1609 dundi_send(trans, resp, 0, 1, ied);
1610 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1611 (peer->model & DUNDI_MODEL_INBOUND) &&
1612 has_permission(&peer->permit, ies.called_context)) {
1613 res = dundi_answer_query(trans, &ies, ies.called_context);
1614 if (res < 0) {
1615 /* There is no such dundi context */
1616 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1617 dundi_send(trans, resp, 0, 1, ied);
1619 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1620 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1621 has_permission(&peer->include, ies.called_context)) {
1622 res = dundi_prop_precache(trans, &ies, ies.called_context);
1623 if (res < 0) {
1624 /* There is no such dundi context */
1625 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1626 dundi_send(trans, resp, 0, 1, ied);
1628 } else {
1629 /* They're not permitted to access that context */
1630 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1631 dundi_send(trans, resp, 0, 1, ied);
1634 } else {
1635 /* They're not permitted to access that context */
1636 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1637 dundi_send(trans, resp, 0, 1, ied);
1640 break;
1641 case DUNDI_COMMAND_REGREQ:
1642 /* A register request -- should only have one entity */
1643 peer = find_peer(ies.eids[0]);
1645 /* if the peer is not found and we have a valid 'any_peer' setting */
1646 if (any_peer && peer == any_peer) {
1647 /* copy any_peer into a new peer object */
1648 peer = ast_calloc(1, sizeof(*peer));
1649 if (peer) {
1650 deep_copy_peer(peer, any_peer);
1652 /* set EID to remote EID */
1653 peer->eid = *ies.eids[0];
1655 AST_LIST_LOCK(&peers);
1656 AST_LIST_INSERT_HEAD(&peers, peer, list);
1657 AST_LIST_UNLOCK(&peers);
1661 if (!peer || !peer->dynamic) {
1662 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1663 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1664 } else {
1665 int hasauth = 0;
1666 trans->us_eid = peer->us_eid;
1667 if (!ast_strlen_zero(peer->inkey)) {
1668 hasauth = encrypted;
1669 } else
1670 hasauth = 1;
1671 if (hasauth) {
1672 int expire = default_expiration;
1673 char data[256];
1674 int needqual = 0;
1675 AST_SCHED_DEL(sched, peer->registerexpire);
1676 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1677 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
1678 ntohs(trans->addr.sin_port), expire);
1679 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1680 if (inaddrcmp(&peer->addr, &trans->addr)) {
1681 if (option_verbose > 2) {
1682 ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n",
1683 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1684 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
1686 needqual = 1;
1689 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1690 dundi_ie_append_short(ied, DUNDI_IE_EXPIRATION, default_expiration);
1691 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1692 if (needqual)
1693 qualify_peer(peer, 1);
1696 break;
1697 case DUNDI_COMMAND_DPRESPONSE:
1698 /* A dialplan response, lets see what we got... */
1699 if (ies.cause < 1) {
1700 /* Success of some sort */
1701 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1702 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1703 authpass = encrypted;
1704 } else
1705 authpass = 1;
1706 if (authpass) {
1707 /* Pass back up answers */
1708 if (trans->parent && trans->parent->dr) {
1709 y = trans->parent->respcount;
1710 for (x=0;x<ies.anscount;x++) {
1711 if (trans->parent->respcount < trans->parent->maxcount) {
1712 /* Make sure it's not already there */
1713 for (z=0;z<trans->parent->respcount;z++) {
1714 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1715 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1716 break;
1718 if (z == trans->parent->respcount) {
1719 /* Copy into parent responses */
1720 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1721 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1722 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1723 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1724 if (ies.expiration > 0)
1725 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1726 else
1727 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1728 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1729 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1730 &ies.answers[x]->eid);
1731 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1732 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1733 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1734 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1735 trans->parent->respcount++;
1736 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1737 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1738 /* Update weight if appropriate */
1739 trans->parent->dr[z].weight = ies.answers[x]->weight;
1741 } else
1742 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1743 trans->parent->number, trans->parent->dcontext);
1745 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1746 the cache know if this request was unaffected by our entity list. */
1747 cache_save(&trans->them_eid, trans->parent, y,
1748 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1749 if (ies.hint) {
1750 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1751 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1752 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1753 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1754 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1755 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1756 sizeof(trans->parent->hmd->exten));
1758 } else {
1759 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1762 if (ies.expiration > 0) {
1763 if (trans->parent->expiration > ies.expiration) {
1764 trans->parent->expiration = ies.expiration;
1768 /* Close connection if not final */
1769 if (!final)
1770 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1773 } else {
1774 /* Auth failure, check for data */
1775 if (!final) {
1776 /* Cancel if they didn't already */
1777 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1780 break;
1781 case DUNDI_COMMAND_EIDRESPONSE:
1782 /* A dialplan response, lets see what we got... */
1783 if (ies.cause < 1) {
1784 /* Success of some sort */
1785 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1786 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1787 authpass = encrypted;
1788 } else
1789 authpass = 1;
1790 if (authpass) {
1791 /* Pass back up answers */
1792 if (trans->parent && trans->parent->dei && ies.q_org) {
1793 if (!trans->parent->respcount) {
1794 trans->parent->respcount++;
1795 if (ies.q_dept)
1796 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1797 if (ies.q_org)
1798 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1799 if (ies.q_locality)
1800 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1801 if (ies.q_stateprov)
1802 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1803 if (ies.q_country)
1804 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1805 if (ies.q_email)
1806 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1807 if (ies.q_phone)
1808 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1809 if (ies.q_ipaddr)
1810 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1811 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1812 /* If it's them, update our address */
1813 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
1816 if (ies.hint) {
1817 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1818 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1821 /* Close connection if not final */
1822 if (!final)
1823 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1826 } else {
1827 /* Auth failure, check for data */
1828 if (!final) {
1829 /* Cancel if they didn't already */
1830 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1833 break;
1834 case DUNDI_COMMAND_REGRESPONSE:
1835 /* A dialplan response, lets see what we got... */
1836 if (ies.cause < 1) {
1837 int hasauth;
1838 /* Success of some sort */
1839 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1840 hasauth = encrypted;
1841 } else
1842 hasauth = 1;
1844 if (!hasauth) {
1845 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1846 if (!final) {
1847 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1848 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
1850 } else {
1851 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),
1852 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1853 /* Close connection if not final */
1854 if (!final)
1855 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1857 } else {
1858 /* Auth failure, cancel if they didn't for some reason */
1859 if (!final) {
1860 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1863 break;
1864 case DUNDI_COMMAND_INVALID:
1865 case DUNDI_COMMAND_NULL:
1866 case DUNDI_COMMAND_PRECACHERP:
1867 /* Do nothing special */
1868 if (!final)
1869 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1870 break;
1871 case DUNDI_COMMAND_ENCREJ:
1872 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1873 /* No really, it's over at this point */
1874 if (!final)
1875 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1876 } else {
1877 /* Send with full key */
1878 ast_set_flag(trans, FLAG_SENDFULLKEY);
1879 if (final) {
1880 /* Ooops, we got a final message, start by sending ACK... */
1881 dundi_ack(trans, hdr->cmdresp & 0x80);
1882 trans->aseqno = trans->iseqno;
1883 /* Now, we gotta create a new transaction */
1884 if (!reset_transaction(trans)) {
1885 /* Make sure handle_frame doesn't destroy us */
1886 hdr->cmdresp &= 0x7f;
1887 /* Parse the message we transmitted */
1888 memset(&ies, 0, sizeof(ies));
1889 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1890 /* Reconstruct outgoing encrypted packet */
1891 memset(ied, 0, sizeof(*ied));
1892 dundi_ie_append_eid(ied, DUNDI_IE_EID, &trans->us_eid);
1893 dundi_ie_append_raw(ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1894 dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1895 if (ies.encblock)
1896 dundi_ie_append_encdata(ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1897 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
1898 peer->sentfullkey = 1;
1902 break;
1903 case DUNDI_COMMAND_ENCRYPT:
1904 if (!encrypted) {
1905 /* No nested encryption! */
1906 if ((trans->iseqno == 1) && !trans->oseqno) {
1907 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1908 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1909 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1910 if (!final) {
1911 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1913 break;
1915 apply_peer(trans, peer);
1916 /* Key passed, use new contexts for this session */
1917 trans->ecx = peer->them_ecx;
1918 trans->dcx = peer->them_dcx;
1920 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1921 struct dundi_hdr *dhdr;
1922 unsigned char decoded[MAX_PACKET_SIZE];
1923 int ddatalen;
1924 ddatalen = sizeof(decoded);
1925 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1926 if (dhdr) {
1927 /* Handle decrypted response */
1928 if (dundidebug)
1929 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1930 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1931 /* Carry back final flag */
1932 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1933 break;
1934 } else
1935 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1938 if (!final) {
1939 /* Turn off encryption */
1940 ast_clear_flag(trans, FLAG_ENCRYPT);
1941 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1943 break;
1944 default:
1945 /* Send unknown command if we don't know it, with final flag IFF it's the
1946 first command in the dialog and only if we haven't recieved final notification */
1947 if (!final) {
1948 dundi_ie_append_byte(ied, DUNDI_IE_UNKNOWN, cmd);
1949 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
1953 retval = 0;
1955 return_cleanup:
1956 #ifdef LOW_MEMORY
1957 ast_free(ied);
1958 #endif
1959 return retval;
1962 static void destroy_packet(struct dundi_packet *pack, int needfree);
1963 static void destroy_packets(struct packetlist *p)
1965 struct dundi_packet *pack;
1967 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1968 AST_SCHED_DEL(sched, pack->retransid);
1969 free(pack);
1974 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1976 struct dundi_packet *pack;
1978 /* Ack transmitted packet corresponding to iseqno */
1979 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
1980 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1981 destroy_packet(pack, 0);
1982 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
1983 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1984 destroy_packets(&trans->lasttrans);
1986 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
1987 AST_SCHED_DEL(sched, trans->autokillid);
1988 return 1;
1992 return 0;
1995 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1997 struct dundi_transaction *trans;
1998 trans = find_transaction(h, sin);
1999 if (!trans) {
2000 dundi_reject(h, sin);
2001 return 0;
2003 /* Got a transaction, see where this header fits in */
2004 if (h->oseqno == trans->iseqno) {
2005 /* Just what we were looking for... Anything but ack increments iseqno */
2006 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
2007 /* If final, we're done */
2008 destroy_trans(trans, 0);
2009 return 0;
2011 if (h->cmdresp != DUNDI_COMMAND_ACK) {
2012 trans->oiseqno = trans->iseqno;
2013 trans->iseqno++;
2014 handle_command_response(trans, h, datalen, 0);
2016 if (trans->aseqno != trans->iseqno) {
2017 dundi_ack(trans, h->cmdresp & 0x80);
2018 trans->aseqno = trans->iseqno;
2020 /* Delete any saved last transmissions */
2021 destroy_packets(&trans->lasttrans);
2022 if (h->cmdresp & 0x80) {
2023 /* Final -- destroy now */
2024 destroy_trans(trans, 0);
2026 } else if (h->oseqno == trans->oiseqno) {
2027 /* Last incoming sequence number -- send ACK without processing */
2028 dundi_ack(trans, 0);
2029 } else {
2030 /* Out of window -- simply drop */
2031 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
2033 return 0;
2036 static int socket_read(int *id, int fd, short events, void *cbdata)
2038 struct sockaddr_in sin;
2039 int res;
2040 struct dundi_hdr *h;
2041 char buf[MAX_PACKET_SIZE];
2042 socklen_t len;
2043 len = sizeof(sin);
2044 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
2045 if (res < 0) {
2046 if (errno != ECONNREFUSED)
2047 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2048 return 1;
2050 if (res < sizeof(struct dundi_hdr)) {
2051 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2052 return 1;
2054 buf[res] = '\0';
2055 h = (struct dundi_hdr *)buf;
2056 if (dundidebug)
2057 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2058 AST_LIST_LOCK(&peers);
2059 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2060 AST_LIST_UNLOCK(&peers);
2061 return 1;
2064 static void build_secret(char *secret, int seclen)
2066 unsigned char tmp[16];
2067 char *s;
2068 build_iv(tmp);
2069 secret[0] = '\0';
2070 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2071 /* Eliminate potential bad characters */
2072 while((s = strchr(secret, ';'))) *s = '+';
2073 while((s = strchr(secret, '/'))) *s = '+';
2074 while((s = strchr(secret, ':'))) *s = '+';
2075 while((s = strchr(secret, '@'))) *s = '+';
2079 static void save_secret(const char *newkey, const char *oldkey)
2081 char tmp[256];
2082 if (oldkey)
2083 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2084 else
2085 snprintf(tmp, sizeof(tmp), "%s", newkey);
2086 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2087 ast_db_put(secretpath, "secret", tmp);
2088 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2089 ast_db_put(secretpath, "secretexpiry", tmp);
2092 static void load_password(void)
2094 char *current=NULL;
2095 char *last=NULL;
2096 char tmp[256];
2097 time_t expired;
2099 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2100 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2101 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2102 current = strchr(tmp, ';');
2103 if (!current)
2104 current = tmp;
2105 else {
2106 *current = '\0';
2107 current++;
2109 if ((time(NULL) - expired) < 0) {
2110 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2111 expired = time(NULL) + DUNDI_SECRET_TIME;
2112 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2113 last = current;
2114 current = NULL;
2115 } else {
2116 last = NULL;
2117 current = NULL;
2120 if (current) {
2121 /* Current key is still valid, just setup rotatation properly */
2122 ast_copy_string(cursecret, current, sizeof(cursecret));
2123 rotatetime = expired;
2124 } else {
2125 /* Current key is out of date, rotate or eliminate all together */
2126 build_secret(cursecret, sizeof(cursecret));
2127 save_secret(cursecret, last);
2131 static void check_password(void)
2133 char oldsecret[80];
2134 time_t now;
2136 time(&now);
2137 #if 0
2138 printf("%ld/%ld\n", now, rotatetime);
2139 #endif
2140 if ((now - rotatetime) >= 0) {
2141 /* Time to rotate keys */
2142 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2143 build_secret(cursecret, sizeof(cursecret));
2144 save_secret(cursecret, oldsecret);
2148 static void *network_thread(void *ignore)
2150 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2151 from the network, and queue them for delivery to the channels */
2152 int res;
2153 /* Establish I/O callback for socket read */
2154 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2156 while (!dundi_shutdown) {
2157 res = ast_sched_wait(sched);
2158 if ((res > 1000) || (res < 0))
2159 res = 1000;
2160 res = ast_io_wait(io, res);
2161 if (res >= 0) {
2162 AST_LIST_LOCK(&peers);
2163 ast_sched_runq(sched);
2164 AST_LIST_UNLOCK(&peers);
2166 check_password();
2169 netthreadid = AST_PTHREADT_NULL;
2171 return NULL;
2174 static void *process_precache(void *ign)
2176 struct dundi_precache_queue *qe;
2177 time_t now;
2178 char context[256];
2179 char number[256];
2180 int run;
2182 while (!dundi_shutdown) {
2183 time(&now);
2184 run = 0;
2185 AST_LIST_LOCK(&pcq);
2186 if ((qe = AST_LIST_FIRST(&pcq))) {
2187 if (!qe->expiration) {
2188 /* Gone... Remove... */
2189 AST_LIST_REMOVE_HEAD(&pcq, list);
2190 free(qe);
2191 } else if (qe->expiration < now) {
2192 /* Process this entry */
2193 qe->expiration = 0;
2194 ast_copy_string(context, qe->context, sizeof(context));
2195 ast_copy_string(number, qe->number, sizeof(number));
2196 run = 1;
2199 AST_LIST_UNLOCK(&pcq);
2200 if (run) {
2201 dundi_precache(context, number);
2202 } else
2203 sleep(1);
2206 precachethreadid = AST_PTHREADT_NULL;
2208 return NULL;
2211 static int start_network_thread(void)
2213 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2214 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2215 return 0;
2218 static int dundi_do_debug(int fd, int argc, char *argv[])
2220 if (argc != 2)
2221 return RESULT_SHOWUSAGE;
2222 dundidebug = 1;
2223 ast_cli(fd, "DUNDi Debugging Enabled\n");
2224 return RESULT_SUCCESS;
2227 static int dundi_do_store_history(int fd, int argc, char *argv[])
2229 if (argc != 3)
2230 return RESULT_SHOWUSAGE;
2231 global_storehistory = 1;
2232 ast_cli(fd, "DUNDi History Storage Enabled\n");
2233 return RESULT_SUCCESS;
2236 static int dundi_flush(int fd, int argc, char *argv[])
2238 int stats = 0;
2239 if ((argc < 2) || (argc > 3))
2240 return RESULT_SHOWUSAGE;
2241 if (argc > 2) {
2242 if (!strcasecmp(argv[2], "stats"))
2243 stats = 1;
2244 else
2245 return RESULT_SHOWUSAGE;
2247 if (stats) {
2248 /* Flush statistics */
2249 struct dundi_peer *p;
2250 int x;
2251 AST_LIST_LOCK(&peers);
2252 AST_LIST_TRAVERSE(&peers, p, list) {
2253 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2254 if (p->lookups[x])
2255 free(p->lookups[x]);
2256 p->lookups[x] = NULL;
2257 p->lookuptimes[x] = 0;
2259 p->avgms = 0;
2261 AST_LIST_UNLOCK(&peers);
2262 } else {
2263 ast_db_deltree("dundi/cache", NULL);
2264 ast_cli(fd, "DUNDi Cache Flushed\n");
2266 return RESULT_SUCCESS;
2269 static int dundi_no_debug(int fd, int argc, char *argv[])
2271 if (argc != 3)
2272 return RESULT_SHOWUSAGE;
2273 dundidebug = 0;
2274 ast_cli(fd, "DUNDi Debugging Disabled\n");
2275 return RESULT_SUCCESS;
2278 static int dundi_no_store_history(int fd, int argc, char *argv[])
2280 if (argc != 4)
2281 return RESULT_SHOWUSAGE;
2282 global_storehistory = 0;
2283 ast_cli(fd, "DUNDi History Storage Disabled\n");
2284 return RESULT_SUCCESS;
2287 static char *model2str(int model)
2289 switch(model) {
2290 case DUNDI_MODEL_INBOUND:
2291 return "Inbound";
2292 case DUNDI_MODEL_OUTBOUND:
2293 return "Outbound";
2294 case DUNDI_MODEL_SYMMETRIC:
2295 return "Symmetric";
2296 default:
2297 return "Unknown";
2301 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2303 int which=0, len;
2304 char *ret = NULL;
2305 struct dundi_peer *p;
2306 char eid_str[20];
2308 if (pos != rpos)
2309 return NULL;
2310 AST_LIST_LOCK(&peers);
2311 len = strlen(word);
2312 AST_LIST_TRAVERSE(&peers, p, list) {
2313 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2314 if (!strncasecmp(word, s, len) && ++which > state) {
2315 ret = ast_strdup(s);
2316 break;
2319 AST_LIST_UNLOCK(&peers);
2320 return ret;
2323 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2325 return complete_peer_helper(line, word, pos, state, 3);
2328 static int rescomp(const void *a, const void *b)
2330 const struct dundi_result *resa, *resb;
2331 resa = a;
2332 resb = b;
2333 if (resa->weight < resb->weight)
2334 return -1;
2335 if (resa->weight > resb->weight)
2336 return 1;
2337 return 0;
2340 static void sort_results(struct dundi_result *results, int count)
2342 qsort(results, count, sizeof(results[0]), rescomp);
2345 static int dundi_do_lookup(int fd, int argc, char *argv[])
2347 int res;
2348 char tmp[256];
2349 char fs[80] = "";
2350 char *context;
2351 int x;
2352 int bypass = 0;
2353 struct dundi_result dr[MAX_RESULTS];
2354 struct timeval start;
2355 if ((argc < 3) || (argc > 4))
2356 return RESULT_SHOWUSAGE;
2357 if (argc > 3) {
2358 if (!strcasecmp(argv[3], "bypass"))
2359 bypass=1;
2360 else
2361 return RESULT_SHOWUSAGE;
2363 ast_copy_string(tmp, argv[2], sizeof(tmp));
2364 context = strchr(tmp, '@');
2365 if (context) {
2366 *context = '\0';
2367 context++;
2369 start = ast_tvnow();
2370 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2372 if (res < 0)
2373 ast_cli(fd, "DUNDi lookup returned error.\n");
2374 else if (!res)
2375 ast_cli(fd, "DUNDi lookup returned no results.\n");
2376 else
2377 sort_results(dr, res);
2378 for (x=0;x<res;x++) {
2379 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));
2380 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2382 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2383 return RESULT_SUCCESS;
2386 static int dundi_do_precache(int fd, int argc, char *argv[])
2388 int res;
2389 char tmp[256];
2390 char *context;
2391 struct timeval start;
2392 if ((argc < 3) || (argc > 3))
2393 return RESULT_SHOWUSAGE;
2394 ast_copy_string(tmp, argv[2], sizeof(tmp));
2395 context = strchr(tmp, '@');
2396 if (context) {
2397 *context = '\0';
2398 context++;
2400 start = ast_tvnow();
2401 res = dundi_precache(context, tmp);
2403 if (res < 0)
2404 ast_cli(fd, "DUNDi precache returned error.\n");
2405 else if (!res)
2406 ast_cli(fd, "DUNDi precache returned no error.\n");
2407 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2408 return RESULT_SUCCESS;
2411 static int dundi_do_query(int fd, int argc, char *argv[])
2413 int res;
2414 char tmp[256];
2415 char *context;
2416 dundi_eid eid;
2417 struct dundi_entity_info dei;
2418 if ((argc < 3) || (argc > 3))
2419 return RESULT_SHOWUSAGE;
2420 if (dundi_str_to_eid(&eid, argv[2])) {
2421 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2422 return RESULT_SHOWUSAGE;
2424 ast_copy_string(tmp, argv[2], sizeof(tmp));
2425 context = strchr(tmp, '@');
2426 if (context) {
2427 *context = '\0';
2428 context++;
2430 res = dundi_query_eid(&dei, context, eid);
2431 if (res < 0)
2432 ast_cli(fd, "DUNDi Query EID returned error.\n");
2433 else if (!res)
2434 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2435 else {
2436 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2437 ast_cli(fd, "Department: %s\n", dei.orgunit);
2438 ast_cli(fd, "Organization: %s\n", dei.org);
2439 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2440 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2441 ast_cli(fd, "Country: %s\n", dei.country);
2442 ast_cli(fd, "E-mail: %s\n", dei.email);
2443 ast_cli(fd, "Phone: %s\n", dei.phone);
2444 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2446 return RESULT_SUCCESS;
2449 static int dundi_show_peer(int fd, int argc, char *argv[])
2451 struct dundi_peer *peer;
2452 struct permission *p;
2453 char *order;
2454 char eid_str[20];
2455 int x, cnt;
2457 if (argc != 4)
2458 return RESULT_SHOWUSAGE;
2459 AST_LIST_LOCK(&peers);
2460 AST_LIST_TRAVERSE(&peers, peer, list) {
2461 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2462 break;
2464 if (peer) {
2465 switch(peer->order) {
2466 case 0:
2467 order = "Primary";
2468 break;
2469 case 1:
2470 order = "Secondary";
2471 break;
2472 case 2:
2473 order = "Tertiary";
2474 break;
2475 case 3:
2476 order = "Quartiary";
2477 break;
2478 default:
2479 order = "Unknown";
2481 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2482 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2483 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2484 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2485 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2486 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2487 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2488 if (!AST_LIST_EMPTY(&peer->include))
2489 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2490 AST_LIST_TRAVERSE(&peer->include, p, list)
2491 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2492 if (!AST_LIST_EMPTY(&peer->permit))
2493 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2494 AST_LIST_TRAVERSE(&peer->permit, p, list)
2495 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2496 cnt = 0;
2497 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2498 if (peer->lookups[x]) {
2499 if (!cnt)
2500 ast_cli(fd, "Last few query times:\n");
2501 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2502 cnt++;
2505 if (cnt)
2506 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2507 } else
2508 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2509 AST_LIST_UNLOCK(&peers);
2510 return RESULT_SUCCESS;
2513 static int dundi_show_peers(int fd, int argc, char *argv[])
2515 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2516 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2517 struct dundi_peer *peer;
2518 int registeredonly=0;
2519 char avgms[20];
2520 char eid_str[20];
2521 int online_peers = 0;
2522 int offline_peers = 0;
2523 int unmonitored_peers = 0;
2524 int total_peers = 0;
2526 if ((argc != 3) && (argc != 4) && (argc != 5))
2527 return RESULT_SHOWUSAGE;
2528 if ((argc == 4)) {
2529 if (!strcasecmp(argv[3], "registered")) {
2530 registeredonly = 1;
2531 } else
2532 return RESULT_SHOWUSAGE;
2534 AST_LIST_LOCK(&peers);
2535 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2536 AST_LIST_TRAVERSE(&peers, peer, list) {
2537 char status[20];
2538 int print_line = -1;
2539 char srch[2000];
2540 total_peers++;
2541 if (registeredonly && !peer->addr.sin_addr.s_addr)
2542 continue;
2543 if (peer->maxms) {
2544 if (peer->lastms < 0) {
2545 strcpy(status, "UNREACHABLE");
2546 offline_peers++;
2548 else if (peer->lastms > peer->maxms) {
2549 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2550 offline_peers++;
2552 else if (peer->lastms) {
2553 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2554 online_peers++;
2556 else {
2557 strcpy(status, "UNKNOWN");
2558 offline_peers++;
2560 } else {
2561 strcpy(status, "Unmonitored");
2562 unmonitored_peers++;
2564 if (peer->avgms)
2565 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2566 else
2567 strcpy(avgms, "Unavail");
2568 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2569 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2570 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2572 if (argc == 5) {
2573 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2574 print_line = -1;
2575 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2576 print_line = 1;
2577 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2578 print_line = -1;
2579 } else {
2580 print_line = 0;
2584 if (print_line) {
2585 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2586 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2587 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2590 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2591 AST_LIST_UNLOCK(&peers);
2592 return RESULT_SUCCESS;
2593 #undef FORMAT
2594 #undef FORMAT2
2597 static int dundi_show_trans(int fd, int argc, char *argv[])
2599 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2600 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2601 struct dundi_transaction *trans;
2602 if (argc != 3)
2603 return RESULT_SHOWUSAGE;
2604 AST_LIST_LOCK(&peers);
2605 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2606 AST_LIST_TRAVERSE(&alltrans, trans, all) {
2607 ast_cli(fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
2608 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2610 AST_LIST_UNLOCK(&peers);
2611 return RESULT_SUCCESS;
2612 #undef FORMAT
2613 #undef FORMAT2
2616 static int dundi_show_entityid(int fd, int argc, char *argv[])
2618 char eid_str[20];
2619 if (argc != 3)
2620 return RESULT_SHOWUSAGE;
2621 AST_LIST_LOCK(&peers);
2622 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2623 AST_LIST_UNLOCK(&peers);
2624 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2625 return RESULT_SUCCESS;
2628 static int dundi_show_requests(int fd, int argc, char *argv[])
2630 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2631 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2632 struct dundi_request *req;
2633 char eidstr[20];
2634 if (argc != 3)
2635 return RESULT_SHOWUSAGE;
2636 AST_LIST_LOCK(&peers);
2637 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2638 AST_LIST_TRAVERSE(&requests, req, list) {
2639 ast_cli(fd, FORMAT, req->number, req->dcontext,
2640 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2642 AST_LIST_UNLOCK(&peers);
2643 return RESULT_SUCCESS;
2644 #undef FORMAT
2645 #undef FORMAT2
2648 /* Grok-a-dial DUNDi */
2650 static int dundi_show_mappings(int fd, int argc, char *argv[])
2652 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2653 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2654 struct dundi_mapping *map;
2655 char fs[256];
2656 if (argc != 3)
2657 return RESULT_SHOWUSAGE;
2658 AST_LIST_LOCK(&peers);
2659 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2660 AST_LIST_TRAVERSE(&mappings, map, list) {
2661 ast_cli(fd, FORMAT, map->dcontext, map->weight,
2662 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2663 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2665 AST_LIST_UNLOCK(&peers);
2666 return RESULT_SUCCESS;
2667 #undef FORMAT
2668 #undef FORMAT2
2671 static int dundi_show_precache(int fd, int argc, char *argv[])
2673 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2674 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2675 struct dundi_precache_queue *qe;
2676 int h,m,s;
2677 time_t now;
2679 if (argc != 3)
2680 return RESULT_SHOWUSAGE;
2681 time(&now);
2682 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2683 AST_LIST_LOCK(&pcq);
2684 AST_LIST_TRAVERSE(&pcq, qe, list) {
2685 s = qe->expiration - now;
2686 h = s / 3600;
2687 s = s % 3600;
2688 m = s / 60;
2689 s = s % 60;
2690 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2692 AST_LIST_UNLOCK(&pcq);
2694 return RESULT_SUCCESS;
2695 #undef FORMAT
2696 #undef FORMAT2
2699 static char debug_usage[] =
2700 "Usage: dundi debug\n"
2701 " Enables dumping of DUNDi packets for debugging purposes\n";
2703 static char no_debug_usage[] =
2704 "Usage: dundi no debug\n"
2705 " Disables dumping of DUNDi packets for debugging purposes\n";
2707 static char store_history_usage[] =
2708 "Usage: dundi store history\n"
2709 " Enables storing of DUNDi requests and times for debugging\n"
2710 "purposes\n";
2712 static char no_store_history_usage[] =
2713 "Usage: dundi no store history\n"
2714 " Disables storing of DUNDi requests and times for debugging\n"
2715 "purposes\n";
2717 static char show_peers_usage[] =
2718 "Usage: dundi show peers\n"
2719 " Lists all known DUNDi peers.\n";
2721 static char show_trans_usage[] =
2722 "Usage: dundi show trans\n"
2723 " Lists all known DUNDi transactions.\n";
2725 static char show_mappings_usage[] =
2726 "Usage: dundi show mappings\n"
2727 " Lists all known DUNDi mappings.\n";
2729 static char show_precache_usage[] =
2730 "Usage: dundi show precache\n"
2731 " Lists all known DUNDi scheduled precache updates.\n";
2733 static char show_entityid_usage[] =
2734 "Usage: dundi show entityid\n"
2735 " Displays the global entityid for this host.\n";
2737 static char show_peer_usage[] =
2738 "Usage: dundi show peer [peer]\n"
2739 " Provide a detailed description of a specifid DUNDi peer.\n";
2741 static char show_requests_usage[] =
2742 "Usage: dundi show requests\n"
2743 " Lists all known pending DUNDi requests.\n";
2745 static char lookup_usage[] =
2746 "Usage: dundi lookup <number>[@context] [bypass]\n"
2747 " Lookup the given number within the given DUNDi context\n"
2748 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2749 "keyword is specified.\n";
2751 static char precache_usage[] =
2752 "Usage: dundi precache <number>[@context]\n"
2753 " Lookup the given number within the given DUNDi context\n"
2754 "(or e164 if none is specified) and precaches the results to any\n"
2755 "upstream DUNDi push servers.\n";
2757 static char query_usage[] =
2758 "Usage: dundi query <entity>[@context]\n"
2759 " Attempts to retrieve contact information for a specific\n"
2760 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2761 "e164 if none is specified).\n";
2763 static char flush_usage[] =
2764 "Usage: dundi flush [stats]\n"
2765 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2766 "'stats' is present, clears timer statistics instead of normal\n"
2767 "operation.\n";
2769 static struct ast_cli_entry cli_dundi[] = {
2770 { { "dundi", "debug", NULL },
2771 dundi_do_debug, "Enable DUNDi debugging",
2772 debug_usage },
2774 { { "dundi", "store", "history", NULL },
2775 dundi_do_store_history, "Enable DUNDi historic records",
2776 store_history_usage },
2778 { { "dundi", "no", "store", "history", NULL },
2779 dundi_no_store_history, "Disable DUNDi historic records",
2780 no_store_history_usage },
2782 { { "dundi", "flush", NULL },
2783 dundi_flush, "Flush DUNDi cache",
2784 flush_usage },
2786 { { "dundi", "no", "debug", NULL },
2787 dundi_no_debug, "Disable DUNDi debugging",
2788 no_debug_usage },
2790 { { "dundi", "show", "peers", NULL },
2791 dundi_show_peers, "Show defined DUNDi peers",
2792 show_peers_usage },
2794 { { "dundi", "show", "trans", NULL },
2795 dundi_show_trans, "Show active DUNDi transactions",
2796 show_trans_usage },
2798 { { "dundi", "show", "entityid", NULL },
2799 dundi_show_entityid, "Display Global Entity ID",
2800 show_entityid_usage },
2802 { { "dundi", "show", "mappings", NULL },
2803 dundi_show_mappings, "Show DUNDi mappings",
2804 show_mappings_usage },
2806 { { "dundi", "show", "precache", NULL },
2807 dundi_show_precache, "Show DUNDi precache",
2808 show_precache_usage },
2810 { { "dundi", "show", "requests", NULL },
2811 dundi_show_requests, "Show DUNDi requests",
2812 show_requests_usage },
2814 { { "dundi", "show", "peer", NULL },
2815 dundi_show_peer, "Show info on a specific DUNDi peer",
2816 show_peer_usage, complete_peer_4 },
2818 { { "dundi", "lookup", NULL },
2819 dundi_do_lookup, "Lookup a number in DUNDi",
2820 lookup_usage },
2822 { { "dundi", "precache", NULL },
2823 dundi_do_precache, "Precache a number in DUNDi",
2824 precache_usage },
2826 { { "dundi", "query", NULL },
2827 dundi_do_query, "Query a DUNDi EID",
2828 query_usage },
2831 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2833 struct dundi_transaction *trans;
2834 int tid;
2836 /* Don't allow creation of transactions to non-registered peers */
2837 if (p && !p->addr.sin_addr.s_addr)
2838 return NULL;
2839 tid = get_trans_id();
2840 if (tid < 1)
2841 return NULL;
2842 trans = ast_calloc(1, sizeof(*trans));
2843 if (trans) {
2844 if (global_storehistory) {
2845 trans->start = ast_tvnow();
2846 ast_set_flag(trans, FLAG_STOREHIST);
2848 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2849 trans->autokillid = -1;
2850 if (p) {
2851 apply_peer(trans, p);
2852 if (!p->sentfullkey)
2853 ast_set_flag(trans, FLAG_SENDFULLKEY);
2855 trans->strans = tid;
2856 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
2858 return trans;
2861 static int dundi_xmit(struct dundi_packet *pack)
2863 int res;
2864 if (dundidebug)
2865 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2866 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2867 if (res < 0) {
2868 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2869 ast_inet_ntoa(pack->parent->addr.sin_addr),
2870 ntohs(pack->parent->addr.sin_port), strerror(errno));
2872 if (res > 0)
2873 res = 0;
2874 return res;
2877 static void destroy_packet(struct dundi_packet *pack, int needfree)
2879 if (pack->parent)
2880 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
2881 AST_SCHED_DEL(sched, pack->retransid);
2882 if (needfree)
2883 free(pack);
2886 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2888 struct dundi_peer *peer;
2889 int ms;
2890 int x;
2891 int cnt;
2892 char eid_str[20];
2893 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2894 AST_LIST_TRAVERSE(&peers, peer, list) {
2895 if (peer->regtrans == trans)
2896 peer->regtrans = NULL;
2897 if (peer->qualtrans == trans) {
2898 if (fromtimeout) {
2899 if (peer->lastms > -1)
2900 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2901 peer->lastms = -1;
2902 } else {
2903 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2904 if (ms < 1)
2905 ms = 1;
2906 if (ms < peer->maxms) {
2907 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2908 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2909 } else if (peer->lastms < peer->maxms) {
2910 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);
2912 peer->lastms = ms;
2914 peer->qualtrans = NULL;
2916 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2917 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2918 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2919 peer->avgms = 0;
2920 cnt = 0;
2921 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2922 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2923 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2924 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2925 peer->lookups[x] = peer->lookups[x-1];
2926 if (peer->lookups[x]) {
2927 peer->avgms += peer->lookuptimes[x];
2928 cnt++;
2931 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2932 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2933 if (peer->lookups[0]) {
2934 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2935 peer->avgms += peer->lookuptimes[0];
2936 cnt++;
2938 if (cnt)
2939 peer->avgms /= cnt;
2945 if (trans->parent) {
2946 /* Unlink from parent if appropriate */
2947 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
2948 if (AST_LIST_EMPTY(&trans->parent->trans)) {
2949 /* Wake up sleeper */
2950 if (trans->parent->pfds[1] > -1) {
2951 write(trans->parent->pfds[1], "killa!", 6);
2955 /* Unlink from all trans */
2956 AST_LIST_REMOVE(&alltrans, trans, all);
2957 destroy_packets(&trans->packets);
2958 destroy_packets(&trans->lasttrans);
2959 AST_SCHED_DEL(sched, trans->autokillid);
2960 if (trans->thread) {
2961 /* If used by a thread, mark as dead and be done */
2962 ast_set_flag(trans, FLAG_DEAD);
2963 } else
2964 free(trans);
2967 static int dundi_rexmit(const void *data)
2969 struct dundi_packet *pack = (struct dundi_packet *)data;
2970 int res;
2971 AST_LIST_LOCK(&peers);
2972 if (pack->retrans < 1) {
2973 pack->retransid = -1;
2974 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
2975 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
2976 ast_inet_ntoa(pack->parent->addr.sin_addr),
2977 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2978 destroy_trans(pack->parent, 1);
2979 res = 0;
2980 } else {
2981 /* Decrement retransmission, try again */
2982 pack->retrans--;
2983 dundi_xmit(pack);
2984 res = 1;
2986 AST_LIST_UNLOCK(&peers);
2987 return res;
2990 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2992 struct dundi_packet *pack;
2993 int res;
2994 int len;
2995 char eid_str[20];
2996 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2997 /* Reserve enough space for encryption */
2998 if (ast_test_flag(trans, FLAG_ENCRYPT))
2999 len += 384;
3000 pack = ast_calloc(1, len);
3001 if (pack) {
3002 pack->h = (struct dundi_hdr *)(pack->data);
3003 if (cmdresp != DUNDI_COMMAND_ACK) {
3004 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
3005 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
3006 AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
3008 pack->parent = trans;
3009 pack->h->strans = htons(trans->strans);
3010 pack->h->dtrans = htons(trans->dtrans);
3011 pack->h->iseqno = trans->iseqno;
3012 pack->h->oseqno = trans->oseqno;
3013 pack->h->cmdresp = cmdresp;
3014 pack->datalen = sizeof(struct dundi_hdr);
3015 if (ied) {
3016 memcpy(pack->h->ies, ied->buf, ied->pos);
3017 pack->datalen += ied->pos;
3019 if (final) {
3020 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3021 ast_set_flag(trans, FLAG_FINAL);
3023 pack->h->cmdflags = flags;
3024 if (cmdresp != DUNDI_COMMAND_ACK) {
3025 trans->oseqno++;
3026 trans->oseqno = trans->oseqno % 256;
3028 trans->aseqno = trans->iseqno;
3029 /* If we have their public key, encrypt */
3030 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3031 switch(cmdresp) {
3032 case DUNDI_COMMAND_REGREQ:
3033 case DUNDI_COMMAND_REGRESPONSE:
3034 case DUNDI_COMMAND_DPDISCOVER:
3035 case DUNDI_COMMAND_DPRESPONSE:
3036 case DUNDI_COMMAND_EIDQUERY:
3037 case DUNDI_COMMAND_EIDRESPONSE:
3038 case DUNDI_COMMAND_PRECACHERQ:
3039 case DUNDI_COMMAND_PRECACHERP:
3040 if (dundidebug)
3041 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3042 res = dundi_encrypt(trans, pack);
3043 break;
3044 default:
3045 res = 0;
3047 } else
3048 res = 0;
3049 if (!res)
3050 res = dundi_xmit(pack);
3051 if (res)
3052 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3054 if (cmdresp == DUNDI_COMMAND_ACK)
3055 free(pack);
3056 return res;
3058 return -1;
3061 static int do_autokill(const void *data)
3063 struct dundi_transaction *trans = (struct dundi_transaction *)data;
3064 char eid_str[20];
3065 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3066 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3067 trans->autokillid = -1;
3068 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3069 return 0;
3072 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3074 struct dundi_peer *p;
3075 if (!dundi_eid_cmp(eid, us)) {
3076 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3077 return;
3079 AST_LIST_LOCK(&peers);
3080 AST_LIST_TRAVERSE(&peers, p, list) {
3081 if (!dundi_eid_cmp(&p->eid, eid)) {
3082 if (has_permission(&p->include, context))
3083 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3084 else
3085 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3086 break;
3089 if (!p)
3090 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3091 AST_LIST_UNLOCK(&peers);
3094 static int dundi_discover(struct dundi_transaction *trans)
3096 struct dundi_ie_data ied;
3097 int x;
3098 if (!trans->parent) {
3099 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3100 return -1;
3102 memset(&ied, 0, sizeof(ied));
3103 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3104 if (!dundi_eid_zero(&trans->us_eid))
3105 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3106 for (x=0;x<trans->eidcount;x++)
3107 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3108 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3109 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3110 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3111 if (trans->parent->cbypass)
3112 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3113 if (trans->autokilltimeout)
3114 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3115 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3118 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3120 struct dundi_ie_data ied;
3121 int x, res;
3122 int max = 999999;
3123 int expiration = dundi_cache_time;
3124 int ouranswers=0;
3125 dundi_eid *avoid[1] = { NULL, };
3126 int direct[1] = { 0, };
3127 struct dundi_result dr[MAX_RESULTS];
3128 struct dundi_hint_metadata hmd;
3129 if (!trans->parent) {
3130 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3131 return -1;
3133 memset(&hmd, 0, sizeof(hmd));
3134 memset(&dr, 0, sizeof(dr));
3135 /* Look up the answers we're going to include */
3136 for (x=0;x<mapcount;x++)
3137 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3138 if (ouranswers < 0)
3139 ouranswers = 0;
3140 for (x=0;x<ouranswers;x++) {
3141 if (dr[x].weight < max)
3142 max = dr[x].weight;
3144 if (max) {
3145 /* If we do not have a canonical result, keep looking */
3146 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
3147 if (res > 0) {
3148 /* Append answer in result */
3149 ouranswers += res;
3153 if (ouranswers > 0) {
3154 *foundanswers += ouranswers;
3155 memset(&ied, 0, sizeof(ied));
3156 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3157 if (!dundi_eid_zero(&trans->us_eid))
3158 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3159 for (x=0;x<trans->eidcount;x++)
3160 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3161 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3162 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3163 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3164 for (x=0;x<ouranswers;x++) {
3165 /* Add answers */
3166 if (dr[x].expiration && (expiration > dr[x].expiration))
3167 expiration = dr[x].expiration;
3168 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3170 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3171 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3172 if (trans->autokilltimeout)
3173 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3174 if (expiration < *minexp)
3175 *minexp = expiration;
3176 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3177 } else {
3178 /* Oops, nothing to send... */
3179 destroy_trans(trans, 0);
3180 return 0;
3184 static int dundi_query(struct dundi_transaction *trans)
3186 struct dundi_ie_data ied;
3187 int x;
3188 if (!trans->parent) {
3189 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3190 return -1;
3192 memset(&ied, 0, sizeof(ied));
3193 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3194 if (!dundi_eid_zero(&trans->us_eid))
3195 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3196 for (x=0;x<trans->eidcount;x++)
3197 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3198 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3199 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3200 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3201 if (trans->autokilltimeout)
3202 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3203 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3206 static int discover_transactions(struct dundi_request *dr)
3208 struct dundi_transaction *trans;
3209 AST_LIST_LOCK(&peers);
3210 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3211 dundi_discover(trans);
3213 AST_LIST_UNLOCK(&peers);
3214 return 0;
3217 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3219 struct dundi_transaction *trans;
3221 /* Mark all as "in thread" so they don't disappear */
3222 AST_LIST_LOCK(&peers);
3223 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3224 if (trans->thread)
3225 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3226 trans->thread = 1;
3228 AST_LIST_UNLOCK(&peers);
3230 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3231 if (!ast_test_flag(trans, FLAG_DEAD))
3232 precache_trans(trans, maps, mapcount, expiration, foundanswers);
3235 /* Cleanup any that got destroyed in the mean time */
3236 AST_LIST_LOCK(&peers);
3237 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
3238 trans->thread = 0;
3239 if (ast_test_flag(trans, FLAG_DEAD)) {
3240 ast_log(LOG_DEBUG, "Our transaction went away!\n");
3241 /* This is going to remove the transaction from the dundi_request's list, as well
3242 * as the global transactions list */
3243 destroy_trans(trans, 0);
3246 AST_LIST_TRAVERSE_SAFE_END
3247 AST_LIST_UNLOCK(&peers);
3249 return 0;
3252 static int query_transactions(struct dundi_request *dr)
3254 struct dundi_transaction *trans;
3256 AST_LIST_LOCK(&peers);
3257 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3258 dundi_query(trans);
3260 AST_LIST_UNLOCK(&peers);
3262 return 0;
3265 static int optimize_transactions(struct dundi_request *dr, int order)
3267 /* Minimize the message propagation through DUNDi by
3268 alerting the network to hops which should be not be considered */
3269 struct dundi_transaction *trans;
3270 struct dundi_peer *peer;
3271 dundi_eid tmp;
3272 int x;
3273 int needpush;
3275 AST_LIST_LOCK(&peers);
3276 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3277 /* Pop off the true root */
3278 if (trans->eidcount) {
3279 tmp = trans->eids[--trans->eidcount];
3280 needpush = 1;
3281 } else {
3282 tmp = trans->us_eid;
3283 needpush = 0;
3286 AST_LIST_TRAVERSE(&peers, peer, list) {
3287 if (has_permission(&peer->include, dr->dcontext) &&
3288 dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
3289 (peer->order <= order)) {
3290 /* For each other transaction, make sure we don't
3291 ask this EID about the others if they're not
3292 already in the list */
3293 if (!dundi_eid_cmp(&tmp, &peer->eid))
3294 x = -1;
3295 else {
3296 for (x=0;x<trans->eidcount;x++) {
3297 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
3298 break;
3301 if (x == trans->eidcount) {
3302 /* Nope not in the list, if needed, add us at the end since we're the source */
3303 if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3304 trans->eids[trans->eidcount++] = peer->eid;
3305 /* Need to insert the real root (or us) at the bottom now as
3306 a requirement now. */
3307 needpush = 1;
3312 /* If necessary, push the true root back on the end */
3313 if (needpush)
3314 trans->eids[trans->eidcount++] = tmp;
3316 AST_LIST_UNLOCK(&peers);
3318 return 0;
3321 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3323 struct dundi_transaction *trans;
3324 int x;
3325 char eid_str[20];
3326 char eid_str2[20];
3328 /* Ignore if not registered */
3329 if (!p->addr.sin_addr.s_addr)
3330 return 0;
3331 if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3332 return 0;
3333 if (ast_strlen_zero(dr->number))
3334 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
3335 else
3336 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
3337 trans = create_transaction(p);
3338 if (!trans)
3339 return -1;
3340 trans->parent = dr;
3341 trans->ttl = ttl;
3342 for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
3343 trans->eids[x] = *avoid[x];
3344 trans->eidcount = x;
3345 AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
3347 return 0;
3350 static void cancel_request(struct dundi_request *dr)
3352 struct dundi_transaction *trans;
3354 AST_LIST_LOCK(&peers);
3355 while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
3356 /* Orphan transaction from request */
3357 trans->parent = NULL;
3358 /* Send final cancel */
3359 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3361 AST_LIST_UNLOCK(&peers);
3364 static void abort_request(struct dundi_request *dr)
3366 struct dundi_transaction *trans;
3368 AST_LIST_LOCK(&peers);
3369 while ((trans = AST_LIST_FIRST(&dr->trans))) {
3370 /* This will remove the transaction from the list */
3371 destroy_trans(trans, 0);
3373 AST_LIST_UNLOCK(&peers);
3376 static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[])
3378 struct dundi_peer *p;
3379 int x;
3380 int res;
3381 int pass;
3382 int allowconnect;
3383 char eid_str[20];
3384 AST_LIST_LOCK(&peers);
3385 AST_LIST_TRAVERSE(&peers, p, list) {
3386 if (modeselect == 1) {
3387 /* Send the precache to push upstreams only! */
3388 pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
3389 allowconnect = 1;
3390 } else {
3391 /* Normal lookup / EID query */
3392 pass = has_permission(&p->include, dr->dcontext);
3393 allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
3395 if (skip) {
3396 if (!dundi_eid_cmp(skip, &p->eid))
3397 pass = 0;
3399 if (pass) {
3400 if (p->order <= order) {
3401 /* Check order first, then check cache, regardless of
3402 omissions, this gets us more likely to not have an
3403 affected answer. */
3404 if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
3405 res = 0;
3406 /* Make sure we haven't already seen it and that it won't
3407 affect our answer */
3408 for (x=0;avoid[x];x++) {
3409 if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
3410 /* If not a direct connection, it affects our answer */
3411 if (directs && !directs[x])
3412 ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED);
3413 break;
3416 /* Make sure we can ask */
3417 if (allowconnect) {
3418 if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
3419 /* Check for a matching or 0 cache entry */
3420 append_transaction(dr, p, ttl, avoid);
3421 } else
3422 ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
3425 *foundcache |= res;
3426 } else if (!*skipped || (p->order < *skipped))
3427 *skipped = p->order;
3430 AST_LIST_UNLOCK(&peers);
3433 static int register_request(struct dundi_request *dr, struct dundi_request **pending)
3435 struct dundi_request *cur;
3436 int res=0;
3437 char eid_str[20];
3438 AST_LIST_LOCK(&peers);
3439 AST_LIST_TRAVERSE(&requests, cur, list) {
3440 if (option_debug)
3441 ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
3442 dr->dcontext, dr->number);
3443 if (!strcasecmp(cur->dcontext, dr->dcontext) &&
3444 !strcasecmp(cur->number, dr->number) &&
3445 (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
3446 ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n",
3447 cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
3448 *pending = cur;
3449 res = 1;
3450 break;
3453 if (!res) {
3454 ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n",
3455 dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
3456 /* Go ahead and link us in since nobody else is searching for this */
3457 AST_LIST_INSERT_HEAD(&requests, dr, list);
3458 *pending = NULL;
3460 AST_LIST_UNLOCK(&peers);
3461 return res;
3464 static void unregister_request(struct dundi_request *dr)
3466 AST_LIST_LOCK(&peers);
3467 AST_LIST_REMOVE(&requests, dr, list);
3468 AST_LIST_UNLOCK(&peers);
3471 static int check_request(struct dundi_request *dr)
3473 struct dundi_request *cur;
3475 AST_LIST_LOCK(&peers);
3476 AST_LIST_TRAVERSE(&requests, cur, list) {
3477 if (cur == dr)
3478 break;
3480 AST_LIST_UNLOCK(&peers);
3482 return cur ? 1 : 0;
3485 static unsigned long avoid_crc32(dundi_eid *avoid[])
3487 /* Idea is that we're calculating a checksum which is independent of
3488 the order that the EID's are listed in */
3489 unsigned long acrc32 = 0;
3490 int x;
3491 for (x=0;avoid[x];x++) {
3492 /* Order doesn't matter */
3493 if (avoid[x+1]) {
3494 acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
3497 return acrc32;
3500 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 *hmd, int *expiration, int cbypass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[])
3502 int res;
3503 struct dundi_request dr, *pending;
3504 dundi_eid *rooteid=NULL;
3505 int x;
3506 int ttlms;
3507 int ms;
3508 int foundcache;
3509 int skipped=0;
3510 int order=0;
3511 char eid_str[20];
3512 struct timeval start;
3514 /* Don't do anthing for a hungup channel */
3515 if (chan && chan->_softhangup)
3516 return 0;
3518 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3520 for (x=0;avoid[x];x++)
3521 rooteid = avoid[x];
3522 /* Now perform real check */
3523 memset(&dr, 0, sizeof(dr));
3524 if (pipe(dr.pfds)) {
3525 ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
3526 return -1;
3528 dr.dr = result;
3529 dr.hmd = hmd;
3530 dr.maxcount = maxret;
3531 dr.expiration = *expiration;
3532 dr.cbypass = cbypass;
3533 dr.crc32 = avoid_crc32(avoid);
3534 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
3535 ast_copy_string(dr.number, number, sizeof(dr.number));
3536 if (rooteid)
3537 dr.root_eid = *rooteid;
3538 res = register_request(&dr, &pending);
3539 if (res) {
3540 /* Already a request */
3541 if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
3542 /* This is on behalf of someone else. Go ahead and close this out since
3543 they'll get their answer anyway. */
3544 ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
3545 dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
3546 close(dr.pfds[0]);
3547 close(dr.pfds[1]);
3548 return -2;
3549 } else {
3550 /* Wait for the cache to populate */
3551 ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
3552 dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
3553 start = ast_tvnow();
3554 while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) {
3555 /* XXX Would be nice to have a way to poll/select here XXX */
3556 /* XXX this is a busy wait loop!!! */
3557 usleep(1);
3559 /* Continue on as normal, our cache should kick in */
3562 /* Create transactions */
3563 do {
3564 order = skipped;
3565 skipped = 0;
3566 foundcache = 0;
3567 build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
3568 } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
3569 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3570 do this earlier because we didn't know if we were going to have transactions
3571 or not. */
3572 if (!ttl) {
3573 ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
3574 abort_request(&dr);
3575 unregister_request(&dr);
3576 close(dr.pfds[0]);
3577 close(dr.pfds[1]);
3578 return 0;
3581 /* Optimize transactions */
3582 optimize_transactions(&dr, order);
3583 /* Actually perform transactions */
3584 discover_transactions(&dr);
3585 /* Wait for transaction to come back */
3586 start = ast_tvnow();
3587 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) {
3588 ms = 100;
3589 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
3591 if (chan && chan->_softhangup)
3592 ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
3593 cancel_request(&dr);
3594 unregister_request(&dr);
3595 res = dr.respcount;
3596 *expiration = dr.expiration;
3597 close(dr.pfds[0]);
3598 close(dr.pfds[1]);
3599 return res;
3602 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
3604 struct dundi_hint_metadata hmd;
3605 dundi_eid *avoid[1] = { NULL, };
3606 int direct[1] = { 0, };
3607 int expiration = dundi_cache_time;
3608 memset(&hmd, 0, sizeof(hmd));
3609 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
3610 return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
3613 static void reschedule_precache(const char *number, const char *context, int expiration)
3615 int len;
3616 struct dundi_precache_queue *qe, *prev;
3618 AST_LIST_LOCK(&pcq);
3619 AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq, qe, list) {
3620 if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
3621 AST_LIST_REMOVE_CURRENT(&pcq, list);
3622 break;
3625 AST_LIST_TRAVERSE_SAFE_END
3626 if (!qe) {
3627 len = sizeof(*qe);
3628 len += strlen(number) + 1;
3629 len += strlen(context) + 1;
3630 if (!(qe = ast_calloc(1, len))) {
3631 AST_LIST_UNLOCK(&pcq);
3632 return;
3634 strcpy(qe->number, number);
3635 qe->context = qe->number + strlen(number) + 1;
3636 strcpy(qe->context, context);
3638 time(&qe->expiration);
3639 qe->expiration += expiration;
3640 if ((prev = AST_LIST_FIRST(&pcq))) {
3641 while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
3642 prev = AST_LIST_NEXT(prev, list);
3643 AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
3644 } else
3645 AST_LIST_INSERT_HEAD(&pcq, qe, list);
3646 AST_LIST_UNLOCK(&pcq);
3649 static void dundi_precache_full(void)
3651 struct dundi_mapping *cur;
3652 struct ast_context *con;
3653 struct ast_exten *e;
3655 AST_LIST_TRAVERSE(&mappings, cur, list) {
3656 ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
3657 ast_rdlock_contexts();
3658 con = ast_walk_contexts(NULL);
3659 while (con) {
3660 if (!strcasecmp(cur->lcontext, ast_get_context_name(con))) {
3661 /* Found the match, now queue them all up */
3662 ast_lock_context(con);
3663 e = ast_walk_context_extensions(con, NULL);
3664 while (e) {
3665 reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
3666 e = ast_walk_context_extensions(con, e);
3668 ast_unlock_context(con);
3670 con = ast_walk_contexts(con);
3672 ast_unlock_contexts();
3676 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
3678 struct dundi_request dr;
3679 struct dundi_hint_metadata hmd;
3680 struct dundi_result dr2[MAX_RESULTS];
3681 struct timeval start;
3682 struct dundi_mapping *maps = NULL, *cur;
3683 int nummaps = 0;
3684 int foundanswers;
3685 int foundcache, skipped, ttlms, ms;
3686 if (!context)
3687 context = "e164";
3688 ast_log(LOG_DEBUG, "Precache internal (%s@%s)!\n", number, context);
3690 AST_LIST_LOCK(&peers);
3691 AST_LIST_TRAVERSE(&mappings, cur, list) {
3692 if (!strcasecmp(cur->dcontext, context))
3693 nummaps++;
3695 if (nummaps) {
3696 maps = alloca(nummaps * sizeof(*maps));
3697 nummaps = 0;
3698 if (maps) {
3699 AST_LIST_TRAVERSE(&mappings, cur, list) {
3700 if (!strcasecmp(cur->dcontext, context))
3701 maps[nummaps++] = *cur;
3705 AST_LIST_UNLOCK(&peers);
3706 if (!nummaps || !maps)
3707 return -1;
3708 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3709 memset(&dr2, 0, sizeof(dr2));
3710 memset(&dr, 0, sizeof(dr));
3711 memset(&hmd, 0, sizeof(hmd));
3712 dr.dr = dr2;
3713 ast_copy_string(dr.number, number, sizeof(dr.number));
3714 ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
3715 dr.maxcount = MAX_RESULTS;
3716 dr.expiration = dundi_cache_time;
3717 dr.hmd = &hmd;
3718 dr.pfds[0] = dr.pfds[1] = -1;
3719 pipe(dr.pfds);
3720 build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
3721 optimize_transactions(&dr, 0);
3722 foundanswers = 0;
3723 precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
3724 if (foundanswers) {
3725 if (dr.expiration > 0)
3726 reschedule_precache(dr.number, dr.dcontext, dr.expiration);
3727 else
3728 ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
3730 start = ast_tvnow();
3731 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
3732 if (dr.pfds[0] > -1) {
3733 ms = 100;
3734 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
3735 } else
3736 usleep(1);
3738 cancel_request(&dr);
3739 if (dr.pfds[0] > -1) {
3740 close(dr.pfds[0]);
3741 close(dr.pfds[1]);
3743 return 0;
3746 int dundi_precache(const char *context, const char *number)
3748 dundi_eid *avoid[1] = { NULL, };
3749 return dundi_precache_internal(context, number, dundi_ttl, avoid);
3752 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[])
3754 int res;
3755 struct dundi_request dr;
3756 dundi_eid *rooteid=NULL;
3757 int x;
3758 int ttlms;
3759 int skipped=0;
3760 int foundcache=0;
3761 struct timeval start;
3763 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3765 for (x=0;avoid[x];x++)
3766 rooteid = avoid[x];
3767 /* Now perform real check */
3768 memset(&dr, 0, sizeof(dr));
3769 dr.hmd = hmd;
3770 dr.dei = dei;
3771 dr.pfds[0] = dr.pfds[1] = -1;
3772 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
3773 memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
3774 if (rooteid)
3775 dr.root_eid = *rooteid;
3776 /* Create transactions */
3777 build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
3779 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3780 do this earlier because we didn't know if we were going to have transactions
3781 or not. */
3782 if (!ttl) {
3783 ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
3784 return 0;
3787 /* Optimize transactions */
3788 optimize_transactions(&dr, 9999);
3789 /* Actually perform transactions */
3790 query_transactions(&dr);
3791 /* Wait for transaction to come back */
3792 start = ast_tvnow();
3793 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
3794 usleep(1);
3795 res = dr.respcount;
3796 return res;
3799 int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
3801 dundi_eid *avoid[1] = { NULL, };
3802 struct dundi_hint_metadata hmd;
3803 memset(&hmd, 0, sizeof(hmd));
3804 return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
3807 static int dundifunc_read(struct ast_channel *chan, char *cmd, char *num, char *buf, size_t len)
3809 char *context;
3810 char *opts;
3811 int results;
3812 int x;
3813 int bypass = 0;
3814 struct ast_module_user *u;
3815 struct dundi_result dr[MAX_RESULTS];
3817 buf[0] = '\0';
3819 if (ast_strlen_zero(num)) {
3820 ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
3821 return -1;
3824 u = ast_module_user_add(chan);
3826 context = strchr(num, '|');
3827 if (context) {
3828 *context++ = '\0';
3829 opts = strchr(context, '|');
3830 if (opts) {
3831 *opts++ = '\0';
3832 if (strchr(opts, 'b'))
3833 bypass = 1;
3837 if (ast_strlen_zero(context))
3838 context = "e164";
3840 results = dundi_lookup(dr, MAX_RESULTS, NULL, context, num, bypass);
3841 if (results > 0) {
3842 sort_results(dr, results);
3843 for (x = 0; x < results; x++) {
3844 if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
3845 snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
3846 break;
3851 ast_module_user_remove(u);
3853 return 0;
3856 /*! DUNDILOOKUP
3857 * \ingroup functions
3860 static struct ast_custom_function dundi_function = {
3861 .name = "DUNDILOOKUP",
3862 .synopsis = "Do a DUNDi lookup of a phone number.",
3863 .syntax = "DUNDILOOKUP(number[|context[|options]])",
3864 .desc = "This will do a DUNDi lookup of the given phone number.\n"
3865 "If no context is given, the default will be e164. The result of\n"
3866 "this function will the Technology/Resource found in the DUNDi\n"
3867 "lookup. If no results were found, the result will be blank.\n"
3868 "If the 'b' option is specified, the internal DUNDi cache will\n"
3869 "be bypassed.\n",
3870 .read = dundifunc_read,
3873 static void mark_peers(void)
3875 struct dundi_peer *peer;
3876 AST_LIST_LOCK(&peers);
3877 AST_LIST_TRAVERSE(&peers, peer, list) {
3878 peer->dead = 1;
3880 AST_LIST_UNLOCK(&peers);
3883 static void mark_mappings(void)
3885 struct dundi_mapping *map;
3887 AST_LIST_LOCK(&peers);
3888 AST_LIST_TRAVERSE(&mappings, map, list) {
3889 map->dead = 1;
3891 AST_LIST_UNLOCK(&peers);
3894 static void destroy_permissions(struct permissionlist *permlist)
3896 struct permission *perm;
3898 while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
3899 free(perm);
3902 static void destroy_peer(struct dundi_peer *peer)
3904 AST_SCHED_DEL(sched, peer->registerid);
3905 if (peer->regtrans)
3906 destroy_trans(peer->regtrans, 0);
3907 AST_SCHED_DEL(sched, peer->qualifyid);
3908 destroy_permissions(&peer->permit);
3909 destroy_permissions(&peer->include);
3910 free(peer);
3913 static void destroy_map(struct dundi_mapping *map)
3915 free(map);
3918 static void prune_peers(void)
3920 struct dundi_peer *peer;
3922 AST_LIST_LOCK(&peers);
3923 AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, peer, list) {
3924 if (peer->dead) {
3925 AST_LIST_REMOVE_CURRENT(&peers, list);
3926 destroy_peer(peer);
3929 AST_LIST_TRAVERSE_SAFE_END
3930 AST_LIST_UNLOCK(&peers);
3933 static void prune_mappings(void)
3935 struct dundi_mapping *map;
3937 AST_LIST_LOCK(&peers);
3938 AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings, map, list) {
3939 if (map->dead) {
3940 AST_LIST_REMOVE_CURRENT(&mappings, list);
3941 destroy_map(map);
3944 AST_LIST_TRAVERSE_SAFE_END
3945 AST_LIST_UNLOCK(&peers);
3948 static void append_permission(struct permissionlist *permlist, char *s, int allow)
3950 struct permission *perm;
3952 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
3953 return;
3955 strcpy(perm->name, s);
3956 perm->allow = allow;
3958 AST_LIST_INSERT_TAIL(permlist, perm, list);
3961 #define MAX_OPTS 128
3963 static void build_mapping(char *name, char *value)
3965 char *t, *fields[MAX_OPTS];
3966 struct dundi_mapping *map;
3967 int x;
3968 int y;
3970 t = ast_strdupa(value);
3972 AST_LIST_TRAVERSE(&mappings, map, list) {
3973 /* Find a double match */
3974 if (!strcasecmp(map->dcontext, name) &&
3975 (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
3976 (!value[strlen(map->lcontext)] ||
3977 (value[strlen(map->lcontext)] == ','))))
3978 break;
3980 if (!map) {
3981 if (!(map = ast_calloc(1, sizeof(*map))))
3982 return;
3983 AST_LIST_INSERT_HEAD(&mappings, map, list);
3984 map->dead = 1;
3986 map->options = 0;
3987 memset(fields, 0, sizeof(fields));
3988 x = 0;
3989 while (t && x < MAX_OPTS) {
3990 fields[x++] = t;
3991 t = strchr(t, ',');
3992 if (t) {
3993 *t = '\0';
3994 t++;
3996 } /* Russell was here, arrrr! */
3997 if ((x == 1) && ast_strlen_zero(fields[0])) {
3998 /* Placeholder mapping */
3999 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
4000 map->dead = 0;
4001 } else if (x >= 4) {
4002 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
4003 ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
4004 if ((sscanf(fields[1], "%d", &map->weight) == 1) && (map->weight >= 0) && (map->weight < 60000)) {
4005 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
4006 if ((map->tech = str2tech(fields[2]))) {
4007 map->dead = 0;
4009 } else {
4010 ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
4012 for (y = 4;y < x; y++) {
4013 if (!strcasecmp(fields[y], "nounsolicited"))
4014 map->options |= DUNDI_FLAG_NOUNSOLICITED;
4015 else if (!strcasecmp(fields[y], "nocomunsolicit"))
4016 map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
4017 else if (!strcasecmp(fields[y], "residential"))
4018 map->options |= DUNDI_FLAG_RESIDENTIAL;
4019 else if (!strcasecmp(fields[y], "commercial"))
4020 map->options |= DUNDI_FLAG_COMMERCIAL;
4021 else if (!strcasecmp(fields[y], "mobile"))
4022 map->options |= DUNDI_FLAG_MOBILE;
4023 else if (!strcasecmp(fields[y], "nopartial"))
4024 map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
4025 else
4026 ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
4028 } else
4029 ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
4032 /* \note Called with the peers list already locked */
4033 static int do_register(const void *data)
4035 struct dundi_ie_data ied;
4036 struct dundi_peer *peer = (struct dundi_peer *)data;
4037 char eid_str[20];
4038 char eid_str2[20];
4039 ast_log(LOG_DEBUG, "Register us as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->us_eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &peer->eid));
4040 peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
4041 /* Destroy old transaction if there is one */
4042 if (peer->regtrans)
4043 destroy_trans(peer->regtrans, 0);
4044 peer->regtrans = create_transaction(peer);
4045 if (peer->regtrans) {
4046 ast_set_flag(peer->regtrans, FLAG_ISREG);
4047 memset(&ied, 0, sizeof(ied));
4048 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
4049 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
4050 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
4051 dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
4053 } else
4054 ast_log(LOG_NOTICE, "Unable to create new transaction for registering to '%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4056 return 0;
4059 static int do_qualify(const void *data)
4061 struct dundi_peer *peer = (struct dundi_peer *)data;
4062 peer->qualifyid = -1;
4063 qualify_peer(peer, 0);
4064 return 0;
4067 static void qualify_peer(struct dundi_peer *peer, int schedonly)
4069 int when;
4070 AST_SCHED_DEL(sched, peer->qualifyid);
4071 if (peer->qualtrans)
4072 destroy_trans(peer->qualtrans, 0);
4073 peer->qualtrans = NULL;
4074 if (peer->maxms > 0) {
4075 when = 60000;
4076 if (peer->lastms < 0)
4077 when = 10000;
4078 if (schedonly)
4079 when = 5000;
4080 peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
4081 if (!schedonly)
4082 peer->qualtrans = create_transaction(peer);
4083 if (peer->qualtrans) {
4084 peer->qualtx = ast_tvnow();
4085 ast_set_flag(peer->qualtrans, FLAG_ISQUAL);
4086 dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
4090 static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
4092 char data[256];
4093 char *c;
4094 int port, expire;
4095 char eid_str[20];
4096 dundi_eid_to_str(eid_str, sizeof(eid_str), eid);
4097 if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
4098 c = strchr(data, ':');
4099 if (c) {
4100 *c = '\0';
4101 c++;
4102 if (sscanf(c, "%d:%d", &port, &expire) == 2) {
4103 /* Got it! */
4104 inet_aton(data, &peer->addr.sin_addr);
4105 peer->addr.sin_family = AF_INET;
4106 peer->addr.sin_port = htons(port);
4107 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
4114 static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
4116 struct dundi_peer *peer;
4117 struct ast_hostent he;
4118 struct hostent *hp;
4119 dundi_eid testeid;
4120 int needregister=0;
4121 char eid_str[20];
4123 AST_LIST_LOCK(&peers);
4124 AST_LIST_TRAVERSE(&peers, peer, list) {
4125 if (!dundi_eid_cmp(&peer->eid, eid)) {
4126 break;
4129 if (!peer) {
4130 /* Add us into the list */
4131 if (!(peer = ast_calloc(1, sizeof(*peer)))) {
4132 AST_LIST_UNLOCK(&peers);
4133 return;
4135 peer->registerid = -1;
4136 peer->registerexpire = -1;
4137 peer->qualifyid = -1;
4138 peer->addr.sin_family = AF_INET;
4139 peer->addr.sin_port = htons(DUNDI_PORT);
4140 populate_addr(peer, eid);
4141 AST_LIST_INSERT_HEAD(&peers, peer, list);
4143 peer->dead = 0;
4144 peer->eid = *eid;
4145 peer->us_eid = global_eid;
4146 destroy_permissions(&peer->permit);
4147 destroy_permissions(&peer->include);
4148 AST_SCHED_DEL(sched, peer->registerid);
4149 for (; v; v = v->next) {
4150 if (!strcasecmp(v->name, "inkey")) {
4151 ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
4152 } else if (!strcasecmp(v->name, "outkey")) {
4153 ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
4154 } else if (!strcasecmp(v->name, "host")) {
4155 if (!strcasecmp(v->value, "dynamic")) {
4156 peer->dynamic = 1;
4157 } else {
4158 hp = ast_gethostbyname(v->value, &he);
4159 if (hp) {
4160 memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
4161 peer->dynamic = 0;
4162 } else {
4163 ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
4164 peer->dead = 1;
4167 } else if (!strcasecmp(v->name, "ustothem")) {
4168 if (!dundi_str_to_eid(&testeid, v->value))
4169 peer->us_eid = testeid;
4170 else
4171 ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
4172 } else if (!strcasecmp(v->name, "include")) {
4173 append_permission(&peer->include, v->value, 1);
4174 } else if (!strcasecmp(v->name, "permit")) {
4175 append_permission(&peer->permit, v->value, 1);
4176 } else if (!strcasecmp(v->name, "noinclude")) {
4177 append_permission(&peer->include, v->value, 0);
4178 } else if (!strcasecmp(v->name, "deny")) {
4179 append_permission(&peer->permit, v->value, 0);
4180 } else if (!strcasecmp(v->name, "register")) {
4181 needregister = ast_true(v->value);
4182 } else if (!strcasecmp(v->name, "order")) {
4183 if (!strcasecmp(v->value, "primary"))
4184 peer->order = 0;
4185 else if (!strcasecmp(v->value, "secondary"))
4186 peer->order = 1;
4187 else if (!strcasecmp(v->value, "tertiary"))
4188 peer->order = 2;
4189 else if (!strcasecmp(v->value, "quartiary"))
4190 peer->order = 3;
4191 else {
4192 ast_log(LOG_WARNING, "'%s' is not a valid order, should be primary, secondary, tertiary or quartiary at line %d\n", v->value, v->lineno);
4194 } else if (!strcasecmp(v->name, "qualify")) {
4195 if (!strcasecmp(v->value, "no")) {
4196 peer->maxms = 0;
4197 } else if (!strcasecmp(v->value, "yes")) {
4198 peer->maxms = DEFAULT_MAXMS;
4199 } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
4200 ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
4201 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
4202 peer->maxms = 0;
4204 } else if (!strcasecmp(v->name, "model")) {
4205 if (!strcasecmp(v->value, "inbound"))
4206 peer->model = DUNDI_MODEL_INBOUND;
4207 else if (!strcasecmp(v->value, "outbound"))
4208 peer->model = DUNDI_MODEL_OUTBOUND;
4209 else if (!strcasecmp(v->value, "symmetric"))
4210 peer->model = DUNDI_MODEL_SYMMETRIC;
4211 else if (!strcasecmp(v->value, "none"))
4212 peer->model = 0;
4213 else {
4214 ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4215 v->value, v->lineno);
4217 } else if (!strcasecmp(v->name, "precache")) {
4218 if (!strcasecmp(v->value, "inbound"))
4219 peer->pcmodel = DUNDI_MODEL_INBOUND;
4220 else if (!strcasecmp(v->value, "outbound"))
4221 peer->pcmodel = DUNDI_MODEL_OUTBOUND;
4222 else if (!strcasecmp(v->value, "symmetric"))
4223 peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
4224 else if (!strcasecmp(v->value, "none"))
4225 peer->pcmodel = 0;
4226 else {
4227 ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4228 v->value, v->lineno);
4232 (*globalpcmode) |= peer->pcmodel;
4233 if (!peer->model && !peer->pcmodel) {
4234 ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
4235 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4236 peer->dead = 1;
4237 } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4238 ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
4239 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4240 peer->dead = 1;
4241 } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4242 ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
4243 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4244 peer->dead = 1;
4245 } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4246 ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
4247 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4248 } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4249 ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer or outbound precache!\n",
4250 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4251 } else {
4252 if (needregister) {
4253 peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
4255 qualify_peer(peer, 1);
4257 AST_LIST_UNLOCK(&peers);
4260 static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
4262 struct dundi_result results[MAX_RESULTS];
4263 int res;
4264 int x;
4265 int found = 0;
4266 if (!strncasecmp(context, "macro-", 6)) {
4267 if (!chan) {
4268 ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
4269 return -1;
4271 /* If done as a macro, use macro extension */
4272 if (!strcasecmp(exten, "s")) {
4273 exten = pbx_builtin_getvar_helper(chan, "ARG1");
4274 if (ast_strlen_zero(exten))
4275 exten = chan->macroexten;
4276 if (ast_strlen_zero(exten))
4277 exten = chan->exten;
4278 if (ast_strlen_zero(exten)) {
4279 ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
4280 return -1;
4283 if (ast_strlen_zero(data))
4284 data = "e164";
4285 } else {
4286 if (ast_strlen_zero(data))
4287 data = context;
4289 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4290 for (x=0;x<res;x++) {
4291 if (ast_test_flag(results + x, flag))
4292 found++;
4294 if (found >= priority)
4295 return 1;
4296 return 0;
4299 static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4301 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
4304 static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4306 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
4309 static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4311 struct dundi_result results[MAX_RESULTS];
4312 int res;
4313 int x=0;
4314 char req[1024];
4315 struct ast_app *dial;
4317 if (!strncasecmp(context, "macro-", 6)) {
4318 if (!chan) {
4319 ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
4320 return -1;
4322 /* If done as a macro, use macro extension */
4323 if (!strcasecmp(exten, "s")) {
4324 exten = pbx_builtin_getvar_helper(chan, "ARG1");
4325 if (ast_strlen_zero(exten))
4326 exten = chan->macroexten;
4327 if (ast_strlen_zero(exten))
4328 exten = chan->exten;
4329 if (ast_strlen_zero(exten)) {
4330 ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
4331 return -1;
4334 if (ast_strlen_zero(data))
4335 data = "e164";
4336 } else {
4337 if (ast_strlen_zero(data))
4338 data = context;
4340 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4341 if (res > 0) {
4342 sort_results(results, res);
4343 for (x=0;x<res;x++) {
4344 if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
4345 if (!--priority)
4346 break;
4350 if (x < res) {
4351 /* Got a hit! */
4352 snprintf(req, sizeof(req), "%s/%s", results[x].tech, results[x].dest);
4353 dial = pbx_findapp("Dial");
4354 if (dial)
4355 res = pbx_exec(chan, dial, req);
4356 } else
4357 res = -1;
4358 return res;
4361 static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4363 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
4366 static struct ast_switch dundi_switch =
4368 name: "DUNDi",
4369 description: "DUNDi Discovered Dialplan Switch",
4370 exists: dundi_exists,
4371 canmatch: dundi_canmatch,
4372 exec: dundi_exec,
4373 matchmore: dundi_matchmore,
4376 static int set_config(char *config_file, struct sockaddr_in* sin)
4378 struct ast_config *cfg;
4379 struct ast_variable *v;
4380 char *cat;
4381 int format;
4382 int x;
4383 char hn[MAXHOSTNAMELEN] = "";
4384 struct ast_hostent he;
4385 struct hostent *hp;
4386 struct sockaddr_in sin2;
4387 static int last_port = 0;
4388 int globalpcmodel = 0;
4389 dundi_eid testeid;
4391 dundi_ttl = DUNDI_DEFAULT_TTL;
4392 dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
4393 any_peer = NULL;
4395 cfg = ast_config_load(config_file);
4397 if (!cfg) {
4398 ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
4399 return -1;
4401 ipaddr[0] = '\0';
4402 if (!gethostname(hn, sizeof(hn)-1)) {
4403 hp = ast_gethostbyname(hn, &he);
4404 if (hp) {
4405 memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
4406 ast_copy_string(ipaddr, ast_inet_ntoa(sin2.sin_addr), sizeof(ipaddr));
4407 } else
4408 ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
4409 } else
4410 ast_log(LOG_WARNING, "Unable to get host name!\n");
4411 AST_LIST_LOCK(&peers);
4412 reset_global_eid();
4413 global_storehistory = 0;
4414 ast_copy_string(secretpath, "dundi", sizeof(secretpath));
4415 v = ast_variable_browse(cfg, "general");
4416 while(v) {
4417 if (!strcasecmp(v->name, "port")){
4418 sin->sin_port = ntohs(atoi(v->value));
4419 if(last_port==0){
4420 last_port=sin->sin_port;
4421 } else if(sin->sin_port != last_port)
4422 ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
4423 } else if (!strcasecmp(v->name, "bindaddr")) {
4424 struct hostent *hp;
4425 struct ast_hostent he;
4426 hp = ast_gethostbyname(v->value, &he);
4427 if (hp) {
4428 memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
4429 } else
4430 ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
4431 } else if (!strcasecmp(v->name, "authdebug")) {
4432 authdebug = ast_true(v->value);
4433 } else if (!strcasecmp(v->name, "ttl")) {
4434 if ((sscanf(v->value, "%d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
4435 dundi_ttl = x;
4436 } else {
4437 ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
4438 v->value, v->lineno, DUNDI_DEFAULT_TTL);
4440 } else if (!strcasecmp(v->name, "autokill")) {
4441 if (sscanf(v->value, "%d", &x) == 1) {
4442 if (x >= 0)
4443 global_autokilltimeout = x;
4444 else
4445 ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
4446 } else if (ast_true(v->value)) {
4447 global_autokilltimeout = DEFAULT_MAXMS;
4448 } else {
4449 global_autokilltimeout = 0;
4451 } else if (!strcasecmp(v->name, "entityid")) {
4452 if (!dundi_str_to_eid(&testeid, v->value))
4453 global_eid = testeid;
4454 else
4455 ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
4456 } else if (!strcasecmp(v->name, "tos")) {
4457 if (sscanf(v->value, "%d", &format) == 1)
4458 tos = format & 0xff;
4459 else if (!strcasecmp(v->value, "lowdelay"))
4460 tos = IPTOS_LOWDELAY;
4461 else if (!strcasecmp(v->value, "throughput"))
4462 tos = IPTOS_THROUGHPUT;
4463 else if (!strcasecmp(v->value, "reliability"))
4464 tos = IPTOS_RELIABILITY;
4465 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
4466 else if (!strcasecmp(v->value, "mincost"))
4467 tos = IPTOS_MINCOST;
4468 #endif
4469 else if (!strcasecmp(v->value, "none"))
4470 tos = 0;
4471 else
4472 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
4473 ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno);
4474 #else
4475 ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', or 'none'\n", v->lineno);
4476 #endif
4477 } else if (!strcasecmp(v->name, "department")) {
4478 ast_copy_string(dept, v->value, sizeof(dept));
4479 } else if (!strcasecmp(v->name, "organization")) {
4480 ast_copy_string(org, v->value, sizeof(org));
4481 } else if (!strcasecmp(v->name, "locality")) {
4482 ast_copy_string(locality, v->value, sizeof(locality));
4483 } else if (!strcasecmp(v->name, "stateprov")) {
4484 ast_copy_string(stateprov, v->value, sizeof(stateprov));
4485 } else if (!strcasecmp(v->name, "country")) {
4486 ast_copy_string(country, v->value, sizeof(country));
4487 } else if (!strcasecmp(v->name, "email")) {
4488 ast_copy_string(email, v->value, sizeof(email));
4489 } else if (!strcasecmp(v->name, "phone")) {
4490 ast_copy_string(phone, v->value, sizeof(phone));
4491 } else if (!strcasecmp(v->name, "storehistory")) {
4492 global_storehistory = ast_true(v->value);
4493 } else if (!strcasecmp(v->name, "cachetime")) {
4494 if ((sscanf(v->value, "%d", &x) == 1)) {
4495 dundi_cache_time = x;
4496 } else {
4497 ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
4498 v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME);
4501 v = v->next;
4503 AST_LIST_UNLOCK(&peers);
4504 mark_mappings();
4505 v = ast_variable_browse(cfg, "mappings");
4506 while(v) {
4507 build_mapping(v->name, v->value);
4508 v = v->next;
4510 prune_mappings();
4511 mark_peers();
4512 cat = ast_category_browse(cfg, NULL);
4513 while(cat) {
4514 if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
4515 /* Entries */
4516 if (!dundi_str_to_eid(&testeid, cat))
4517 build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
4518 else if (!strcasecmp(cat, "*")) {
4519 build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
4520 any_peer = find_peer(NULL);
4521 } else
4522 ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
4524 cat = ast_category_browse(cfg, cat);
4526 prune_peers();
4527 ast_config_destroy(cfg);
4528 load_password();
4529 if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
4530 dundi_precache_full();
4531 return 0;
4534 static int unload_module(void)
4536 pthread_t previous_netthreadid = netthreadid, previous_precachethreadid = precachethreadid;
4537 ast_module_user_hangup_all();
4539 /* Stop all currently running threads */
4540 dundi_shutdown = 1;
4541 if (previous_netthreadid != AST_PTHREADT_NULL) {
4542 pthread_kill(previous_netthreadid, SIGURG);
4543 pthread_join(previous_netthreadid, NULL);
4545 if (previous_precachethreadid != AST_PTHREADT_NULL) {
4546 pthread_kill(previous_precachethreadid, SIGURG);
4547 pthread_join(previous_precachethreadid, NULL);
4550 ast_cli_unregister_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(struct ast_cli_entry));
4551 ast_unregister_switch(&dundi_switch);
4552 ast_custom_function_unregister(&dundi_function);
4553 close(netsocket);
4554 io_context_destroy(io);
4555 sched_context_destroy(sched);
4557 mark_mappings();
4558 prune_mappings();
4559 mark_peers();
4560 prune_peers();
4562 return 0;
4565 static int reload(void)
4567 struct sockaddr_in sin;
4568 set_config("dundi.conf",&sin);
4569 return 0;
4572 static int load_module(void)
4574 int res = 0;
4575 struct sockaddr_in sin;
4577 dundi_set_output(dundi_debug_output);
4578 dundi_set_error(dundi_error_output);
4580 sin.sin_family = AF_INET;
4581 sin.sin_port = ntohs(DUNDI_PORT);
4582 sin.sin_addr.s_addr = INADDR_ANY;
4584 /* Make a UDP socket */
4585 io = io_context_create();
4586 sched = sched_context_create();
4588 if (!io || !sched) {
4589 ast_log(LOG_ERROR, "Out of memory\n");
4590 return -1;
4593 if(set_config("dundi.conf",&sin))
4594 return AST_MODULE_LOAD_DECLINE;
4596 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
4598 if (netsocket < 0) {
4599 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
4600 return -1;
4602 if (bind(netsocket,(struct sockaddr *)&sin, sizeof(sin))) {
4603 ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
4604 return -1;
4607 if (option_verbose > 1)
4608 ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
4610 if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)))
4611 ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
4613 res = start_network_thread();
4614 if (res) {
4615 ast_log(LOG_ERROR, "Unable to start network thread\n");
4616 close(netsocket);
4617 return -1;
4620 if (option_verbose > 1)
4621 ast_verbose(VERBOSE_PREFIX_2 "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
4623 ast_cli_register_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(struct ast_cli_entry));
4624 if (ast_register_switch(&dundi_switch))
4625 ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
4626 ast_custom_function_register(&dundi_function);
4628 return res;
4631 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
4632 .load = load_module,
4633 .unload = unload_module,
4634 .reload = reload,