Blocked revisions 115296 via svnmerge
[asterisk-bristuff.git] / pbx / pbx_dundi.c
blob647cd1dc22380948bfd6e80618a303da1c831279
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 struct dundi_ie_data ied;
1545 struct dundi_ies ies;
1546 struct dundi_peer *peer = NULL;
1547 char eid_str[20];
1548 char eid_str2[20];
1549 memset(&ied, 0, sizeof(ied));
1550 memset(&ies, 0, sizeof(ies));
1551 if (datalen) {
1552 bufcpy = alloca(datalen);
1553 if (!bufcpy)
1554 return -1;
1555 /* Make a copy for parsing */
1556 memcpy(bufcpy, hdr->ies, datalen);
1557 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1558 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1559 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1560 return -1;
1563 switch(cmd) {
1564 case DUNDI_COMMAND_DPDISCOVER:
1565 case DUNDI_COMMAND_EIDQUERY:
1566 case DUNDI_COMMAND_PRECACHERQ:
1567 if (cmd == DUNDI_COMMAND_EIDQUERY)
1568 resp = DUNDI_COMMAND_EIDRESPONSE;
1569 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1570 resp = DUNDI_COMMAND_PRECACHERP;
1571 else
1572 resp = DUNDI_COMMAND_DPRESPONSE;
1573 /* A dialplan or entity discover -- qualify by highest level entity */
1574 peer = find_peer(ies.eids[0]);
1575 if (!peer) {
1576 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1577 dundi_send(trans, resp, 0, 1, &ied);
1578 } else {
1579 int hasauth = 0;
1580 trans->us_eid = peer->us_eid;
1581 if (strlen(peer->inkey)) {
1582 hasauth = encrypted;
1583 } else
1584 hasauth = 1;
1585 if (hasauth) {
1586 /* Okay we're authentiated and all, now we check if they're authorized */
1587 if (!ies.called_context)
1588 ies.called_context = "e164";
1589 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1590 res = dundi_answer_entity(trans, &ies, ies.called_context);
1591 } else {
1592 if (ast_strlen_zero(ies.called_number)) {
1593 /* They're not permitted to access that context */
1594 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1595 dundi_send(trans, resp, 0, 1, &ied);
1596 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1597 (peer->model & DUNDI_MODEL_INBOUND) &&
1598 has_permission(&peer->permit, ies.called_context)) {
1599 res = dundi_answer_query(trans, &ies, ies.called_context);
1600 if (res < 0) {
1601 /* There is no such dundi context */
1602 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1603 dundi_send(trans, resp, 0, 1, &ied);
1605 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1606 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1607 has_permission(&peer->include, ies.called_context)) {
1608 res = dundi_prop_precache(trans, &ies, ies.called_context);
1609 if (res < 0) {
1610 /* There is no such dundi context */
1611 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1612 dundi_send(trans, resp, 0, 1, &ied);
1614 } else {
1615 /* They're not permitted to access that context */
1616 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1617 dundi_send(trans, resp, 0, 1, &ied);
1620 } else {
1621 /* They're not permitted to access that context */
1622 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1623 dundi_send(trans, resp, 0, 1, &ied);
1626 break;
1627 case DUNDI_COMMAND_REGREQ:
1628 /* A register request -- should only have one entity */
1629 peer = find_peer(ies.eids[0]);
1631 /* if the peer is not found and we have a valid 'any_peer' setting */
1632 if (any_peer && peer == any_peer) {
1633 /* copy any_peer into a new peer object */
1634 peer = ast_calloc(1, sizeof(*peer));
1635 if (peer) {
1636 deep_copy_peer(peer, any_peer);
1638 /* set EID to remote EID */
1639 peer->eid = *ies.eids[0];
1641 AST_LIST_LOCK(&peers);
1642 AST_LIST_INSERT_HEAD(&peers, peer, list);
1643 AST_LIST_UNLOCK(&peers);
1647 if (!peer || !peer->dynamic) {
1648 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1649 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1650 } else {
1651 int hasauth = 0;
1652 trans->us_eid = peer->us_eid;
1653 if (!ast_strlen_zero(peer->inkey)) {
1654 hasauth = encrypted;
1655 } else
1656 hasauth = 1;
1657 if (hasauth) {
1658 int expire = default_expiration;
1659 char data[256];
1660 int needqual = 0;
1661 AST_SCHED_DEL(sched, peer->registerexpire);
1662 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1663 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
1664 ntohs(trans->addr.sin_port), expire);
1665 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1666 if (inaddrcmp(&peer->addr, &trans->addr)) {
1667 if (option_verbose > 2) {
1668 ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n",
1669 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1670 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
1672 needqual = 1;
1675 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1676 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1677 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1678 if (needqual)
1679 qualify_peer(peer, 1);
1682 break;
1683 case DUNDI_COMMAND_DPRESPONSE:
1684 /* A dialplan response, lets see what we got... */
1685 if (ies.cause < 1) {
1686 /* Success of some sort */
1687 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1688 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1689 authpass = encrypted;
1690 } else
1691 authpass = 1;
1692 if (authpass) {
1693 /* Pass back up answers */
1694 if (trans->parent && trans->parent->dr) {
1695 y = trans->parent->respcount;
1696 for (x=0;x<ies.anscount;x++) {
1697 if (trans->parent->respcount < trans->parent->maxcount) {
1698 /* Make sure it's not already there */
1699 for (z=0;z<trans->parent->respcount;z++) {
1700 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1701 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1702 break;
1704 if (z == trans->parent->respcount) {
1705 /* Copy into parent responses */
1706 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1707 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1708 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1709 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1710 if (ies.expiration > 0)
1711 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1712 else
1713 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1714 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1715 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1716 &ies.answers[x]->eid);
1717 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1718 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1719 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1720 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1721 trans->parent->respcount++;
1722 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1723 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1724 /* Update weight if appropriate */
1725 trans->parent->dr[z].weight = ies.answers[x]->weight;
1727 } else
1728 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1729 trans->parent->number, trans->parent->dcontext);
1731 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1732 the cache know if this request was unaffected by our entity list. */
1733 cache_save(&trans->them_eid, trans->parent, y,
1734 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1735 if (ies.hint) {
1736 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1737 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1738 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1739 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1740 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1741 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1742 sizeof(trans->parent->hmd->exten));
1744 } else {
1745 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1748 if (ies.expiration > 0) {
1749 if (trans->parent->expiration > ies.expiration) {
1750 trans->parent->expiration = ies.expiration;
1754 /* Close connection if not final */
1755 if (!final)
1756 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1759 } else {
1760 /* Auth failure, check for data */
1761 if (!final) {
1762 /* Cancel if they didn't already */
1763 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1766 break;
1767 case DUNDI_COMMAND_EIDRESPONSE:
1768 /* A dialplan response, lets see what we got... */
1769 if (ies.cause < 1) {
1770 /* Success of some sort */
1771 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1772 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1773 authpass = encrypted;
1774 } else
1775 authpass = 1;
1776 if (authpass) {
1777 /* Pass back up answers */
1778 if (trans->parent && trans->parent->dei && ies.q_org) {
1779 if (!trans->parent->respcount) {
1780 trans->parent->respcount++;
1781 if (ies.q_dept)
1782 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1783 if (ies.q_org)
1784 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1785 if (ies.q_locality)
1786 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1787 if (ies.q_stateprov)
1788 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1789 if (ies.q_country)
1790 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1791 if (ies.q_email)
1792 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1793 if (ies.q_phone)
1794 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1795 if (ies.q_ipaddr)
1796 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1797 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1798 /* If it's them, update our address */
1799 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
1802 if (ies.hint) {
1803 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1804 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1807 /* Close connection if not final */
1808 if (!final)
1809 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1812 } else {
1813 /* Auth failure, check for data */
1814 if (!final) {
1815 /* Cancel if they didn't already */
1816 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1819 break;
1820 case DUNDI_COMMAND_REGRESPONSE:
1821 /* A dialplan response, lets see what we got... */
1822 if (ies.cause < 1) {
1823 int hasauth;
1824 /* Success of some sort */
1825 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1826 hasauth = encrypted;
1827 } else
1828 hasauth = 1;
1830 if (!hasauth) {
1831 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1832 if (!final) {
1833 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1834 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1836 } else {
1837 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),
1838 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1839 /* Close connection if not final */
1840 if (!final)
1841 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1843 } else {
1844 /* Auth failure, cancel if they didn't for some reason */
1845 if (!final) {
1846 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1849 break;
1850 case DUNDI_COMMAND_INVALID:
1851 case DUNDI_COMMAND_NULL:
1852 case DUNDI_COMMAND_PRECACHERP:
1853 /* Do nothing special */
1854 if (!final)
1855 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1856 break;
1857 case DUNDI_COMMAND_ENCREJ:
1858 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1859 /* No really, it's over at this point */
1860 if (!final)
1861 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1862 } else {
1863 /* Send with full key */
1864 ast_set_flag(trans, FLAG_SENDFULLKEY);
1865 if (final) {
1866 /* Ooops, we got a final message, start by sending ACK... */
1867 dundi_ack(trans, hdr->cmdresp & 0x80);
1868 trans->aseqno = trans->iseqno;
1869 /* Now, we gotta create a new transaction */
1870 if (!reset_transaction(trans)) {
1871 /* Make sure handle_frame doesn't destroy us */
1872 hdr->cmdresp &= 0x7f;
1873 /* Parse the message we transmitted */
1874 memset(&ies, 0, sizeof(ies));
1875 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1876 /* Reconstruct outgoing encrypted packet */
1877 memset(&ied, 0, sizeof(ied));
1878 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1879 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1880 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1881 if (ies.encblock)
1882 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1883 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
1884 peer->sentfullkey = 1;
1888 break;
1889 case DUNDI_COMMAND_ENCRYPT:
1890 if (!encrypted) {
1891 /* No nested encryption! */
1892 if ((trans->iseqno == 1) && !trans->oseqno) {
1893 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1894 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1895 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1896 if (!final) {
1897 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1899 break;
1901 apply_peer(trans, peer);
1902 /* Key passed, use new contexts for this session */
1903 trans->ecx = peer->them_ecx;
1904 trans->dcx = peer->them_dcx;
1906 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1907 struct dundi_hdr *dhdr;
1908 unsigned char decoded[MAX_PACKET_SIZE];
1909 int ddatalen;
1910 ddatalen = sizeof(decoded);
1911 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1912 if (dhdr) {
1913 /* Handle decrypted response */
1914 if (dundidebug)
1915 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1916 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1917 /* Carry back final flag */
1918 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1919 break;
1920 } else
1921 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1924 if (!final) {
1925 /* Turn off encryption */
1926 ast_clear_flag(trans, FLAG_ENCRYPT);
1927 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1929 break;
1930 default:
1931 /* Send unknown command if we don't know it, with final flag IFF it's the
1932 first command in the dialog and only if we haven't recieved final notification */
1933 if (!final) {
1934 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1935 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1938 return 0;
1941 static void destroy_packet(struct dundi_packet *pack, int needfree);
1942 static void destroy_packets(struct packetlist *p)
1944 struct dundi_packet *pack;
1946 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1947 AST_SCHED_DEL(sched, pack->retransid);
1948 free(pack);
1953 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1955 struct dundi_packet *pack;
1957 /* Ack transmitted packet corresponding to iseqno */
1958 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
1959 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1960 destroy_packet(pack, 0);
1961 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
1962 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1963 destroy_packets(&trans->lasttrans);
1965 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
1966 AST_SCHED_DEL(sched, trans->autokillid);
1967 return 1;
1971 return 0;
1974 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1976 struct dundi_transaction *trans;
1977 trans = find_transaction(h, sin);
1978 if (!trans) {
1979 dundi_reject(h, sin);
1980 return 0;
1982 /* Got a transaction, see where this header fits in */
1983 if (h->oseqno == trans->iseqno) {
1984 /* Just what we were looking for... Anything but ack increments iseqno */
1985 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1986 /* If final, we're done */
1987 destroy_trans(trans, 0);
1988 return 0;
1990 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1991 trans->oiseqno = trans->iseqno;
1992 trans->iseqno++;
1993 handle_command_response(trans, h, datalen, 0);
1995 if (trans->aseqno != trans->iseqno) {
1996 dundi_ack(trans, h->cmdresp & 0x80);
1997 trans->aseqno = trans->iseqno;
1999 /* Delete any saved last transmissions */
2000 destroy_packets(&trans->lasttrans);
2001 if (h->cmdresp & 0x80) {
2002 /* Final -- destroy now */
2003 destroy_trans(trans, 0);
2005 } else if (h->oseqno == trans->oiseqno) {
2006 /* Last incoming sequence number -- send ACK without processing */
2007 dundi_ack(trans, 0);
2008 } else {
2009 /* Out of window -- simply drop */
2010 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
2012 return 0;
2015 static int socket_read(int *id, int fd, short events, void *cbdata)
2017 struct sockaddr_in sin;
2018 int res;
2019 struct dundi_hdr *h;
2020 char buf[MAX_PACKET_SIZE];
2021 socklen_t len;
2022 len = sizeof(sin);
2023 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
2024 if (res < 0) {
2025 if (errno != ECONNREFUSED)
2026 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2027 return 1;
2029 if (res < sizeof(struct dundi_hdr)) {
2030 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2031 return 1;
2033 buf[res] = '\0';
2034 h = (struct dundi_hdr *)buf;
2035 if (dundidebug)
2036 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2037 AST_LIST_LOCK(&peers);
2038 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2039 AST_LIST_UNLOCK(&peers);
2040 return 1;
2043 static void build_secret(char *secret, int seclen)
2045 unsigned char tmp[16];
2046 char *s;
2047 build_iv(tmp);
2048 secret[0] = '\0';
2049 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2050 /* Eliminate potential bad characters */
2051 while((s = strchr(secret, ';'))) *s = '+';
2052 while((s = strchr(secret, '/'))) *s = '+';
2053 while((s = strchr(secret, ':'))) *s = '+';
2054 while((s = strchr(secret, '@'))) *s = '+';
2058 static void save_secret(const char *newkey, const char *oldkey)
2060 char tmp[256];
2061 if (oldkey)
2062 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2063 else
2064 snprintf(tmp, sizeof(tmp), "%s", newkey);
2065 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2066 ast_db_put(secretpath, "secret", tmp);
2067 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2068 ast_db_put(secretpath, "secretexpiry", tmp);
2071 static void load_password(void)
2073 char *current=NULL;
2074 char *last=NULL;
2075 char tmp[256];
2076 time_t expired;
2078 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2079 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2080 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2081 current = strchr(tmp, ';');
2082 if (!current)
2083 current = tmp;
2084 else {
2085 *current = '\0';
2086 current++;
2088 if ((time(NULL) - expired) < 0) {
2089 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2090 expired = time(NULL) + DUNDI_SECRET_TIME;
2091 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2092 last = current;
2093 current = NULL;
2094 } else {
2095 last = NULL;
2096 current = NULL;
2099 if (current) {
2100 /* Current key is still valid, just setup rotatation properly */
2101 ast_copy_string(cursecret, current, sizeof(cursecret));
2102 rotatetime = expired;
2103 } else {
2104 /* Current key is out of date, rotate or eliminate all together */
2105 build_secret(cursecret, sizeof(cursecret));
2106 save_secret(cursecret, last);
2110 static void check_password(void)
2112 char oldsecret[80];
2113 time_t now;
2115 time(&now);
2116 #if 0
2117 printf("%ld/%ld\n", now, rotatetime);
2118 #endif
2119 if ((now - rotatetime) >= 0) {
2120 /* Time to rotate keys */
2121 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2122 build_secret(cursecret, sizeof(cursecret));
2123 save_secret(cursecret, oldsecret);
2127 static void *network_thread(void *ignore)
2129 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2130 from the network, and queue them for delivery to the channels */
2131 int res;
2132 /* Establish I/O callback for socket read */
2133 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2135 while (!dundi_shutdown) {
2136 res = ast_sched_wait(sched);
2137 if ((res > 1000) || (res < 0))
2138 res = 1000;
2139 res = ast_io_wait(io, res);
2140 if (res >= 0) {
2141 AST_LIST_LOCK(&peers);
2142 ast_sched_runq(sched);
2143 AST_LIST_UNLOCK(&peers);
2145 check_password();
2148 netthreadid = AST_PTHREADT_NULL;
2150 return NULL;
2153 static void *process_precache(void *ign)
2155 struct dundi_precache_queue *qe;
2156 time_t now;
2157 char context[256];
2158 char number[256];
2159 int run;
2161 while (!dundi_shutdown) {
2162 time(&now);
2163 run = 0;
2164 AST_LIST_LOCK(&pcq);
2165 if ((qe = AST_LIST_FIRST(&pcq))) {
2166 if (!qe->expiration) {
2167 /* Gone... Remove... */
2168 AST_LIST_REMOVE_HEAD(&pcq, list);
2169 free(qe);
2170 } else if (qe->expiration < now) {
2171 /* Process this entry */
2172 qe->expiration = 0;
2173 ast_copy_string(context, qe->context, sizeof(context));
2174 ast_copy_string(number, qe->number, sizeof(number));
2175 run = 1;
2178 AST_LIST_UNLOCK(&pcq);
2179 if (run) {
2180 dundi_precache(context, number);
2181 } else
2182 sleep(1);
2185 precachethreadid = AST_PTHREADT_NULL;
2187 return NULL;
2190 static int start_network_thread(void)
2192 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2193 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2194 return 0;
2197 static int dundi_do_debug(int fd, int argc, char *argv[])
2199 if (argc != 2)
2200 return RESULT_SHOWUSAGE;
2201 dundidebug = 1;
2202 ast_cli(fd, "DUNDi Debugging Enabled\n");
2203 return RESULT_SUCCESS;
2206 static int dundi_do_store_history(int fd, int argc, char *argv[])
2208 if (argc != 3)
2209 return RESULT_SHOWUSAGE;
2210 global_storehistory = 1;
2211 ast_cli(fd, "DUNDi History Storage Enabled\n");
2212 return RESULT_SUCCESS;
2215 static int dundi_flush(int fd, int argc, char *argv[])
2217 int stats = 0;
2218 if ((argc < 2) || (argc > 3))
2219 return RESULT_SHOWUSAGE;
2220 if (argc > 2) {
2221 if (!strcasecmp(argv[2], "stats"))
2222 stats = 1;
2223 else
2224 return RESULT_SHOWUSAGE;
2226 if (stats) {
2227 /* Flush statistics */
2228 struct dundi_peer *p;
2229 int x;
2230 AST_LIST_LOCK(&peers);
2231 AST_LIST_TRAVERSE(&peers, p, list) {
2232 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2233 if (p->lookups[x])
2234 free(p->lookups[x]);
2235 p->lookups[x] = NULL;
2236 p->lookuptimes[x] = 0;
2238 p->avgms = 0;
2240 AST_LIST_UNLOCK(&peers);
2241 } else {
2242 ast_db_deltree("dundi/cache", NULL);
2243 ast_cli(fd, "DUNDi Cache Flushed\n");
2245 return RESULT_SUCCESS;
2248 static int dundi_no_debug(int fd, int argc, char *argv[])
2250 if (argc != 3)
2251 return RESULT_SHOWUSAGE;
2252 dundidebug = 0;
2253 ast_cli(fd, "DUNDi Debugging Disabled\n");
2254 return RESULT_SUCCESS;
2257 static int dundi_no_store_history(int fd, int argc, char *argv[])
2259 if (argc != 4)
2260 return RESULT_SHOWUSAGE;
2261 global_storehistory = 0;
2262 ast_cli(fd, "DUNDi History Storage Disabled\n");
2263 return RESULT_SUCCESS;
2266 static char *model2str(int model)
2268 switch(model) {
2269 case DUNDI_MODEL_INBOUND:
2270 return "Inbound";
2271 case DUNDI_MODEL_OUTBOUND:
2272 return "Outbound";
2273 case DUNDI_MODEL_SYMMETRIC:
2274 return "Symmetric";
2275 default:
2276 return "Unknown";
2280 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2282 int which=0, len;
2283 char *ret = NULL;
2284 struct dundi_peer *p;
2285 char eid_str[20];
2287 if (pos != rpos)
2288 return NULL;
2289 AST_LIST_LOCK(&peers);
2290 len = strlen(word);
2291 AST_LIST_TRAVERSE(&peers, p, list) {
2292 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2293 if (!strncasecmp(word, s, len) && ++which > state) {
2294 ret = ast_strdup(s);
2295 break;
2298 AST_LIST_UNLOCK(&peers);
2299 return ret;
2302 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2304 return complete_peer_helper(line, word, pos, state, 3);
2307 static int rescomp(const void *a, const void *b)
2309 const struct dundi_result *resa, *resb;
2310 resa = a;
2311 resb = b;
2312 if (resa->weight < resb->weight)
2313 return -1;
2314 if (resa->weight > resb->weight)
2315 return 1;
2316 return 0;
2319 static void sort_results(struct dundi_result *results, int count)
2321 qsort(results, count, sizeof(results[0]), rescomp);
2324 static int dundi_do_lookup(int fd, int argc, char *argv[])
2326 int res;
2327 char tmp[256];
2328 char fs[80] = "";
2329 char *context;
2330 int x;
2331 int bypass = 0;
2332 struct dundi_result dr[MAX_RESULTS];
2333 struct timeval start;
2334 if ((argc < 3) || (argc > 4))
2335 return RESULT_SHOWUSAGE;
2336 if (argc > 3) {
2337 if (!strcasecmp(argv[3], "bypass"))
2338 bypass=1;
2339 else
2340 return RESULT_SHOWUSAGE;
2342 ast_copy_string(tmp, argv[2], sizeof(tmp));
2343 context = strchr(tmp, '@');
2344 if (context) {
2345 *context = '\0';
2346 context++;
2348 start = ast_tvnow();
2349 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2351 if (res < 0)
2352 ast_cli(fd, "DUNDi lookup returned error.\n");
2353 else if (!res)
2354 ast_cli(fd, "DUNDi lookup returned no results.\n");
2355 else
2356 sort_results(dr, res);
2357 for (x=0;x<res;x++) {
2358 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));
2359 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2361 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2362 return RESULT_SUCCESS;
2365 static int dundi_do_precache(int fd, int argc, char *argv[])
2367 int res;
2368 char tmp[256];
2369 char *context;
2370 struct timeval start;
2371 if ((argc < 3) || (argc > 3))
2372 return RESULT_SHOWUSAGE;
2373 ast_copy_string(tmp, argv[2], sizeof(tmp));
2374 context = strchr(tmp, '@');
2375 if (context) {
2376 *context = '\0';
2377 context++;
2379 start = ast_tvnow();
2380 res = dundi_precache(context, tmp);
2382 if (res < 0)
2383 ast_cli(fd, "DUNDi precache returned error.\n");
2384 else if (!res)
2385 ast_cli(fd, "DUNDi precache returned no error.\n");
2386 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2387 return RESULT_SUCCESS;
2390 static int dundi_do_query(int fd, int argc, char *argv[])
2392 int res;
2393 char tmp[256];
2394 char *context;
2395 dundi_eid eid;
2396 struct dundi_entity_info dei;
2397 if ((argc < 3) || (argc > 3))
2398 return RESULT_SHOWUSAGE;
2399 if (dundi_str_to_eid(&eid, argv[2])) {
2400 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2401 return RESULT_SHOWUSAGE;
2403 ast_copy_string(tmp, argv[2], sizeof(tmp));
2404 context = strchr(tmp, '@');
2405 if (context) {
2406 *context = '\0';
2407 context++;
2409 res = dundi_query_eid(&dei, context, eid);
2410 if (res < 0)
2411 ast_cli(fd, "DUNDi Query EID returned error.\n");
2412 else if (!res)
2413 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2414 else {
2415 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2416 ast_cli(fd, "Department: %s\n", dei.orgunit);
2417 ast_cli(fd, "Organization: %s\n", dei.org);
2418 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2419 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2420 ast_cli(fd, "Country: %s\n", dei.country);
2421 ast_cli(fd, "E-mail: %s\n", dei.email);
2422 ast_cli(fd, "Phone: %s\n", dei.phone);
2423 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2425 return RESULT_SUCCESS;
2428 static int dundi_show_peer(int fd, int argc, char *argv[])
2430 struct dundi_peer *peer;
2431 struct permission *p;
2432 char *order;
2433 char eid_str[20];
2434 int x, cnt;
2436 if (argc != 4)
2437 return RESULT_SHOWUSAGE;
2438 AST_LIST_LOCK(&peers);
2439 AST_LIST_TRAVERSE(&peers, peer, list) {
2440 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2441 break;
2443 if (peer) {
2444 switch(peer->order) {
2445 case 0:
2446 order = "Primary";
2447 break;
2448 case 1:
2449 order = "Secondary";
2450 break;
2451 case 2:
2452 order = "Tertiary";
2453 break;
2454 case 3:
2455 order = "Quartiary";
2456 break;
2457 default:
2458 order = "Unknown";
2460 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2461 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2462 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2463 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2464 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2465 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2466 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2467 if (!AST_LIST_EMPTY(&peer->include))
2468 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2469 AST_LIST_TRAVERSE(&peer->include, p, list)
2470 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2471 if (!AST_LIST_EMPTY(&peer->permit))
2472 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2473 AST_LIST_TRAVERSE(&peer->permit, p, list)
2474 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2475 cnt = 0;
2476 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2477 if (peer->lookups[x]) {
2478 if (!cnt)
2479 ast_cli(fd, "Last few query times:\n");
2480 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2481 cnt++;
2484 if (cnt)
2485 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2486 } else
2487 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2488 AST_LIST_UNLOCK(&peers);
2489 return RESULT_SUCCESS;
2492 static int dundi_show_peers(int fd, int argc, char *argv[])
2494 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2495 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2496 struct dundi_peer *peer;
2497 int registeredonly=0;
2498 char avgms[20];
2499 char eid_str[20];
2500 int online_peers = 0;
2501 int offline_peers = 0;
2502 int unmonitored_peers = 0;
2503 int total_peers = 0;
2505 if ((argc != 3) && (argc != 4) && (argc != 5))
2506 return RESULT_SHOWUSAGE;
2507 if ((argc == 4)) {
2508 if (!strcasecmp(argv[3], "registered")) {
2509 registeredonly = 1;
2510 } else
2511 return RESULT_SHOWUSAGE;
2513 AST_LIST_LOCK(&peers);
2514 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2515 AST_LIST_TRAVERSE(&peers, peer, list) {
2516 char status[20];
2517 int print_line = -1;
2518 char srch[2000];
2519 total_peers++;
2520 if (registeredonly && !peer->addr.sin_addr.s_addr)
2521 continue;
2522 if (peer->maxms) {
2523 if (peer->lastms < 0) {
2524 strcpy(status, "UNREACHABLE");
2525 offline_peers++;
2527 else if (peer->lastms > peer->maxms) {
2528 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2529 offline_peers++;
2531 else if (peer->lastms) {
2532 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2533 online_peers++;
2535 else {
2536 strcpy(status, "UNKNOWN");
2537 offline_peers++;
2539 } else {
2540 strcpy(status, "Unmonitored");
2541 unmonitored_peers++;
2543 if (peer->avgms)
2544 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2545 else
2546 strcpy(avgms, "Unavail");
2547 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2548 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2549 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2551 if (argc == 5) {
2552 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2553 print_line = -1;
2554 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2555 print_line = 1;
2556 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2557 print_line = -1;
2558 } else {
2559 print_line = 0;
2563 if (print_line) {
2564 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2565 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2566 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2569 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2570 AST_LIST_UNLOCK(&peers);
2571 return RESULT_SUCCESS;
2572 #undef FORMAT
2573 #undef FORMAT2
2576 static int dundi_show_trans(int fd, int argc, char *argv[])
2578 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2579 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2580 struct dundi_transaction *trans;
2581 if (argc != 3)
2582 return RESULT_SHOWUSAGE;
2583 AST_LIST_LOCK(&peers);
2584 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2585 AST_LIST_TRAVERSE(&alltrans, trans, all) {
2586 ast_cli(fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
2587 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2589 AST_LIST_UNLOCK(&peers);
2590 return RESULT_SUCCESS;
2591 #undef FORMAT
2592 #undef FORMAT2
2595 static int dundi_show_entityid(int fd, int argc, char *argv[])
2597 char eid_str[20];
2598 if (argc != 3)
2599 return RESULT_SHOWUSAGE;
2600 AST_LIST_LOCK(&peers);
2601 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2602 AST_LIST_UNLOCK(&peers);
2603 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2604 return RESULT_SUCCESS;
2607 static int dundi_show_requests(int fd, int argc, char *argv[])
2609 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2610 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2611 struct dundi_request *req;
2612 char eidstr[20];
2613 if (argc != 3)
2614 return RESULT_SHOWUSAGE;
2615 AST_LIST_LOCK(&peers);
2616 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2617 AST_LIST_TRAVERSE(&requests, req, list) {
2618 ast_cli(fd, FORMAT, req->number, req->dcontext,
2619 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2621 AST_LIST_UNLOCK(&peers);
2622 return RESULT_SUCCESS;
2623 #undef FORMAT
2624 #undef FORMAT2
2627 /* Grok-a-dial DUNDi */
2629 static int dundi_show_mappings(int fd, int argc, char *argv[])
2631 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2632 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2633 struct dundi_mapping *map;
2634 char fs[256];
2635 if (argc != 3)
2636 return RESULT_SHOWUSAGE;
2637 AST_LIST_LOCK(&peers);
2638 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2639 AST_LIST_TRAVERSE(&mappings, map, list) {
2640 ast_cli(fd, FORMAT, map->dcontext, map->weight,
2641 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2642 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2644 AST_LIST_UNLOCK(&peers);
2645 return RESULT_SUCCESS;
2646 #undef FORMAT
2647 #undef FORMAT2
2650 static int dundi_show_precache(int fd, int argc, char *argv[])
2652 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2653 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2654 struct dundi_precache_queue *qe;
2655 int h,m,s;
2656 time_t now;
2658 if (argc != 3)
2659 return RESULT_SHOWUSAGE;
2660 time(&now);
2661 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2662 AST_LIST_LOCK(&pcq);
2663 AST_LIST_TRAVERSE(&pcq, qe, list) {
2664 s = qe->expiration - now;
2665 h = s / 3600;
2666 s = s % 3600;
2667 m = s / 60;
2668 s = s % 60;
2669 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2671 AST_LIST_UNLOCK(&pcq);
2673 return RESULT_SUCCESS;
2674 #undef FORMAT
2675 #undef FORMAT2
2678 static char debug_usage[] =
2679 "Usage: dundi debug\n"
2680 " Enables dumping of DUNDi packets for debugging purposes\n";
2682 static char no_debug_usage[] =
2683 "Usage: dundi no debug\n"
2684 " Disables dumping of DUNDi packets for debugging purposes\n";
2686 static char store_history_usage[] =
2687 "Usage: dundi store history\n"
2688 " Enables storing of DUNDi requests and times for debugging\n"
2689 "purposes\n";
2691 static char no_store_history_usage[] =
2692 "Usage: dundi no store history\n"
2693 " Disables storing of DUNDi requests and times for debugging\n"
2694 "purposes\n";
2696 static char show_peers_usage[] =
2697 "Usage: dundi show peers\n"
2698 " Lists all known DUNDi peers.\n";
2700 static char show_trans_usage[] =
2701 "Usage: dundi show trans\n"
2702 " Lists all known DUNDi transactions.\n";
2704 static char show_mappings_usage[] =
2705 "Usage: dundi show mappings\n"
2706 " Lists all known DUNDi mappings.\n";
2708 static char show_precache_usage[] =
2709 "Usage: dundi show precache\n"
2710 " Lists all known DUNDi scheduled precache updates.\n";
2712 static char show_entityid_usage[] =
2713 "Usage: dundi show entityid\n"
2714 " Displays the global entityid for this host.\n";
2716 static char show_peer_usage[] =
2717 "Usage: dundi show peer [peer]\n"
2718 " Provide a detailed description of a specifid DUNDi peer.\n";
2720 static char show_requests_usage[] =
2721 "Usage: dundi show requests\n"
2722 " Lists all known pending DUNDi requests.\n";
2724 static char lookup_usage[] =
2725 "Usage: dundi lookup <number>[@context] [bypass]\n"
2726 " Lookup the given number within the given DUNDi context\n"
2727 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2728 "keyword is specified.\n";
2730 static char precache_usage[] =
2731 "Usage: dundi precache <number>[@context]\n"
2732 " Lookup the given number within the given DUNDi context\n"
2733 "(or e164 if none is specified) and precaches the results to any\n"
2734 "upstream DUNDi push servers.\n";
2736 static char query_usage[] =
2737 "Usage: dundi query <entity>[@context]\n"
2738 " Attempts to retrieve contact information for a specific\n"
2739 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2740 "e164 if none is specified).\n";
2742 static char flush_usage[] =
2743 "Usage: dundi flush [stats]\n"
2744 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2745 "'stats' is present, clears timer statistics instead of normal\n"
2746 "operation.\n";
2748 static struct ast_cli_entry cli_dundi[] = {
2749 { { "dundi", "debug", NULL },
2750 dundi_do_debug, "Enable DUNDi debugging",
2751 debug_usage },
2753 { { "dundi", "store", "history", NULL },
2754 dundi_do_store_history, "Enable DUNDi historic records",
2755 store_history_usage },
2757 { { "dundi", "no", "store", "history", NULL },
2758 dundi_no_store_history, "Disable DUNDi historic records",
2759 no_store_history_usage },
2761 { { "dundi", "flush", NULL },
2762 dundi_flush, "Flush DUNDi cache",
2763 flush_usage },
2765 { { "dundi", "no", "debug", NULL },
2766 dundi_no_debug, "Disable DUNDi debugging",
2767 no_debug_usage },
2769 { { "dundi", "show", "peers", NULL },
2770 dundi_show_peers, "Show defined DUNDi peers",
2771 show_peers_usage },
2773 { { "dundi", "show", "trans", NULL },
2774 dundi_show_trans, "Show active DUNDi transactions",
2775 show_trans_usage },
2777 { { "dundi", "show", "entityid", NULL },
2778 dundi_show_entityid, "Display Global Entity ID",
2779 show_entityid_usage },
2781 { { "dundi", "show", "mappings", NULL },
2782 dundi_show_mappings, "Show DUNDi mappings",
2783 show_mappings_usage },
2785 { { "dundi", "show", "precache", NULL },
2786 dundi_show_precache, "Show DUNDi precache",
2787 show_precache_usage },
2789 { { "dundi", "show", "requests", NULL },
2790 dundi_show_requests, "Show DUNDi requests",
2791 show_requests_usage },
2793 { { "dundi", "show", "peer", NULL },
2794 dundi_show_peer, "Show info on a specific DUNDi peer",
2795 show_peer_usage, complete_peer_4 },
2797 { { "dundi", "lookup", NULL },
2798 dundi_do_lookup, "Lookup a number in DUNDi",
2799 lookup_usage },
2801 { { "dundi", "precache", NULL },
2802 dundi_do_precache, "Precache a number in DUNDi",
2803 precache_usage },
2805 { { "dundi", "query", NULL },
2806 dundi_do_query, "Query a DUNDi EID",
2807 query_usage },
2810 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2812 struct dundi_transaction *trans;
2813 int tid;
2815 /* Don't allow creation of transactions to non-registered peers */
2816 if (p && !p->addr.sin_addr.s_addr)
2817 return NULL;
2818 tid = get_trans_id();
2819 if (tid < 1)
2820 return NULL;
2821 trans = ast_calloc(1, sizeof(*trans));
2822 if (trans) {
2823 if (global_storehistory) {
2824 trans->start = ast_tvnow();
2825 ast_set_flag(trans, FLAG_STOREHIST);
2827 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2828 trans->autokillid = -1;
2829 if (p) {
2830 apply_peer(trans, p);
2831 if (!p->sentfullkey)
2832 ast_set_flag(trans, FLAG_SENDFULLKEY);
2834 trans->strans = tid;
2835 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
2837 return trans;
2840 static int dundi_xmit(struct dundi_packet *pack)
2842 int res;
2843 if (dundidebug)
2844 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2845 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2846 if (res < 0) {
2847 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2848 ast_inet_ntoa(pack->parent->addr.sin_addr),
2849 ntohs(pack->parent->addr.sin_port), strerror(errno));
2851 if (res > 0)
2852 res = 0;
2853 return res;
2856 static void destroy_packet(struct dundi_packet *pack, int needfree)
2858 if (pack->parent)
2859 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
2860 AST_SCHED_DEL(sched, pack->retransid);
2861 if (needfree)
2862 free(pack);
2865 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2867 struct dundi_peer *peer;
2868 int ms;
2869 int x;
2870 int cnt;
2871 char eid_str[20];
2872 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2873 AST_LIST_TRAVERSE(&peers, peer, list) {
2874 if (peer->regtrans == trans)
2875 peer->regtrans = NULL;
2876 if (peer->qualtrans == trans) {
2877 if (fromtimeout) {
2878 if (peer->lastms > -1)
2879 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2880 peer->lastms = -1;
2881 } else {
2882 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2883 if (ms < 1)
2884 ms = 1;
2885 if (ms < peer->maxms) {
2886 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2887 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2888 } else if (peer->lastms < peer->maxms) {
2889 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);
2891 peer->lastms = ms;
2893 peer->qualtrans = NULL;
2895 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2896 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2897 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2898 peer->avgms = 0;
2899 cnt = 0;
2900 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2901 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2902 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2903 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2904 peer->lookups[x] = peer->lookups[x-1];
2905 if (peer->lookups[x]) {
2906 peer->avgms += peer->lookuptimes[x];
2907 cnt++;
2910 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2911 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2912 if (peer->lookups[0]) {
2913 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2914 peer->avgms += peer->lookuptimes[0];
2915 cnt++;
2917 if (cnt)
2918 peer->avgms /= cnt;
2924 if (trans->parent) {
2925 /* Unlink from parent if appropriate */
2926 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
2927 if (AST_LIST_EMPTY(&trans->parent->trans)) {
2928 /* Wake up sleeper */
2929 if (trans->parent->pfds[1] > -1) {
2930 write(trans->parent->pfds[1], "killa!", 6);
2934 /* Unlink from all trans */
2935 AST_LIST_REMOVE(&alltrans, trans, all);
2936 destroy_packets(&trans->packets);
2937 destroy_packets(&trans->lasttrans);
2938 AST_SCHED_DEL(sched, trans->autokillid);
2939 if (trans->thread) {
2940 /* If used by a thread, mark as dead and be done */
2941 ast_set_flag(trans, FLAG_DEAD);
2942 } else
2943 free(trans);
2946 static int dundi_rexmit(const void *data)
2948 struct dundi_packet *pack = (struct dundi_packet *)data;
2949 int res;
2950 AST_LIST_LOCK(&peers);
2951 if (pack->retrans < 1) {
2952 pack->retransid = -1;
2953 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
2954 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
2955 ast_inet_ntoa(pack->parent->addr.sin_addr),
2956 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2957 destroy_trans(pack->parent, 1);
2958 res = 0;
2959 } else {
2960 /* Decrement retransmission, try again */
2961 pack->retrans--;
2962 dundi_xmit(pack);
2963 res = 1;
2965 AST_LIST_UNLOCK(&peers);
2966 return res;
2969 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2971 struct dundi_packet *pack;
2972 int res;
2973 int len;
2974 char eid_str[20];
2975 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2976 /* Reserve enough space for encryption */
2977 if (ast_test_flag(trans, FLAG_ENCRYPT))
2978 len += 384;
2979 pack = ast_calloc(1, len);
2980 if (pack) {
2981 pack->h = (struct dundi_hdr *)(pack->data);
2982 if (cmdresp != DUNDI_COMMAND_ACK) {
2983 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
2984 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
2985 AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
2987 pack->parent = trans;
2988 pack->h->strans = htons(trans->strans);
2989 pack->h->dtrans = htons(trans->dtrans);
2990 pack->h->iseqno = trans->iseqno;
2991 pack->h->oseqno = trans->oseqno;
2992 pack->h->cmdresp = cmdresp;
2993 pack->datalen = sizeof(struct dundi_hdr);
2994 if (ied) {
2995 memcpy(pack->h->ies, ied->buf, ied->pos);
2996 pack->datalen += ied->pos;
2998 if (final) {
2999 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3000 ast_set_flag(trans, FLAG_FINAL);
3002 pack->h->cmdflags = flags;
3003 if (cmdresp != DUNDI_COMMAND_ACK) {
3004 trans->oseqno++;
3005 trans->oseqno = trans->oseqno % 256;
3007 trans->aseqno = trans->iseqno;
3008 /* If we have their public key, encrypt */
3009 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3010 switch(cmdresp) {
3011 case DUNDI_COMMAND_REGREQ:
3012 case DUNDI_COMMAND_REGRESPONSE:
3013 case DUNDI_COMMAND_DPDISCOVER:
3014 case DUNDI_COMMAND_DPRESPONSE:
3015 case DUNDI_COMMAND_EIDQUERY:
3016 case DUNDI_COMMAND_EIDRESPONSE:
3017 case DUNDI_COMMAND_PRECACHERQ:
3018 case DUNDI_COMMAND_PRECACHERP:
3019 if (dundidebug)
3020 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3021 res = dundi_encrypt(trans, pack);
3022 break;
3023 default:
3024 res = 0;
3026 } else
3027 res = 0;
3028 if (!res)
3029 res = dundi_xmit(pack);
3030 if (res)
3031 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3033 if (cmdresp == DUNDI_COMMAND_ACK)
3034 free(pack);
3035 return res;
3037 return -1;
3040 static int do_autokill(const void *data)
3042 struct dundi_transaction *trans = (struct dundi_transaction *)data;
3043 char eid_str[20];
3044 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3045 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3046 trans->autokillid = -1;
3047 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3048 return 0;
3051 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3053 struct dundi_peer *p;
3054 if (!dundi_eid_cmp(eid, us)) {
3055 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3056 return;
3058 AST_LIST_LOCK(&peers);
3059 AST_LIST_TRAVERSE(&peers, p, list) {
3060 if (!dundi_eid_cmp(&p->eid, eid)) {
3061 if (has_permission(&p->include, context))
3062 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3063 else
3064 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3065 break;
3068 if (!p)
3069 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3070 AST_LIST_UNLOCK(&peers);
3073 static int dundi_discover(struct dundi_transaction *trans)
3075 struct dundi_ie_data ied;
3076 int x;
3077 if (!trans->parent) {
3078 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3079 return -1;
3081 memset(&ied, 0, sizeof(ied));
3082 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3083 if (!dundi_eid_zero(&trans->us_eid))
3084 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3085 for (x=0;x<trans->eidcount;x++)
3086 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3087 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3088 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3089 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3090 if (trans->parent->cbypass)
3091 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3092 if (trans->autokilltimeout)
3093 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3094 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3097 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3099 struct dundi_ie_data ied;
3100 int x, res;
3101 int max = 999999;
3102 int expiration = dundi_cache_time;
3103 int ouranswers=0;
3104 dundi_eid *avoid[1] = { NULL, };
3105 int direct[1] = { 0, };
3106 struct dundi_result dr[MAX_RESULTS];
3107 struct dundi_hint_metadata hmd;
3108 if (!trans->parent) {
3109 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3110 return -1;
3112 memset(&hmd, 0, sizeof(hmd));
3113 memset(&dr, 0, sizeof(dr));
3114 /* Look up the answers we're going to include */
3115 for (x=0;x<mapcount;x++)
3116 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3117 if (ouranswers < 0)
3118 ouranswers = 0;
3119 for (x=0;x<ouranswers;x++) {
3120 if (dr[x].weight < max)
3121 max = dr[x].weight;
3123 if (max) {
3124 /* If we do not have a canonical result, keep looking */
3125 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);
3126 if (res > 0) {
3127 /* Append answer in result */
3128 ouranswers += res;
3132 if (ouranswers > 0) {
3133 *foundanswers += ouranswers;
3134 memset(&ied, 0, sizeof(ied));
3135 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3136 if (!dundi_eid_zero(&trans->us_eid))
3137 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3138 for (x=0;x<trans->eidcount;x++)
3139 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3140 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3141 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3142 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3143 for (x=0;x<ouranswers;x++) {
3144 /* Add answers */
3145 if (dr[x].expiration && (expiration > dr[x].expiration))
3146 expiration = dr[x].expiration;
3147 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3149 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3150 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3151 if (trans->autokilltimeout)
3152 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3153 if (expiration < *minexp)
3154 *minexp = expiration;
3155 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3156 } else {
3157 /* Oops, nothing to send... */
3158 destroy_trans(trans, 0);
3159 return 0;
3163 static int dundi_query(struct dundi_transaction *trans)
3165 struct dundi_ie_data ied;
3166 int x;
3167 if (!trans->parent) {
3168 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3169 return -1;
3171 memset(&ied, 0, sizeof(ied));
3172 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3173 if (!dundi_eid_zero(&trans->us_eid))
3174 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3175 for (x=0;x<trans->eidcount;x++)
3176 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3177 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3178 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3179 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3180 if (trans->autokilltimeout)
3181 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3182 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3185 static int discover_transactions(struct dundi_request *dr)
3187 struct dundi_transaction *trans;
3188 AST_LIST_LOCK(&peers);
3189 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3190 dundi_discover(trans);
3192 AST_LIST_UNLOCK(&peers);
3193 return 0;
3196 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3198 struct dundi_transaction *trans;
3200 /* Mark all as "in thread" so they don't disappear */
3201 AST_LIST_LOCK(&peers);
3202 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3203 if (trans->thread)
3204 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3205 trans->thread = 1;
3207 AST_LIST_UNLOCK(&peers);
3209 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3210 if (!ast_test_flag(trans, FLAG_DEAD))
3211 precache_trans(trans, maps, mapcount, expiration, foundanswers);
3214 /* Cleanup any that got destroyed in the mean time */
3215 AST_LIST_LOCK(&peers);
3216 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
3217 trans->thread = 0;
3218 if (ast_test_flag(trans, FLAG_DEAD)) {
3219 ast_log(LOG_DEBUG, "Our transaction went away!\n");
3220 /* This is going to remove the transaction from the dundi_request's list, as well
3221 * as the global transactions list */
3222 destroy_trans(trans, 0);
3225 AST_LIST_TRAVERSE_SAFE_END
3226 AST_LIST_UNLOCK(&peers);
3228 return 0;
3231 static int query_transactions(struct dundi_request *dr)
3233 struct dundi_transaction *trans;
3235 AST_LIST_LOCK(&peers);
3236 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3237 dundi_query(trans);
3239 AST_LIST_UNLOCK(&peers);
3241 return 0;
3244 static int optimize_transactions(struct dundi_request *dr, int order)
3246 /* Minimize the message propagation through DUNDi by
3247 alerting the network to hops which should be not be considered */
3248 struct dundi_transaction *trans;
3249 struct dundi_peer *peer;
3250 dundi_eid tmp;
3251 int x;
3252 int needpush;
3254 AST_LIST_LOCK(&peers);
3255 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3256 /* Pop off the true root */
3257 if (trans->eidcount) {
3258 tmp = trans->eids[--trans->eidcount];
3259 needpush = 1;
3260 } else {
3261 tmp = trans->us_eid;
3262 needpush = 0;
3265 AST_LIST_TRAVERSE(&peers, peer, list) {
3266 if (has_permission(&peer->include, dr->dcontext) &&
3267 dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
3268 (peer->order <= order)) {
3269 /* For each other transaction, make sure we don't
3270 ask this EID about the others if they're not
3271 already in the list */
3272 if (!dundi_eid_cmp(&tmp, &peer->eid))
3273 x = -1;
3274 else {
3275 for (x=0;x<trans->eidcount;x++) {
3276 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
3277 break;
3280 if (x == trans->eidcount) {
3281 /* Nope not in the list, if needed, add us at the end since we're the source */
3282 if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3283 trans->eids[trans->eidcount++] = peer->eid;
3284 /* Need to insert the real root (or us) at the bottom now as
3285 a requirement now. */
3286 needpush = 1;
3291 /* If necessary, push the true root back on the end */
3292 if (needpush)
3293 trans->eids[trans->eidcount++] = tmp;
3295 AST_LIST_UNLOCK(&peers);
3297 return 0;
3300 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3302 struct dundi_transaction *trans;
3303 int x;
3304 char eid_str[20];
3305 char eid_str2[20];
3307 /* Ignore if not registered */
3308 if (!p->addr.sin_addr.s_addr)
3309 return 0;
3310 if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3311 return 0;
3312 if (ast_strlen_zero(dr->number))
3313 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);
3314 else
3315 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);
3316 trans = create_transaction(p);
3317 if (!trans)
3318 return -1;
3319 trans->parent = dr;
3320 trans->ttl = ttl;
3321 for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
3322 trans->eids[x] = *avoid[x];
3323 trans->eidcount = x;
3324 AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
3326 return 0;
3329 static void cancel_request(struct dundi_request *dr)
3331 struct dundi_transaction *trans;
3333 AST_LIST_LOCK(&peers);
3334 while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
3335 /* Orphan transaction from request */
3336 trans->parent = NULL;
3337 /* Send final cancel */
3338 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3340 AST_LIST_UNLOCK(&peers);
3343 static void abort_request(struct dundi_request *dr)
3345 struct dundi_transaction *trans;
3347 AST_LIST_LOCK(&peers);
3348 while ((trans = AST_LIST_FIRST(&dr->trans))) {
3349 /* This will remove the transaction from the list */
3350 destroy_trans(trans, 0);
3352 AST_LIST_UNLOCK(&peers);
3355 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[])
3357 struct dundi_peer *p;
3358 int x;
3359 int res;
3360 int pass;
3361 int allowconnect;
3362 char eid_str[20];
3363 AST_LIST_LOCK(&peers);
3364 AST_LIST_TRAVERSE(&peers, p, list) {
3365 if (modeselect == 1) {
3366 /* Send the precache to push upstreams only! */
3367 pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
3368 allowconnect = 1;
3369 } else {
3370 /* Normal lookup / EID query */
3371 pass = has_permission(&p->include, dr->dcontext);
3372 allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
3374 if (skip) {
3375 if (!dundi_eid_cmp(skip, &p->eid))
3376 pass = 0;
3378 if (pass) {
3379 if (p->order <= order) {
3380 /* Check order first, then check cache, regardless of
3381 omissions, this gets us more likely to not have an
3382 affected answer. */
3383 if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
3384 res = 0;
3385 /* Make sure we haven't already seen it and that it won't
3386 affect our answer */
3387 for (x=0;avoid[x];x++) {
3388 if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
3389 /* If not a direct connection, it affects our answer */
3390 if (directs && !directs[x])
3391 ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED);
3392 break;
3395 /* Make sure we can ask */
3396 if (allowconnect) {
3397 if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
3398 /* Check for a matching or 0 cache entry */
3399 append_transaction(dr, p, ttl, avoid);
3400 } else
3401 ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
3404 *foundcache |= res;
3405 } else if (!*skipped || (p->order < *skipped))
3406 *skipped = p->order;
3409 AST_LIST_UNLOCK(&peers);
3412 static int register_request(struct dundi_request *dr, struct dundi_request **pending)
3414 struct dundi_request *cur;
3415 int res=0;
3416 char eid_str[20];
3417 AST_LIST_LOCK(&peers);
3418 AST_LIST_TRAVERSE(&requests, cur, list) {
3419 if (option_debug)
3420 ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
3421 dr->dcontext, dr->number);
3422 if (!strcasecmp(cur->dcontext, dr->dcontext) &&
3423 !strcasecmp(cur->number, dr->number) &&
3424 (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
3425 ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n",
3426 cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
3427 *pending = cur;
3428 res = 1;
3429 break;
3432 if (!res) {
3433 ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n",
3434 dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
3435 /* Go ahead and link us in since nobody else is searching for this */
3436 AST_LIST_INSERT_HEAD(&requests, dr, list);
3437 *pending = NULL;
3439 AST_LIST_UNLOCK(&peers);
3440 return res;
3443 static void unregister_request(struct dundi_request *dr)
3445 AST_LIST_LOCK(&peers);
3446 AST_LIST_REMOVE(&requests, dr, list);
3447 AST_LIST_UNLOCK(&peers);
3450 static int check_request(struct dundi_request *dr)
3452 struct dundi_request *cur;
3454 AST_LIST_LOCK(&peers);
3455 AST_LIST_TRAVERSE(&requests, cur, list) {
3456 if (cur == dr)
3457 break;
3459 AST_LIST_UNLOCK(&peers);
3461 return cur ? 1 : 0;
3464 static unsigned long avoid_crc32(dundi_eid *avoid[])
3466 /* Idea is that we're calculating a checksum which is independent of
3467 the order that the EID's are listed in */
3468 unsigned long acrc32 = 0;
3469 int x;
3470 for (x=0;avoid[x];x++) {
3471 /* Order doesn't matter */
3472 if (avoid[x+1]) {
3473 acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
3476 return acrc32;
3479 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[])
3481 int res;
3482 struct dundi_request dr, *pending;
3483 dundi_eid *rooteid=NULL;
3484 int x;
3485 int ttlms;
3486 int ms;
3487 int foundcache;
3488 int skipped=0;
3489 int order=0;
3490 char eid_str[20];
3491 struct timeval start;
3493 /* Don't do anthing for a hungup channel */
3494 if (chan && chan->_softhangup)
3495 return 0;
3497 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3499 for (x=0;avoid[x];x++)
3500 rooteid = avoid[x];
3501 /* Now perform real check */
3502 memset(&dr, 0, sizeof(dr));
3503 if (pipe(dr.pfds)) {
3504 ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
3505 return -1;
3507 dr.dr = result;
3508 dr.hmd = hmd;
3509 dr.maxcount = maxret;
3510 dr.expiration = *expiration;
3511 dr.cbypass = cbypass;
3512 dr.crc32 = avoid_crc32(avoid);
3513 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
3514 ast_copy_string(dr.number, number, sizeof(dr.number));
3515 if (rooteid)
3516 dr.root_eid = *rooteid;
3517 res = register_request(&dr, &pending);
3518 if (res) {
3519 /* Already a request */
3520 if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
3521 /* This is on behalf of someone else. Go ahead and close this out since
3522 they'll get their answer anyway. */
3523 ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
3524 dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
3525 close(dr.pfds[0]);
3526 close(dr.pfds[1]);
3527 return -2;
3528 } else {
3529 /* Wait for the cache to populate */
3530 ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
3531 dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
3532 start = ast_tvnow();
3533 while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) {
3534 /* XXX Would be nice to have a way to poll/select here XXX */
3535 /* XXX this is a busy wait loop!!! */
3536 usleep(1);
3538 /* Continue on as normal, our cache should kick in */
3541 /* Create transactions */
3542 do {
3543 order = skipped;
3544 skipped = 0;
3545 foundcache = 0;
3546 build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
3547 } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
3548 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3549 do this earlier because we didn't know if we were going to have transactions
3550 or not. */
3551 if (!ttl) {
3552 ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
3553 abort_request(&dr);
3554 unregister_request(&dr);
3555 close(dr.pfds[0]);
3556 close(dr.pfds[1]);
3557 return 0;
3560 /* Optimize transactions */
3561 optimize_transactions(&dr, order);
3562 /* Actually perform transactions */
3563 discover_transactions(&dr);
3564 /* Wait for transaction to come back */
3565 start = ast_tvnow();
3566 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) {
3567 ms = 100;
3568 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
3570 if (chan && chan->_softhangup)
3571 ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
3572 cancel_request(&dr);
3573 unregister_request(&dr);
3574 res = dr.respcount;
3575 *expiration = dr.expiration;
3576 close(dr.pfds[0]);
3577 close(dr.pfds[1]);
3578 return res;
3581 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
3583 struct dundi_hint_metadata hmd;
3584 dundi_eid *avoid[1] = { NULL, };
3585 int direct[1] = { 0, };
3586 int expiration = dundi_cache_time;
3587 memset(&hmd, 0, sizeof(hmd));
3588 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
3589 return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
3592 static void reschedule_precache(const char *number, const char *context, int expiration)
3594 int len;
3595 struct dundi_precache_queue *qe, *prev;
3597 AST_LIST_LOCK(&pcq);
3598 AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq, qe, list) {
3599 if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
3600 AST_LIST_REMOVE_CURRENT(&pcq, list);
3601 break;
3604 AST_LIST_TRAVERSE_SAFE_END
3605 if (!qe) {
3606 len = sizeof(*qe);
3607 len += strlen(number) + 1;
3608 len += strlen(context) + 1;
3609 if (!(qe = ast_calloc(1, len))) {
3610 AST_LIST_UNLOCK(&pcq);
3611 return;
3613 strcpy(qe->number, number);
3614 qe->context = qe->number + strlen(number) + 1;
3615 strcpy(qe->context, context);
3617 time(&qe->expiration);
3618 qe->expiration += expiration;
3619 if ((prev = AST_LIST_FIRST(&pcq))) {
3620 while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
3621 prev = AST_LIST_NEXT(prev, list);
3622 AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
3623 } else
3624 AST_LIST_INSERT_HEAD(&pcq, qe, list);
3625 AST_LIST_UNLOCK(&pcq);
3628 static void dundi_precache_full(void)
3630 struct dundi_mapping *cur;
3631 struct ast_context *con;
3632 struct ast_exten *e;
3634 AST_LIST_TRAVERSE(&mappings, cur, list) {
3635 ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
3636 ast_rdlock_contexts();
3637 con = ast_walk_contexts(NULL);
3638 while (con) {
3639 if (!strcasecmp(cur->lcontext, ast_get_context_name(con))) {
3640 /* Found the match, now queue them all up */
3641 ast_lock_context(con);
3642 e = ast_walk_context_extensions(con, NULL);
3643 while (e) {
3644 reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
3645 e = ast_walk_context_extensions(con, e);
3647 ast_unlock_context(con);
3649 con = ast_walk_contexts(con);
3651 ast_unlock_contexts();
3655 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
3657 struct dundi_request dr;
3658 struct dundi_hint_metadata hmd;
3659 struct dundi_result dr2[MAX_RESULTS];
3660 struct timeval start;
3661 struct dundi_mapping *maps = NULL, *cur;
3662 int nummaps = 0;
3663 int foundanswers;
3664 int foundcache, skipped, ttlms, ms;
3665 if (!context)
3666 context = "e164";
3667 ast_log(LOG_DEBUG, "Precache internal (%s@%s)!\n", number, context);
3669 AST_LIST_LOCK(&peers);
3670 AST_LIST_TRAVERSE(&mappings, cur, list) {
3671 if (!strcasecmp(cur->dcontext, context))
3672 nummaps++;
3674 if (nummaps) {
3675 maps = alloca(nummaps * sizeof(*maps));
3676 nummaps = 0;
3677 if (maps) {
3678 AST_LIST_TRAVERSE(&mappings, cur, list) {
3679 if (!strcasecmp(cur->dcontext, context))
3680 maps[nummaps++] = *cur;
3684 AST_LIST_UNLOCK(&peers);
3685 if (!nummaps || !maps)
3686 return -1;
3687 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3688 memset(&dr2, 0, sizeof(dr2));
3689 memset(&dr, 0, sizeof(dr));
3690 memset(&hmd, 0, sizeof(hmd));
3691 dr.dr = dr2;
3692 ast_copy_string(dr.number, number, sizeof(dr.number));
3693 ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
3694 dr.maxcount = MAX_RESULTS;
3695 dr.expiration = dundi_cache_time;
3696 dr.hmd = &hmd;
3697 dr.pfds[0] = dr.pfds[1] = -1;
3698 pipe(dr.pfds);
3699 build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
3700 optimize_transactions(&dr, 0);
3701 foundanswers = 0;
3702 precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
3703 if (foundanswers) {
3704 if (dr.expiration > 0)
3705 reschedule_precache(dr.number, dr.dcontext, dr.expiration);
3706 else
3707 ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
3709 start = ast_tvnow();
3710 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
3711 if (dr.pfds[0] > -1) {
3712 ms = 100;
3713 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
3714 } else
3715 usleep(1);
3717 cancel_request(&dr);
3718 if (dr.pfds[0] > -1) {
3719 close(dr.pfds[0]);
3720 close(dr.pfds[1]);
3722 return 0;
3725 int dundi_precache(const char *context, const char *number)
3727 dundi_eid *avoid[1] = { NULL, };
3728 return dundi_precache_internal(context, number, dundi_ttl, avoid);
3731 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[])
3733 int res;
3734 struct dundi_request dr;
3735 dundi_eid *rooteid=NULL;
3736 int x;
3737 int ttlms;
3738 int skipped=0;
3739 int foundcache=0;
3740 struct timeval start;
3742 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3744 for (x=0;avoid[x];x++)
3745 rooteid = avoid[x];
3746 /* Now perform real check */
3747 memset(&dr, 0, sizeof(dr));
3748 dr.hmd = hmd;
3749 dr.dei = dei;
3750 dr.pfds[0] = dr.pfds[1] = -1;
3751 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
3752 memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
3753 if (rooteid)
3754 dr.root_eid = *rooteid;
3755 /* Create transactions */
3756 build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
3758 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3759 do this earlier because we didn't know if we were going to have transactions
3760 or not. */
3761 if (!ttl) {
3762 ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
3763 return 0;
3766 /* Optimize transactions */
3767 optimize_transactions(&dr, 9999);
3768 /* Actually perform transactions */
3769 query_transactions(&dr);
3770 /* Wait for transaction to come back */
3771 start = ast_tvnow();
3772 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
3773 usleep(1);
3774 res = dr.respcount;
3775 return res;
3778 int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
3780 dundi_eid *avoid[1] = { NULL, };
3781 struct dundi_hint_metadata hmd;
3782 memset(&hmd, 0, sizeof(hmd));
3783 return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
3786 static int dundifunc_read(struct ast_channel *chan, char *cmd, char *num, char *buf, size_t len)
3788 char *context;
3789 char *opts;
3790 int results;
3791 int x;
3792 int bypass = 0;
3793 struct ast_module_user *u;
3794 struct dundi_result dr[MAX_RESULTS];
3796 buf[0] = '\0';
3798 if (ast_strlen_zero(num)) {
3799 ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
3800 return -1;
3803 u = ast_module_user_add(chan);
3805 context = strchr(num, '|');
3806 if (context) {
3807 *context++ = '\0';
3808 opts = strchr(context, '|');
3809 if (opts) {
3810 *opts++ = '\0';
3811 if (strchr(opts, 'b'))
3812 bypass = 1;
3816 if (ast_strlen_zero(context))
3817 context = "e164";
3819 results = dundi_lookup(dr, MAX_RESULTS, NULL, context, num, bypass);
3820 if (results > 0) {
3821 sort_results(dr, results);
3822 for (x = 0; x < results; x++) {
3823 if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
3824 snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
3825 break;
3830 ast_module_user_remove(u);
3832 return 0;
3835 /*! DUNDILOOKUP
3836 * \ingroup functions
3839 static struct ast_custom_function dundi_function = {
3840 .name = "DUNDILOOKUP",
3841 .synopsis = "Do a DUNDi lookup of a phone number.",
3842 .syntax = "DUNDILOOKUP(number[|context[|options]])",
3843 .desc = "This will do a DUNDi lookup of the given phone number.\n"
3844 "If no context is given, the default will be e164. The result of\n"
3845 "this function will the Technology/Resource found in the DUNDi\n"
3846 "lookup. If no results were found, the result will be blank.\n"
3847 "If the 'b' option is specified, the internal DUNDi cache will\n"
3848 "be bypassed.\n",
3849 .read = dundifunc_read,
3852 static void mark_peers(void)
3854 struct dundi_peer *peer;
3855 AST_LIST_LOCK(&peers);
3856 AST_LIST_TRAVERSE(&peers, peer, list) {
3857 peer->dead = 1;
3859 AST_LIST_UNLOCK(&peers);
3862 static void mark_mappings(void)
3864 struct dundi_mapping *map;
3866 AST_LIST_LOCK(&peers);
3867 AST_LIST_TRAVERSE(&mappings, map, list) {
3868 map->dead = 1;
3870 AST_LIST_UNLOCK(&peers);
3873 static void destroy_permissions(struct permissionlist *permlist)
3875 struct permission *perm;
3877 while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
3878 free(perm);
3881 static void destroy_peer(struct dundi_peer *peer)
3883 AST_SCHED_DEL(sched, peer->registerid);
3884 if (peer->regtrans)
3885 destroy_trans(peer->regtrans, 0);
3886 AST_SCHED_DEL(sched, peer->qualifyid);
3887 destroy_permissions(&peer->permit);
3888 destroy_permissions(&peer->include);
3889 free(peer);
3892 static void destroy_map(struct dundi_mapping *map)
3894 free(map);
3897 static void prune_peers(void)
3899 struct dundi_peer *peer;
3901 AST_LIST_LOCK(&peers);
3902 AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, peer, list) {
3903 if (peer->dead) {
3904 AST_LIST_REMOVE_CURRENT(&peers, list);
3905 destroy_peer(peer);
3908 AST_LIST_TRAVERSE_SAFE_END
3909 AST_LIST_UNLOCK(&peers);
3912 static void prune_mappings(void)
3914 struct dundi_mapping *map;
3916 AST_LIST_LOCK(&peers);
3917 AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings, map, list) {
3918 if (map->dead) {
3919 AST_LIST_REMOVE_CURRENT(&mappings, list);
3920 destroy_map(map);
3923 AST_LIST_TRAVERSE_SAFE_END
3924 AST_LIST_UNLOCK(&peers);
3927 static void append_permission(struct permissionlist *permlist, char *s, int allow)
3929 struct permission *perm;
3931 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
3932 return;
3934 strcpy(perm->name, s);
3935 perm->allow = allow;
3937 AST_LIST_INSERT_TAIL(permlist, perm, list);
3940 #define MAX_OPTS 128
3942 static void build_mapping(char *name, char *value)
3944 char *t, *fields[MAX_OPTS];
3945 struct dundi_mapping *map;
3946 int x;
3947 int y;
3949 t = ast_strdupa(value);
3951 AST_LIST_TRAVERSE(&mappings, map, list) {
3952 /* Find a double match */
3953 if (!strcasecmp(map->dcontext, name) &&
3954 (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
3955 (!value[strlen(map->lcontext)] ||
3956 (value[strlen(map->lcontext)] == ','))))
3957 break;
3959 if (!map) {
3960 if (!(map = ast_calloc(1, sizeof(*map))))
3961 return;
3962 AST_LIST_INSERT_HEAD(&mappings, map, list);
3963 map->dead = 1;
3965 map->options = 0;
3966 memset(fields, 0, sizeof(fields));
3967 x = 0;
3968 while (t && x < MAX_OPTS) {
3969 fields[x++] = t;
3970 t = strchr(t, ',');
3971 if (t) {
3972 *t = '\0';
3973 t++;
3975 } /* Russell was here, arrrr! */
3976 if ((x == 1) && ast_strlen_zero(fields[0])) {
3977 /* Placeholder mapping */
3978 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
3979 map->dead = 0;
3980 } else if (x >= 4) {
3981 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
3982 ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
3983 if ((sscanf(fields[1], "%d", &map->weight) == 1) && (map->weight >= 0) && (map->weight < 60000)) {
3984 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
3985 if ((map->tech = str2tech(fields[2]))) {
3986 map->dead = 0;
3988 } else {
3989 ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
3991 for (y = 4;y < x; y++) {
3992 if (!strcasecmp(fields[y], "nounsolicited"))
3993 map->options |= DUNDI_FLAG_NOUNSOLICITED;
3994 else if (!strcasecmp(fields[y], "nocomunsolicit"))
3995 map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
3996 else if (!strcasecmp(fields[y], "residential"))
3997 map->options |= DUNDI_FLAG_RESIDENTIAL;
3998 else if (!strcasecmp(fields[y], "commercial"))
3999 map->options |= DUNDI_FLAG_COMMERCIAL;
4000 else if (!strcasecmp(fields[y], "mobile"))
4001 map->options |= DUNDI_FLAG_MOBILE;
4002 else if (!strcasecmp(fields[y], "nopartial"))
4003 map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
4004 else
4005 ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
4007 } else
4008 ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
4011 /* \note Called with the peers list already locked */
4012 static int do_register(const void *data)
4014 struct dundi_ie_data ied;
4015 struct dundi_peer *peer = (struct dundi_peer *)data;
4016 char eid_str[20];
4017 char eid_str2[20];
4018 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));
4019 peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
4020 /* Destroy old transaction if there is one */
4021 if (peer->regtrans)
4022 destroy_trans(peer->regtrans, 0);
4023 peer->regtrans = create_transaction(peer);
4024 if (peer->regtrans) {
4025 ast_set_flag(peer->regtrans, FLAG_ISREG);
4026 memset(&ied, 0, sizeof(ied));
4027 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
4028 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
4029 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
4030 dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
4032 } else
4033 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));
4035 return 0;
4038 static int do_qualify(const void *data)
4040 struct dundi_peer *peer = (struct dundi_peer *)data;
4041 peer->qualifyid = -1;
4042 qualify_peer(peer, 0);
4043 return 0;
4046 static void qualify_peer(struct dundi_peer *peer, int schedonly)
4048 int when;
4049 AST_SCHED_DEL(sched, peer->qualifyid);
4050 if (peer->qualtrans)
4051 destroy_trans(peer->qualtrans, 0);
4052 peer->qualtrans = NULL;
4053 if (peer->maxms > 0) {
4054 when = 60000;
4055 if (peer->lastms < 0)
4056 when = 10000;
4057 if (schedonly)
4058 when = 5000;
4059 peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
4060 if (!schedonly)
4061 peer->qualtrans = create_transaction(peer);
4062 if (peer->qualtrans) {
4063 peer->qualtx = ast_tvnow();
4064 ast_set_flag(peer->qualtrans, FLAG_ISQUAL);
4065 dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
4069 static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
4071 char data[256];
4072 char *c;
4073 int port, expire;
4074 char eid_str[20];
4075 dundi_eid_to_str(eid_str, sizeof(eid_str), eid);
4076 if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
4077 c = strchr(data, ':');
4078 if (c) {
4079 *c = '\0';
4080 c++;
4081 if (sscanf(c, "%d:%d", &port, &expire) == 2) {
4082 /* Got it! */
4083 inet_aton(data, &peer->addr.sin_addr);
4084 peer->addr.sin_family = AF_INET;
4085 peer->addr.sin_port = htons(port);
4086 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
4093 static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
4095 struct dundi_peer *peer;
4096 struct ast_hostent he;
4097 struct hostent *hp;
4098 dundi_eid testeid;
4099 int needregister=0;
4100 char eid_str[20];
4102 AST_LIST_LOCK(&peers);
4103 AST_LIST_TRAVERSE(&peers, peer, list) {
4104 if (!dundi_eid_cmp(&peer->eid, eid)) {
4105 break;
4108 if (!peer) {
4109 /* Add us into the list */
4110 if (!(peer = ast_calloc(1, sizeof(*peer)))) {
4111 AST_LIST_UNLOCK(&peers);
4112 return;
4114 peer->registerid = -1;
4115 peer->registerexpire = -1;
4116 peer->qualifyid = -1;
4117 peer->addr.sin_family = AF_INET;
4118 peer->addr.sin_port = htons(DUNDI_PORT);
4119 populate_addr(peer, eid);
4120 AST_LIST_INSERT_HEAD(&peers, peer, list);
4122 peer->dead = 0;
4123 peer->eid = *eid;
4124 peer->us_eid = global_eid;
4125 destroy_permissions(&peer->permit);
4126 destroy_permissions(&peer->include);
4127 AST_SCHED_DEL(sched, peer->registerid);
4128 for (; v; v = v->next) {
4129 if (!strcasecmp(v->name, "inkey")) {
4130 ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
4131 } else if (!strcasecmp(v->name, "outkey")) {
4132 ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
4133 } else if (!strcasecmp(v->name, "host")) {
4134 if (!strcasecmp(v->value, "dynamic")) {
4135 peer->dynamic = 1;
4136 } else {
4137 hp = ast_gethostbyname(v->value, &he);
4138 if (hp) {
4139 memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
4140 peer->dynamic = 0;
4141 } else {
4142 ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
4143 peer->dead = 1;
4146 } else if (!strcasecmp(v->name, "ustothem")) {
4147 if (!dundi_str_to_eid(&testeid, v->value))
4148 peer->us_eid = testeid;
4149 else
4150 ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
4151 } else if (!strcasecmp(v->name, "include")) {
4152 append_permission(&peer->include, v->value, 1);
4153 } else if (!strcasecmp(v->name, "permit")) {
4154 append_permission(&peer->permit, v->value, 1);
4155 } else if (!strcasecmp(v->name, "noinclude")) {
4156 append_permission(&peer->include, v->value, 0);
4157 } else if (!strcasecmp(v->name, "deny")) {
4158 append_permission(&peer->permit, v->value, 0);
4159 } else if (!strcasecmp(v->name, "register")) {
4160 needregister = ast_true(v->value);
4161 } else if (!strcasecmp(v->name, "order")) {
4162 if (!strcasecmp(v->value, "primary"))
4163 peer->order = 0;
4164 else if (!strcasecmp(v->value, "secondary"))
4165 peer->order = 1;
4166 else if (!strcasecmp(v->value, "tertiary"))
4167 peer->order = 2;
4168 else if (!strcasecmp(v->value, "quartiary"))
4169 peer->order = 3;
4170 else {
4171 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);
4173 } else if (!strcasecmp(v->name, "qualify")) {
4174 if (!strcasecmp(v->value, "no")) {
4175 peer->maxms = 0;
4176 } else if (!strcasecmp(v->value, "yes")) {
4177 peer->maxms = DEFAULT_MAXMS;
4178 } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
4179 ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
4180 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
4181 peer->maxms = 0;
4183 } else if (!strcasecmp(v->name, "model")) {
4184 if (!strcasecmp(v->value, "inbound"))
4185 peer->model = DUNDI_MODEL_INBOUND;
4186 else if (!strcasecmp(v->value, "outbound"))
4187 peer->model = DUNDI_MODEL_OUTBOUND;
4188 else if (!strcasecmp(v->value, "symmetric"))
4189 peer->model = DUNDI_MODEL_SYMMETRIC;
4190 else if (!strcasecmp(v->value, "none"))
4191 peer->model = 0;
4192 else {
4193 ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4194 v->value, v->lineno);
4196 } else if (!strcasecmp(v->name, "precache")) {
4197 if (!strcasecmp(v->value, "inbound"))
4198 peer->pcmodel = DUNDI_MODEL_INBOUND;
4199 else if (!strcasecmp(v->value, "outbound"))
4200 peer->pcmodel = DUNDI_MODEL_OUTBOUND;
4201 else if (!strcasecmp(v->value, "symmetric"))
4202 peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
4203 else if (!strcasecmp(v->value, "none"))
4204 peer->pcmodel = 0;
4205 else {
4206 ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4207 v->value, v->lineno);
4211 (*globalpcmode) |= peer->pcmodel;
4212 if (!peer->model && !peer->pcmodel) {
4213 ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
4214 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4215 peer->dead = 1;
4216 } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4217 ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
4218 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4219 peer->dead = 1;
4220 } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4221 ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
4222 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4223 peer->dead = 1;
4224 } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4225 ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
4226 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4227 } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4228 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",
4229 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4230 } else {
4231 if (needregister) {
4232 peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
4234 qualify_peer(peer, 1);
4236 AST_LIST_UNLOCK(&peers);
4239 static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
4241 struct dundi_result results[MAX_RESULTS];
4242 int res;
4243 int x;
4244 int found = 0;
4245 if (!strncasecmp(context, "macro-", 6)) {
4246 if (!chan) {
4247 ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
4248 return -1;
4250 /* If done as a macro, use macro extension */
4251 if (!strcasecmp(exten, "s")) {
4252 exten = pbx_builtin_getvar_helper(chan, "ARG1");
4253 if (ast_strlen_zero(exten))
4254 exten = chan->macroexten;
4255 if (ast_strlen_zero(exten))
4256 exten = chan->exten;
4257 if (ast_strlen_zero(exten)) {
4258 ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
4259 return -1;
4262 if (ast_strlen_zero(data))
4263 data = "e164";
4264 } else {
4265 if (ast_strlen_zero(data))
4266 data = context;
4268 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4269 for (x=0;x<res;x++) {
4270 if (ast_test_flag(results + x, flag))
4271 found++;
4273 if (found >= priority)
4274 return 1;
4275 return 0;
4278 static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4280 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
4283 static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4285 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
4288 static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4290 struct dundi_result results[MAX_RESULTS];
4291 int res;
4292 int x=0;
4293 char req[1024];
4294 struct ast_app *dial;
4296 if (!strncasecmp(context, "macro-", 6)) {
4297 if (!chan) {
4298 ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
4299 return -1;
4301 /* If done as a macro, use macro extension */
4302 if (!strcasecmp(exten, "s")) {
4303 exten = pbx_builtin_getvar_helper(chan, "ARG1");
4304 if (ast_strlen_zero(exten))
4305 exten = chan->macroexten;
4306 if (ast_strlen_zero(exten))
4307 exten = chan->exten;
4308 if (ast_strlen_zero(exten)) {
4309 ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
4310 return -1;
4313 if (ast_strlen_zero(data))
4314 data = "e164";
4315 } else {
4316 if (ast_strlen_zero(data))
4317 data = context;
4319 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4320 if (res > 0) {
4321 sort_results(results, res);
4322 for (x=0;x<res;x++) {
4323 if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
4324 if (!--priority)
4325 break;
4329 if (x < res) {
4330 /* Got a hit! */
4331 snprintf(req, sizeof(req), "%s/%s", results[x].tech, results[x].dest);
4332 dial = pbx_findapp("Dial");
4333 if (dial)
4334 res = pbx_exec(chan, dial, req);
4335 } else
4336 res = -1;
4337 return res;
4340 static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4342 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
4345 static struct ast_switch dundi_switch =
4347 name: "DUNDi",
4348 description: "DUNDi Discovered Dialplan Switch",
4349 exists: dundi_exists,
4350 canmatch: dundi_canmatch,
4351 exec: dundi_exec,
4352 matchmore: dundi_matchmore,
4355 static int set_config(char *config_file, struct sockaddr_in* sin)
4357 struct ast_config *cfg;
4358 struct ast_variable *v;
4359 char *cat;
4360 int format;
4361 int x;
4362 char hn[MAXHOSTNAMELEN] = "";
4363 struct ast_hostent he;
4364 struct hostent *hp;
4365 struct sockaddr_in sin2;
4366 static int last_port = 0;
4367 int globalpcmodel = 0;
4368 dundi_eid testeid;
4370 dundi_ttl = DUNDI_DEFAULT_TTL;
4371 dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
4372 any_peer = NULL;
4374 cfg = ast_config_load(config_file);
4376 if (!cfg) {
4377 ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
4378 return -1;
4380 ipaddr[0] = '\0';
4381 if (!gethostname(hn, sizeof(hn)-1)) {
4382 hp = ast_gethostbyname(hn, &he);
4383 if (hp) {
4384 memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
4385 ast_copy_string(ipaddr, ast_inet_ntoa(sin2.sin_addr), sizeof(ipaddr));
4386 } else
4387 ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
4388 } else
4389 ast_log(LOG_WARNING, "Unable to get host name!\n");
4390 AST_LIST_LOCK(&peers);
4391 reset_global_eid();
4392 global_storehistory = 0;
4393 ast_copy_string(secretpath, "dundi", sizeof(secretpath));
4394 v = ast_variable_browse(cfg, "general");
4395 while(v) {
4396 if (!strcasecmp(v->name, "port")){
4397 sin->sin_port = ntohs(atoi(v->value));
4398 if(last_port==0){
4399 last_port=sin->sin_port;
4400 } else if(sin->sin_port != last_port)
4401 ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
4402 } else if (!strcasecmp(v->name, "bindaddr")) {
4403 struct hostent *hp;
4404 struct ast_hostent he;
4405 hp = ast_gethostbyname(v->value, &he);
4406 if (hp) {
4407 memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
4408 } else
4409 ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
4410 } else if (!strcasecmp(v->name, "authdebug")) {
4411 authdebug = ast_true(v->value);
4412 } else if (!strcasecmp(v->name, "ttl")) {
4413 if ((sscanf(v->value, "%d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
4414 dundi_ttl = x;
4415 } else {
4416 ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
4417 v->value, v->lineno, DUNDI_DEFAULT_TTL);
4419 } else if (!strcasecmp(v->name, "autokill")) {
4420 if (sscanf(v->value, "%d", &x) == 1) {
4421 if (x >= 0)
4422 global_autokilltimeout = x;
4423 else
4424 ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
4425 } else if (ast_true(v->value)) {
4426 global_autokilltimeout = DEFAULT_MAXMS;
4427 } else {
4428 global_autokilltimeout = 0;
4430 } else if (!strcasecmp(v->name, "entityid")) {
4431 if (!dundi_str_to_eid(&testeid, v->value))
4432 global_eid = testeid;
4433 else
4434 ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
4435 } else if (!strcasecmp(v->name, "tos")) {
4436 if (sscanf(v->value, "%d", &format) == 1)
4437 tos = format & 0xff;
4438 else if (!strcasecmp(v->value, "lowdelay"))
4439 tos = IPTOS_LOWDELAY;
4440 else if (!strcasecmp(v->value, "throughput"))
4441 tos = IPTOS_THROUGHPUT;
4442 else if (!strcasecmp(v->value, "reliability"))
4443 tos = IPTOS_RELIABILITY;
4444 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
4445 else if (!strcasecmp(v->value, "mincost"))
4446 tos = IPTOS_MINCOST;
4447 #endif
4448 else if (!strcasecmp(v->value, "none"))
4449 tos = 0;
4450 else
4451 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
4452 ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno);
4453 #else
4454 ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', or 'none'\n", v->lineno);
4455 #endif
4456 } else if (!strcasecmp(v->name, "department")) {
4457 ast_copy_string(dept, v->value, sizeof(dept));
4458 } else if (!strcasecmp(v->name, "organization")) {
4459 ast_copy_string(org, v->value, sizeof(org));
4460 } else if (!strcasecmp(v->name, "locality")) {
4461 ast_copy_string(locality, v->value, sizeof(locality));
4462 } else if (!strcasecmp(v->name, "stateprov")) {
4463 ast_copy_string(stateprov, v->value, sizeof(stateprov));
4464 } else if (!strcasecmp(v->name, "country")) {
4465 ast_copy_string(country, v->value, sizeof(country));
4466 } else if (!strcasecmp(v->name, "email")) {
4467 ast_copy_string(email, v->value, sizeof(email));
4468 } else if (!strcasecmp(v->name, "phone")) {
4469 ast_copy_string(phone, v->value, sizeof(phone));
4470 } else if (!strcasecmp(v->name, "storehistory")) {
4471 global_storehistory = ast_true(v->value);
4472 } else if (!strcasecmp(v->name, "cachetime")) {
4473 if ((sscanf(v->value, "%d", &x) == 1)) {
4474 dundi_cache_time = x;
4475 } else {
4476 ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
4477 v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME);
4480 v = v->next;
4482 AST_LIST_UNLOCK(&peers);
4483 mark_mappings();
4484 v = ast_variable_browse(cfg, "mappings");
4485 while(v) {
4486 build_mapping(v->name, v->value);
4487 v = v->next;
4489 prune_mappings();
4490 mark_peers();
4491 cat = ast_category_browse(cfg, NULL);
4492 while(cat) {
4493 if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
4494 /* Entries */
4495 if (!dundi_str_to_eid(&testeid, cat))
4496 build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
4497 else if (!strcasecmp(cat, "*")) {
4498 build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
4499 any_peer = find_peer(NULL);
4500 } else
4501 ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
4503 cat = ast_category_browse(cfg, cat);
4505 prune_peers();
4506 ast_config_destroy(cfg);
4507 load_password();
4508 if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
4509 dundi_precache_full();
4510 return 0;
4513 static int unload_module(void)
4515 pthread_t previous_netthreadid = netthreadid, previous_precachethreadid = precachethreadid;
4516 ast_module_user_hangup_all();
4518 /* Stop all currently running threads */
4519 dundi_shutdown = 1;
4520 if (previous_netthreadid != AST_PTHREADT_NULL) {
4521 pthread_kill(previous_netthreadid, SIGURG);
4522 pthread_join(previous_netthreadid, NULL);
4524 if (previous_precachethreadid != AST_PTHREADT_NULL) {
4525 pthread_kill(previous_precachethreadid, SIGURG);
4526 pthread_join(previous_precachethreadid, NULL);
4529 ast_cli_unregister_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(struct ast_cli_entry));
4530 ast_unregister_switch(&dundi_switch);
4531 ast_custom_function_unregister(&dundi_function);
4532 close(netsocket);
4533 io_context_destroy(io);
4534 sched_context_destroy(sched);
4536 mark_mappings();
4537 prune_mappings();
4538 mark_peers();
4539 prune_peers();
4541 return 0;
4544 static int reload(void)
4546 struct sockaddr_in sin;
4547 set_config("dundi.conf",&sin);
4548 return 0;
4551 static int load_module(void)
4553 int res = 0;
4554 struct sockaddr_in sin;
4556 dundi_set_output(dundi_debug_output);
4557 dundi_set_error(dundi_error_output);
4559 sin.sin_family = AF_INET;
4560 sin.sin_port = ntohs(DUNDI_PORT);
4561 sin.sin_addr.s_addr = INADDR_ANY;
4563 /* Make a UDP socket */
4564 io = io_context_create();
4565 sched = sched_context_create();
4567 if (!io || !sched) {
4568 ast_log(LOG_ERROR, "Out of memory\n");
4569 return -1;
4572 if(set_config("dundi.conf",&sin))
4573 return AST_MODULE_LOAD_DECLINE;
4575 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
4577 if (netsocket < 0) {
4578 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
4579 return -1;
4581 if (bind(netsocket,(struct sockaddr *)&sin, sizeof(sin))) {
4582 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));
4583 return -1;
4586 if (option_verbose > 1)
4587 ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
4589 if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)))
4590 ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
4592 res = start_network_thread();
4593 if (res) {
4594 ast_log(LOG_ERROR, "Unable to start network thread\n");
4595 close(netsocket);
4596 return -1;
4599 if (option_verbose > 1)
4600 ast_verbose(VERBOSE_PREFIX_2 "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
4602 ast_cli_register_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(struct ast_cli_entry));
4603 if (ast_register_switch(&dundi_switch))
4604 ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
4605 ast_custom_function_register(&dundi_function);
4607 return res;
4610 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
4611 .load = load_module,
4612 .unload = unload_module,
4613 .reload = reload,