1 diff -ruN asterisk-1.0.7-orig/channels/chan_iax2.c asterisk-1.0.7-pbx_dundi/channels/chan_iax2.c
2 --- asterisk-1.0.7-orig/channels/chan_iax2.c 2005-03-18 18:30:05.000000000 +0100
3 +++ asterisk-1.0.7-pbx_dundi/channels/chan_iax2.c 2005-06-02 20:21:37.000000000 +0200
11 char inkeys[80]; /* Key(s) this user can use to authenticate to us */
17 char outkey[80]; /* What key we use to talk to this peer */
18 char context[AST_MAX_EXTENSION]; /* Default context (for transfer really) */
19 char regexten[AST_MAX_EXTENSION]; /* Extension to register (if regcontext is used) */
20 @@ -2194,6 +2196,26 @@
21 *notransfer=p->notransfer;
23 *usejitterbuf=p->usejitterbuf;
25 + if (!ast_strlen_zero(p->dbsecret)) {
26 + char *family, *key=NULL;
27 + family = ast_strdupa(p->dbsecret);
29 + key = strchr(family, '/');
35 + if (!family || !key || ast_db_get(family, key, secret, seclen)) {
36 + ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", p->dbsecret);
42 + strncpy(secret, p->secret, seclen); /* safe */
47 @@ -3624,6 +3646,15 @@
51 +static void free_context(struct iax2_context *con)
53 + struct iax2_context *conl;
61 static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies)
63 @@ -3769,6 +3800,28 @@
64 strncpy(iaxs[callno]->language, user->language, sizeof(iaxs[callno]->language)-1);
65 iaxs[callno]->notransfer = user->notransfer;
66 iaxs[callno]->usejitterbuf = user->usejitterbuf;
67 + /* Keep this check last */
68 + if (!ast_strlen_zero(user->dbsecret)) {
69 + char *family, *key=NULL;
70 + family = ast_strdupa(user->dbsecret);
72 + key = strchr(family, '/');
78 + if (!family || !key || ast_db_get(family, key, iaxs[callno]->secret, sizeof(iaxs[callno]->secret))) {
79 + ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", user->dbsecret);
80 + if (user->temponly) {
81 + ast_free_ha(user->ha);
82 + free_context(user->contexts);
88 + strncpy(iaxs[callno]->secret, user->secret, sizeof(iaxs[callno]->secret) - 1);
91 iaxs[callno]->trunk = iax2_getpeertrunk(*sin);
92 @@ -3844,15 +3897,23 @@
93 } else if (p->authmethods & IAX_AUTH_MD5) {
94 struct MD5Context md5;
95 unsigned char digest[16];
97 - MD5Update(&md5, p->challenge, strlen(p->challenge));
98 - MD5Update(&md5, p->secret, strlen(p->secret));
99 - MD5Final(digest, &md5);
100 - /* If they support md5, authenticate with it. */
102 - sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
103 - if (!strcasecmp(requeststr, md5secret))
105 + char *tmppw, *stringp;
107 + tmppw = ast_strdupa(p->secret);
109 + while((tmppw = strsep(&stringp, ";"))) {
111 + MD5Update(&md5, p->challenge, strlen(p->challenge));
112 + MD5Update(&md5, tmppw, strlen(tmppw));
113 + MD5Final(digest, &md5);
114 + /* If they support md5, authenticate with it. */
116 + sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
117 + if (!strcasecmp(requeststr, md5secret)) {
122 } else if (p->authmethods & IAX_AUTH_PLAINTEXT) {
123 if (!strcmp(secret, p->secret))
125 @@ -6237,16 +6298,6 @@
129 -static void free_context(struct iax2_context *con)
131 - struct iax2_context *conl;
139 static struct ast_channel *iax2_request(char *type, int format, void *data)
142 @@ -6469,6 +6520,8 @@
143 strncpy(peer->secret, v->value, sizeof(peer->secret)-1);
144 else if (!strcasecmp(v->name, "mailbox"))
145 strncpy(peer->mailbox, v->value, sizeof(peer->mailbox) - 1);
146 + else if (!strcasecmp(v->name, "dbsecret"))
147 + strncpy(peer->dbsecret, v->value, sizeof(peer->dbsecret)-1);
148 else if (!strcasecmp(v->name, "mailboxdetail"))
149 peer->messagedetail = ast_true(v->value);
150 else if (!strcasecmp(v->name, "trunk")) {
151 @@ -6665,6 +6718,8 @@
152 user->notransfer = ast_true(v->value);
153 } else if (!strcasecmp(v->name, "jitterbuffer")) {
154 user->usejitterbuf = ast_true(v->value);
155 + } else if (!strcasecmp(v->name, "dbsecret")) {
156 + strncpy(user->dbsecret, v->value, sizeof(user->dbsecret)-1);
157 } else if (!strcasecmp(v->name, "secret")) {
158 strncpy(user->secret, v->value, sizeof(user->secret)-1);
159 } else if (!strcasecmp(v->name, "callerid")) {
160 diff -ruN asterisk-1.0.7-orig/configs/dundi.conf.sample asterisk-1.0.7-pbx_dundi/configs/dundi.conf.sample
161 --- asterisk-1.0.7-orig/configs/dundi.conf.sample 1970-01-01 01:00:00.000000000 +0100
162 +++ asterisk-1.0.7-pbx_dundi/configs/dundi.conf.sample 2005-06-02 20:21:37.000000000 +0200
165 +; DUNDi configuration file
170 +; The "general" section contains general parameters relating
171 +; to the operation of the dundi client and server.
173 +; The first part should be your complete contact information
174 +; should someone else in your peer group need to contact you.
176 +;department=Your Department
177 +;organization=Your Company, Inc.
181 +;email=your@email.com
185 +; Specify bind address and port number. Default is
191 +; Our entity identifier (Should generally be the MAC address of the
192 +; machine it's running on. Defaults to the first eth address, but you
193 +; can override it here, as long as you set it to the MAC of *something*
196 +;entityid=00:07:E9:3B:76:60
198 +; Define the max depth in which to search the DUNDi system (also max # of
199 +; seconds to wait for a reply)
203 +; If we don't get ACK to our DPREQUEST within 2000ms, and autokill is set
204 +; to yes, then we cancel the whole thing (that's enough time for one
205 +; retransmission only). This is used to keep things from stalling for a long
206 +; time for a host that is not available, but would be ill advised for bad
207 +; connections. In addition to 'yes' or 'no' you can also specify a number
208 +; of milliseconds. See 'qualify' for individual peers to turn on for just
213 +; pbx_dundi creates a rotating key called "secret", under the family
214 +; 'secretpath'. The default family is dundi (resulting in
215 +; the key being held at dundi/secret).
219 +; The 'storehistory' option (also changeable at runtime with
220 +; 'dundi store history' and 'dundi no store history') will
221 +; cause the DUNDi engine to keep track of the last several
222 +; queries and the amount of time each query took to execute
223 +; for the purpose of tracking slow nodes. This option is
224 +; off by default due to performance impacts.
230 +; The "mappings" section maps DUNDi contexts
231 +; to contexts on the local asterisk system. Remember
232 +; that numbers that are made available under the e164
233 +; DUNDi context are regulated by the DUNDi General Peering
234 +; Agreement (GPA) if you are a member of the DUNDi E.164
237 +; dundi_context => local_context,weight,tech,dest[,options]]
239 +; dundi_context is the name of the context being requested
240 +; within the DUNDi request
242 +; local_context is the name of the context on the local system
243 +; in which numbers can be looked up for which responses shall be given.
245 +; tech is the technology to use (IAX, SIP, H323)
247 +; dest is the destination to supply for reaching that number. The
248 +; following variables can be used in the destination string and will
249 +; be automatically substituted:
250 +; ${NUMBER}: The number being requested
251 +; ${IPADDR}: The IP address to connect to
252 +; ${SECRET}: The current rotating secret key to be used
254 +; Further options may include:
256 +; nounsolicited: No unsolicited calls of any type permitted via this
258 +; nocomunsolicit: No commercial unsolicited calls permitted via
260 +; residential: This number is known to be a residence
261 +; commercial: This number is known to be a business
262 +; mobile: This number is known to be a mobile phone
263 +; nocomunsolicit: No commercial unsolicited calls permitted via
265 +; nopartial: Do not search for partial matches
267 +; There *must* exist an entry in mappings for DUNDi to respond
268 +; to any request, although it may be empty.
270 +;e164 => dundi-e164-canonical,0,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
271 +;e164 => dundi-e164-customers,100,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
272 +;e164 => dundi-e164-via-pstn,400,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
274 +;digexten => default,0,IAX2,guest@lappy/${NUMBER}
280 +; The remaining sections represent the peers
281 +; that we fundamentally trust. The section name
282 +; represents the name and optionally at a specific
283 +; DUNDi context if you want the trust to be established
284 +; for only a specific DUNDi context.
286 +; inkey - What key they will be authenticating to us with
288 +; outkey - What key we use to authenticate to them
290 +; host - What their host is
292 +; order - What search order to use. May be 'primary', 'secondary',
293 +; 'tertiary' or 'quartiary'. In large systems, it is beneficial
294 +; to only query one up-stream host in order to maximize caching
295 +; value. Adding one with primary and one with secondary gives you
296 +; redundancy without sacraficing performance.
298 +; include - Includes this peer when searching a particular context
299 +; for lookup (set "all" to perform all lookups with that
300 +; host. This is also the context in which peers are permitted
303 +; noinclude - Disincludes this peer when searching a particular context
304 +; for lookup (set "all" to perform no lookups with that
307 +; permit - Permits this peer to search a given DUNDi context on
308 +; the local system. Set "all" to permit this host to
309 +; lookup all contexts. This is also a context for which
310 +; we will create/forward PRECACHE commands.
312 +; deny - Denies this peer to search a given DUNDi context on
313 +; the local system. Set "all" to deny this host to
314 +; lookup all contexts.
316 +; model - inbound, outbound, or symmetric for whether we receive
317 +; requests only, transmit requests only, or do both.
319 +; precache - Utilize/Permit precaching with this peer (to pre
320 +; cache means to provide an answer when no request
321 +; was made and is used so that machines with few
322 +; routes can push those routes up a to a higher level).
323 +; outgoing means we send precache routes to this peer,
324 +; incoming means we permit this peer to send us
325 +; precache routes. symmetric means we do both.
327 +; Note: You cannot mix symmetric/outbound model with symmetric/inbound
328 +; precache, nor can you mix symmetric/inbound model with symmetric/outbound
332 +; The '*' peer is special and matches an unspecified entity
336 +; Sample Primary e164 DUNDi peer
340 +host = 64.215.96.114
348 +; Sample Secondary e164 DUNDi peer
350 +;[00:A0:C9:96:92:84]
352 +;host = misery.digium.com
361 +; Sample "push mode" downstream host
363 +;[00:0C:76:96:75:28]
366 +;precache = incoming
369 +;include = e164 ; In this case used only for precaching
374 +; Sample "push mode" upstream host
376 +;[00:07:E9:3B:76:60]
378 +;precache = outbound
379 +;host = 216.207.245.34
382 +;permit = all ; In this case used only for precaching
389 diff -ruN asterisk-1.0.7-orig/include/asterisk/dundi.h asterisk-1.0.7-pbx_dundi/include/asterisk/dundi.h
390 --- asterisk-1.0.7-orig/include/asterisk/dundi.h 1970-01-01 01:00:00.000000000 +0100
391 +++ asterisk-1.0.7-pbx_dundi/include/asterisk/dundi.h 2005-06-02 20:21:37.000000000 +0200
394 + * Distributed Universal Number Discovery (DUNDi)
396 + * Copyright (C) 2004, Digium Inc.
398 + * Written by Mark Spencer <markster@digium.com>
400 + * This program is Free Software distributed under the terms of
401 + * of the GNU General Public License.
403 +#ifndef _ASTERISK_DUNDI_H
404 +#define _ASTERISK_DUNDI_H
406 +#include <asterisk/channel.h>
408 +#define DUNDI_PORT 4520
410 +/* A DUNDi Entity ID is essentially a MAC address, brief and unique */
412 + unsigned char eid[6];
413 +} __attribute__ ((__packed__));
415 +typedef struct _dundi_eid dundi_eid;
418 + unsigned short strans; /* Source transaction */
419 + unsigned short dtrans; /* Destination transaction */
420 + unsigned char iseqno; /* Next expected incoming sequence number */
421 + unsigned char oseqno; /* Outgoing sequence number */
422 + unsigned char cmdresp; /* Command / Response */
423 + unsigned char cmdflags; /* Command / Response specific flags*/
424 + unsigned char ies[0];
425 +} __attribute__ ((__packed__));
427 +struct dundi_ie_hdr {
430 + unsigned char iedata[0];
431 +} __attribute__ ((__packed__));
433 +#define DUNDI_FLAG_RETRANS (1 << 16) /* Applies to dtrans */
434 +#define DUNDI_FLAG_RESERVED (1 << 16) /* Applies to strans */
436 +#define DUNDI_PROTO_NONE 0 /* No answer yet */
437 +#define DUNDI_PROTO_IAX 1 /* IAX version 2 */
438 +#define DUNDI_PROTO_SIP 2 /* Session Initiation Protocol */
439 +#define DUNDI_PROTO_H323 3 /* ITU H.323 */
441 +#define DUNDI_FLAG_NONEXISTANT (0) /* Isn't and can't be a valid number */
442 +#define DUNDI_FLAG_EXISTS (1 << 0) /* Is a valid number */
443 +#define DUNDI_FLAG_MATCHMORE (1 << 1) /* Might be valid if you add more digits */
444 +#define DUNDI_FLAG_CANMATCH (1 << 2) /* Might be a match */
445 +#define DUNDI_FLAG_IGNOREPAT (1 << 3) /* Keep dialtone */
446 +#define DUNDI_FLAG_RESIDENTIAL (1 << 4) /* Destination known to be residential */
447 +#define DUNDI_FLAG_COMMERCIAL (1 << 5) /* Destination known to be commercial */
448 +#define DUNDI_FLAG_MOBILE (1 << 6) /* Destination known to be cellular/mobile */
449 +#define DUNDI_FLAG_NOUNSOLICITED (1 << 7) /* No unsolicited calls of any kind through this route */
450 +#define DUNDI_FLAG_NOCOMUNSOLICIT (1 << 8) /* No commercial unsolicited calls through this route */
452 +#define DUNDI_HINT_NONE (0)
453 +#define DUNDI_HINT_TTL_EXPIRED (1 << 0) /* TTL Expired */
454 +#define DUNDI_HINT_DONT_ASK (1 << 1) /* Don't ask for anything beginning with data */
455 +#define DUNDI_HINT_UNAFFECTED (1 << 2) /* Answer not affected by entity list */
457 +struct dundi_encblock { /* AES-128 encrypted block */
458 + unsigned char iv[16]; /* Initialization vector of random data */
459 + unsigned char encdata[0]; /* Encrypted / compressed data */
460 +} __attribute__ ((__packed__));
462 +struct dundi_answer {
463 + dundi_eid eid; /* Original source of answer */
464 + unsigned char protocol; /* Protocol (DUNDI_PROTO_*) */
465 + unsigned short flags; /* Flags relating to answer */
466 + unsigned short weight; /* Weight of answers */
467 + unsigned char data[0]; /* Protocol specific URI */
468 +} __attribute__ ((__packed__));
471 + unsigned short flags; /* Flags relating to answer */
472 + unsigned char data[0]; /* For data for hint */
473 +} __attribute__ ((__packed__));
475 +#define DUNDI_CAUSE_SUCCESS 0 /* Success */
476 +#define DUNDI_CAUSE_GENERAL 1 /* General unspecified failure */
477 +#define DUNDI_CAUSE_DYNAMIC 2 /* Requested entity is dynamic */
478 +#define DUNDI_CAUSE_NOAUTH 3 /* No or improper authorization */
479 +#define DUNDI_CAUSE_DUPLICATE 4 /* Duplicate request */
480 +#define DUNDI_CAUSE_TTL_EXPIRED 5 /* Expired TTL */
481 +#define DUNDI_CAUSE_NEEDKEY 6 /* Need new session key to decode */
482 +#define DUNDI_CAUSE_BADENCRYPT 7 /* Badly encrypted data */
484 +struct dundi_cause {
485 + unsigned char causecode; /* Numerical cause (DUNDI_CAUSE_*) */
486 + char desc[0]; /* Textual description */
487 +} __attribute__ ((__packed__));
489 +struct dundi_peer_status {
490 + unsigned int flags;
491 + unsigned short netlag;
492 + unsigned short querylag;
494 +} __attribute__ ((__packed__));
496 +#define DUNDI_PEER_PRIMARY (1 << 0)
497 +#define DUNDI_PEER_SECONDARY (1 << 1)
498 +#define DUNDI_PEER_UNAVAILABLE (1 << 2)
499 +#define DUNDI_PEER_REGISTERED (1 << 3)
500 +#define DUNDI_PEER_MOD_OUTBOUND (1 << 4)
501 +#define DUNDI_PEER_MOD_INBOUND (1 << 5)
502 +#define DUNDI_PEER_PCMOD_OUTBOUND (1 << 6)
503 +#define DUNDI_PEER_PCMOD_INBOUND (1 << 7)
505 +#define DUNDI_COMMAND_FINAL (0x80) /* Or'd with other flags */
507 +#define DUNDI_COMMAND_ACK (0 | 0x40) /* Ack a message */
508 +#define DUNDI_COMMAND_DPDISCOVER 1 /* Request discovery */
509 +#define DUNDI_COMMAND_DPRESPONSE (2 | 0x40) /* Respond to a discovery request */
510 +#define DUNDI_COMMAND_EIDQUERY 3 /* Request information for a peer */
511 +#define DUNDI_COMMAND_EIDRESPONSE (4 | 0x40) /* Response to a peer query */
512 +#define DUNDI_COMMAND_PRECACHERQ 5 /* Pre-cache Request */
513 +#define DUNDI_COMMAND_PRECACHERP (6 | 0x40) /* Pre-cache Response */
514 +#define DUNDI_COMMAND_INVALID (7 | 0x40) /* Invalid dialog state (does not require ack) */
515 +#define DUNDI_COMMAND_UNKNOWN (8 | 0x40) /* Unknown command */
516 +#define DUNDI_COMMAND_NULL 9 /* No-op */
517 +#define DUNDI_COMMAND_REGREQ (10) /* Register Request */
518 +#define DUNDI_COMMAND_REGRESPONSE (11 | 0x40) /* Register Response */
519 +#define DUNDI_COMMAND_CANCEL (12) /* Cancel transaction entirely */
520 +#define DUNDI_COMMAND_ENCRYPT (13) /* Send an encrypted message */
521 +#define DUNDI_COMMAND_ENCREJ (14 | 0x40) /* Reject an encrypted message */
523 +#define DUNDI_COMMAND_STATUS 15 /* Status command */
526 + * Remember that some information elements may occur
527 + * more than one time within a message
530 +#define DUNDI_IE_EID 1 /* Entity identifier (dundi_eid) */
531 +#define DUNDI_IE_CALLED_CONTEXT 2 /* DUNDi Context (string) */
532 +#define DUNDI_IE_CALLED_NUMBER 3 /* Number of equivalent (string) */
533 +#define DUNDI_IE_EID_DIRECT 4 /* Entity identifier (dundi_eid), direct connect */
534 +#define DUNDI_IE_ANSWER 5 /* An answer (struct dundi_answer) */
535 +#define DUNDI_IE_TTL 6 /* Max TTL for this request / Remaining TTL for the response (short)*/
536 +#define DUNDI_IE_VERSION 10 /* DUNDi version (should be 1) (short) */
537 +#define DUNDI_IE_EXPIRATION 11 /* Recommended expiration (short) */
538 +#define DUNDI_IE_UNKNOWN 12 /* Unknown command (byte) */
539 +#define DUNDI_IE_CAUSE 14 /* Success or cause of failure */
540 +#define DUNDI_IE_REQEID 15 /* EID being requested for EIDQUERY*/
541 +#define DUNDI_IE_ENCDATA 16 /* AES-128 encrypted data */
542 +#define DUNDI_IE_SHAREDKEY 17 /* RSA encrypted AES-128 key */
543 +#define DUNDI_IE_SIGNATURE 18 /* RSA Signature of encrypted shared key */
544 +#define DUNDI_IE_KEYCRC32 19 /* CRC32 of encrypted key (int) */
545 +#define DUNDI_IE_HINT 20 /* Answer hints (struct ast_hint) */
547 +#define DUNDI_IE_DEPARTMENT 21 /* Department, for EIDQUERY (string) */
548 +#define DUNDI_IE_ORGANIZATION 22 /* Organization, for EIDQUERY (string) */
549 +#define DUNDI_IE_LOCALITY 23 /* City/Locality, for EIDQUERY (string) */
550 +#define DUNDI_IE_STATE_PROV 24 /* State/Province, for EIDQUERY (string) */
551 +#define DUNDI_IE_COUNTRY 25 /* Country, for EIDQUERY (string) */
552 +#define DUNDI_IE_EMAIL 26 /* E-mail addy, for EIDQUERY (string) */
553 +#define DUNDI_IE_PHONE 27 /* Contact Phone, for EIDQUERY (string) */
554 +#define DUNDI_IE_IPADDR 28 /* IP Address, for EIDQUERY (string) */
555 +#define DUNDI_IE_CACHEBYPASS 29 /* Bypass cache (empty) */
557 +#define DUNDI_IE_PEERSTATUS 30 /* Peer/peer status (struct dundi_peer_status) */
559 +#define DUNDI_FLUFF_TIME 2000 /* Amount of time for answer */
560 +#define DUNDI_TTL_TIME 200 /* Incremental average time */
562 +#define DUNDI_DEFAULT_RETRANS 5
563 +#define DUNDI_DEFAULT_RETRANS_TIMER 1000
564 +#define DUNDI_DEFAULT_TTL 120 /* In seconds/hops like TTL */
565 +#define DUNDI_DEFAULT_VERSION 1
566 +#define DUNDI_DEFAULT_CACHE_TIME 3600 /* In seconds */
567 +#define DUNDI_DEFAULT_KEY_EXPIRE 3600 /* Life of shared key In seconds */
568 +#define DUNDI_DEF_EMPTY_CACHE_TIME 60 /* In seconds, cache of empty answer */
569 +#define DUNDI_WINDOW 1 /* Max 1 message in window */
571 +#define DEFAULT_MAXMS 2000
573 +struct dundi_result {
584 +struct dundi_entity_info {
586 + char stateprov[80];
595 +/* Lookup the given number in the given dundi context (or e164 if unspecified) using the given callerid (if specified) and return up to maxret results in the array specified.
596 + returns the number of results found or -1 on a hangup of teh channel. */
597 +int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int nocache);
599 +/* Retrieve information on a specific EID */
600 +int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid);
602 +/* Pre-cache to push upstream peers */
603 +int dundi_precache(const char *dcontext, const char *number);
604 +#endif /* _ASTERISK_DUNDI_H */
605 diff -ruN asterisk-1.0.7-orig/pbx/Makefile asterisk-1.0.7-pbx_dundi/pbx/Makefile
606 --- asterisk-1.0.7-orig/pbx/Makefile 2003-10-26 19:50:49.000000000 +0100
607 +++ asterisk-1.0.7-pbx_dundi/pbx/Makefile 2005-06-02 20:21:37.000000000 +0200
612 -PBX_LIBS=pbx_config.so pbx_wilcalu.so pbx_spool.so # pbx_gtkconsole.so pbx_kdeconsole.so
613 +PBX_LIBS=pbx_config.so pbx_wilcalu.so pbx_spool.so pbx_dundi.so # pbx_gtkconsole.so pbx_kdeconsole.so
615 # Add GTK console if appropriate
616 PBX_LIBS+=$(shell gtk-config --cflags >/dev/null 2>/dev/null && echo "pbx_gtkconsole.so")
618 pbx_kdeconsole.so: $(KDE_CONSOLE_OBJS)
619 $(CC) $(SOLINK) -o $@ $(KDE_CONSOLE_OBJS) $(KDE_LIBS)
621 +pbx_dundi.so: dundi-parser.o pbx_dundi.o
622 + $(CC) $(SOLINK) -o $@ pbx_dundi.o dundi-parser.o $(LDFLAGS_EXTRA) -lz
627 diff -ruN asterisk-1.0.7-orig/pbx/dundi-parser.c asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.c
628 --- asterisk-1.0.7-orig/pbx/dundi-parser.c 1970-01-01 01:00:00.000000000 +0100
629 +++ asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.c 2005-06-02 20:21:37.000000000 +0200
632 + * Distributed Universal Number Discovery (DUNDi)
634 + * Copyright (C) 2004, Digium Inc.
636 + * Written by Mark Spencer <markster@digium.com>
638 + * This program is Free Software distributed under the terms of
639 + * of the GNU General Public License.
642 +#include <sys/types.h>
643 +#include <sys/socket.h>
645 +#include <netinet/in.h>
646 +#include <asterisk/frame.h>
647 +#include <asterisk/utils.h>
648 +#include <arpa/inet.h>
652 +#include <asterisk/dundi.h>
653 +#include "dundi-parser.h"
654 +#include <asterisk/dundi.h>
656 +static void internaloutput(const char *str)
658 + fputs(str, stdout);
661 +static void internalerror(const char *str)
663 + fprintf(stderr, "WARNING: %s", str);
666 +static void (*outputf)(const char *str) = internaloutput;
667 +static void (*errorf)(const char *str) = internalerror;
669 +char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid)
674 + if (s && (maxlen > 0))
677 + for (x=0;x<5;x++) {
678 + sprintf(s, "%02x:", eid->eid[x]);
681 + sprintf(s, "%02x", eid->eid[5]);
686 +char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
691 + if (s && (maxlen > 0))
694 + for (x=0;x<6;x++) {
695 + sprintf(s, "%02X", eid->eid[x]);
702 +int dundi_str_to_eid(dundi_eid *eid, char *s)
704 + unsigned int eid_int[6];
706 + if (sscanf(s, "%x:%x:%x:%x:%x:%x", &eid_int[0], &eid_int[1], &eid_int[2],
707 + &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
710 + eid->eid[x] = eid_int[x];
714 +int dundi_str_short_to_eid(dundi_eid *eid, char *s)
716 + unsigned int eid_int[6];
718 + if (sscanf(s, "%2x%2x%2x%2x%2x%2x", &eid_int[0], &eid_int[1], &eid_int[2],
719 + &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
722 + eid->eid[x] = eid_int[x];
726 +int dundi_eid_zero(dundi_eid *eid)
729 + for (x=0;x<sizeof(eid->eid) / sizeof(eid->eid[0]);x++)
730 + if (eid->eid[x]) return 0;
734 +int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2)
736 + return memcmp(eid1, eid2, sizeof(dundi_eid));
739 +static void dump_string(char *output, int maxlen, void *value, int len)
744 + strncpy(output,value, maxlen);
745 + output[maxlen] = '\0';
748 +static void dump_cbypass(char *output, int maxlen, void *value, int len)
750 + strncpy(output, "Bypass Caches", maxlen);
751 + output[maxlen] = '\0';
754 +static void dump_eid(char *output, int maxlen, void *value, int len)
757 + dundi_eid_to_str(output, maxlen, (dundi_eid *)value);
759 + snprintf(output, maxlen, "Invalid EID len %d", len);
762 +char *dundi_hint2str(char *buf, int bufsiz, int flags)
765 + buf[bufsiz-1] = '\0';
766 + if (flags & DUNDI_HINT_TTL_EXPIRED) {
767 + strncat(buf, "TTLEXPIRED|", bufsiz - strlen(buf) - 1);
769 + if (flags & DUNDI_HINT_DONT_ASK) {
770 + strncat(buf, "DONTASK|", bufsiz - strlen(buf) - 1);
772 + if (flags & DUNDI_HINT_UNAFFECTED) {
773 + strncat(buf, "UNAFFECTED|", bufsiz - strlen(buf) - 1);
775 + /* Get rid of trailing | */
776 + if (ast_strlen_zero(buf))
777 + strcpy(buf, "NONE|");
778 + buf[strlen(buf)-1] = '\0';
782 +static void dump_hint(char *output, int maxlen, void *value, int len)
784 + unsigned short flags;
788 + strncpy(output, "<invalid contents>", maxlen);
791 + memcpy(&flags, value, sizeof(flags));
792 + flags = ntohs(flags);
793 + memset(tmp, 0, sizeof(tmp));
794 + dundi_hint2str(tmp2, sizeof(tmp2), flags);
795 + snprintf(tmp, sizeof(tmp), "[%s] ", tmp2);
796 + memcpy(tmp + strlen(tmp), value + 2, len - 2);
797 + strncpy(output, tmp, maxlen - 1);
800 +static void dump_cause(char *output, int maxlen, void *value, int len)
802 + static char *causes[] = {
811 + unsigned char cause;
813 + strncpy(output, "<invalid contents>", maxlen);
816 + cause = *((unsigned char *)value);
817 + memset(tmp2, 0, sizeof(tmp2));
821 + memcpy(tmp2, value + 1, mlen);
822 + if (cause < sizeof(causes) / sizeof(causes[0])) {
824 + snprintf(tmp, sizeof(tmp), "%s: %s", causes[cause], tmp2);
826 + snprintf(tmp, sizeof(tmp), "%s", causes[cause]);
829 + snprintf(tmp, sizeof(tmp), "%d: %s", cause, tmp2);
831 + snprintf(tmp, sizeof(tmp), "%d", cause);
834 + strncpy(output,tmp, maxlen);
835 + output[maxlen] = '\0';
838 +static void dump_int(char *output, int maxlen, void *value, int len)
840 + if (len == (int)sizeof(unsigned int))
841 + snprintf(output, maxlen, "%lu", (unsigned long)ntohl(*((unsigned int *)value)));
843 + snprintf(output, maxlen, "Invalid INT");
846 +static void dump_short(char *output, int maxlen, void *value, int len)
848 + if (len == (int)sizeof(unsigned short))
849 + snprintf(output, maxlen, "%d", ntohs(*((unsigned short *)value)));
851 + snprintf(output, maxlen, "Invalid SHORT");
854 +static void dump_byte(char *output, int maxlen, void *value, int len)
856 + if (len == (int)sizeof(unsigned char))
857 + snprintf(output, maxlen, "%d", *((unsigned char *)value));
859 + snprintf(output, maxlen, "Invalid BYTE");
862 +static char *proto2str(int proto, char *buf, int bufsiz)
865 + case DUNDI_PROTO_NONE:
866 + strncpy(buf, "None", bufsiz - 1);
868 + case DUNDI_PROTO_IAX:
869 + strncpy(buf, "IAX", bufsiz - 1);
871 + case DUNDI_PROTO_SIP:
872 + strncpy(buf, "SIP", bufsiz - 1);
874 + case DUNDI_PROTO_H323:
875 + strncpy(buf, "H.323", bufsiz - 1);
878 + snprintf(buf, bufsiz, "Unknown Proto(%d)", proto);
880 + buf[bufsiz-1] = '\0';
884 +char *dundi_flags2str(char *buf, int bufsiz, int flags)
887 + buf[bufsiz-1] = '\0';
888 + if (flags & DUNDI_FLAG_EXISTS) {
889 + strncat(buf, "EXISTS|", bufsiz - strlen(buf) - 1);
891 + if (flags & DUNDI_FLAG_MATCHMORE) {
892 + strncat(buf, "MATCHMORE|", bufsiz - strlen(buf) - 1);
894 + if (flags & DUNDI_FLAG_CANMATCH) {
895 + strncat(buf, "CANMATCH|", bufsiz - strlen(buf) - 1);
897 + if (flags & DUNDI_FLAG_IGNOREPAT) {
898 + strncat(buf, "IGNOREPAT|", bufsiz - strlen(buf) - 1);
900 + if (flags & DUNDI_FLAG_RESIDENTIAL) {
901 + strncat(buf, "RESIDENCE|", bufsiz - strlen(buf) - 1);
903 + if (flags & DUNDI_FLAG_COMMERCIAL) {
904 + strncat(buf, "COMMERCIAL|", bufsiz - strlen(buf) - 1);
906 + if (flags & DUNDI_FLAG_MOBILE) {
907 + strncat(buf, "MOBILE", bufsiz - strlen(buf) - 1);
909 + if (flags & DUNDI_FLAG_NOUNSOLICITED) {
910 + strncat(buf, "NOUNSLCTD|", bufsiz - strlen(buf) - 1);
912 + if (flags & DUNDI_FLAG_NOCOMUNSOLICIT) {
913 + strncat(buf, "NOCOMUNSLTD|", bufsiz - strlen(buf) - 1);
915 + /* Get rid of trailing | */
916 + if (ast_strlen_zero(buf))
917 + strcpy(buf, "NONE|");
918 + buf[strlen(buf)-1] = '\0';
922 +static void dump_answer(char *output, int maxlen, void *value, int len)
924 + struct dundi_answer *answer;
930 + answer = (struct dundi_answer *)(value);
931 + memcpy(tmp, answer->data, (len >= 500) ? 500 : len - 10);
932 + dundi_eid_to_str(eid_str, sizeof(eid_str), &answer->eid);
933 + snprintf(output, maxlen, "[%s] %d <%s/%s> from [%s]",
934 + dundi_flags2str(flags, sizeof(flags), ntohs(answer->flags)),
935 + ntohs(answer->weight),
936 + proto2str(answer->protocol, proto, sizeof(proto)),
939 + strncpy(output, "Invalid Answer", maxlen - 1);
942 +static void dump_encrypted(char *output, int maxlen, void *value, int len)
946 + if ((len > 16) && !(len % 16)) {
948 + for (x=0;x<16;x++) {
949 + snprintf(iv + (x << 1), 3, "%02x", ((unsigned char *)value)[x]);
951 + snprintf(output, maxlen, "[IV %s] %d encrypted blocks\n", iv, len / 16);
953 + snprintf(output, maxlen, "Invalid Encrypted Datalen %d", len);
956 +static void dump_raw(char *output, int maxlen, void *value, int len)
959 + unsigned char *u = value;
960 + output[maxlen - 1] = '\0';
961 + strcpy(output, "[ ");
962 + for (x=0;x<len;x++) {
963 + snprintf(output + strlen(output), maxlen - strlen(output) - 1, "%02x ", u[x]);
965 + strncat(output + strlen(output), "]", maxlen - strlen(output) - 1);
968 +static struct dundi_ie {
971 + void (*dump)(char *output, int maxlen, void *value, int len);
973 + { DUNDI_IE_EID, "ENTITY IDENT", dump_eid },
974 + { DUNDI_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string },
975 + { DUNDI_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string },
976 + { DUNDI_IE_EID_DIRECT, "DIRECT EID", dump_eid },
977 + { DUNDI_IE_ANSWER, "ANSWER", dump_answer },
978 + { DUNDI_IE_TTL, "TTL", dump_short },
979 + { DUNDI_IE_VERSION, "VERSION", dump_short },
980 + { DUNDI_IE_EXPIRATION, "EXPIRATION", dump_short },
981 + { DUNDI_IE_UNKNOWN, "UKWN DUNDI CMD", dump_byte },
982 + { DUNDI_IE_CAUSE, "CAUSE", dump_cause },
983 + { DUNDI_IE_REQEID, "REQUEST EID", dump_eid },
984 + { DUNDI_IE_ENCDATA, "ENCDATA", dump_encrypted },
985 + { DUNDI_IE_SHAREDKEY, "SHAREDKEY", dump_raw },
986 + { DUNDI_IE_SIGNATURE, "SIGNATURE", dump_raw },
987 + { DUNDI_IE_KEYCRC32, "KEYCRC32", dump_int },
988 + { DUNDI_IE_HINT, "HINT", dump_hint },
989 + { DUNDI_IE_DEPARTMENT, "DEPARTMENT", dump_string },
990 + { DUNDI_IE_ORGANIZATION, "ORGANIZTN", dump_string },
991 + { DUNDI_IE_LOCALITY, "LOCALITY", dump_string },
992 + { DUNDI_IE_STATE_PROV, "STATEPROV", dump_string },
993 + { DUNDI_IE_COUNTRY, "COUNTRY", dump_string },
994 + { DUNDI_IE_EMAIL, "EMAIL", dump_string },
995 + { DUNDI_IE_PHONE, "PHONE", dump_string },
996 + { DUNDI_IE_IPADDR, "ADDRESS", dump_string },
997 + { DUNDI_IE_CACHEBYPASS, "CBYPASS", dump_cbypass },
1000 +const char *dundi_ie2str(int ie)
1003 + for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
1004 + if (ies[x].ie == ie)
1005 + return ies[x].name;
1007 + return "Unknown IE";
1010 +static void dump_ies(unsigned char *iedata, int spaces, int len)
1016 + char interp[1024];
1022 + ielen = iedata[1];
1023 + /* Encrypted data is the remainder */
1024 + if (ie == DUNDI_IE_ENCDATA)
1026 + if (ielen + 2> len) {
1027 + snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len);
1032 + for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
1033 + if (ies[x].ie == ie) {
1034 + if (ies[x].dump) {
1035 + ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
1036 + snprintf(tmp, (int)sizeof(tmp), " %s%-15.15s : %s\n", (spaces ? " " : "" ), ies[x].name, interp);
1040 + snprintf(interp, (int)sizeof(interp), "%d bytes", ielen);
1042 + strcpy(interp, "Present");
1043 + snprintf(tmp, (int)sizeof(tmp), " %s%-15.15s : %s\n", (spaces ? " " : "" ), ies[x].name, interp);
1050 + snprintf(tmp, (int)sizeof(tmp), " %sUnknown IE %03d : Present\n", (spaces ? " " : "" ), ie);
1053 + iedata += (2 + ielen);
1054 + len -= (2 + ielen);
1059 +void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen)
1066 + char *commands[] = {
1084 + char subclass2[20];
1088 + char iabuf[INET_ADDRSTRLEN];
1089 + if (ntohs(fhi->dtrans) & DUNDI_FLAG_RETRANS)
1090 + strcpy(retries, "Yes");
1092 + strcpy(retries, "No");
1093 + if ((ntohs(fhi->strans) & DUNDI_FLAG_RESERVED)) {
1094 + /* Ignore frames with high bit set to 1 */
1097 + if ((fhi->cmdresp & 0x3f) > (int)sizeof(commands)/(int)sizeof(char *)) {
1098 + snprintf(class2, (int)sizeof(class2), "(%d?)", fhi->cmdresp);
1101 + class = commands[(int)(fhi->cmdresp & 0x3f)];
1103 + snprintf(subclass2, (int)sizeof(subclass2), "%02x", fhi->cmdflags);
1104 + subclass = subclass2;
1105 + snprintf(tmp, (int)sizeof(tmp),
1106 + "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s (%s)\n",
1108 + retries, fhi->oseqno, fhi->iseqno, class, fhi->cmdresp & 0x40 ? "Response" : "Command");
1110 + snprintf(tmp, (int)sizeof(tmp),
1111 + "%s Flags: %s STrans: %5.5d DTrans: %5.5d [%s:%d]%s\n", (rx > 1) ? " " : "",
1112 + subclass, ntohs(fhi->strans) & ~DUNDI_FLAG_RESERVED, ntohs(fhi->dtrans) & ~DUNDI_FLAG_RETRANS,
1113 + ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port),
1114 + fhi->cmdresp & 0x80 ? " (Final)" : "");
1116 + dump_ies(fhi->ies, rx > 1, datalen);
1119 +int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen)
1122 + if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
1123 + snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
1127 + ied->buf[ied->pos++] = ie;
1128 + ied->buf[ied->pos++] = datalen;
1129 + memcpy(ied->buf + ied->pos, data, datalen);
1130 + ied->pos += datalen;
1134 +int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, unsigned char *data)
1137 + int datalen = data ? strlen(data) + 1 : 1;
1138 + if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
1139 + snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
1143 + ied->buf[ied->pos++] = ie;
1144 + ied->buf[ied->pos++] = datalen;
1145 + ied->buf[ied->pos++] = cause;
1146 + memcpy(ied->buf + ied->pos, data, datalen-1);
1147 + ied->pos += datalen-1;
1151 +int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, unsigned char *data)
1154 + int datalen = data ? strlen(data) + 2 : 2;
1155 + if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
1156 + snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
1160 + ied->buf[ied->pos++] = ie;
1161 + ied->buf[ied->pos++] = datalen;
1162 + flags = htons(flags);
1163 + memcpy(ied->buf + ied->pos, &flags, sizeof(flags));
1165 + memcpy(ied->buf + ied->pos, data, datalen-1);
1166 + ied->pos += datalen-2;
1170 +int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen)
1174 + if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
1175 + snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
1179 + ied->buf[ied->pos++] = ie;
1180 + ied->buf[ied->pos++] = datalen;
1181 + memcpy(ied->buf + ied->pos, iv, 16);
1184 + memcpy(ied->buf + ied->pos, data, datalen-16);
1185 + ied->pos += datalen-16;
1190 +int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, unsigned char *data)
1193 + int datalen = data ? strlen(data) + 11 : 11;
1195 + unsigned short myw;
1196 + if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
1197 + snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
1201 + ied->buf[ied->pos++] = ie;
1202 + ied->buf[ied->pos++] = datalen;
1204 + ied->buf[ied->pos++] = eid->eid[x];
1205 + ied->buf[ied->pos++] = protocol;
1206 + myw = htons(flags);
1207 + memcpy(ied->buf + ied->pos, &myw, 2);
1209 + myw = htons(weight);
1210 + memcpy(ied->buf + ied->pos, &myw, 2);
1212 + memcpy(ied->buf + ied->pos, data, datalen-11);
1213 + ied->pos += datalen-11;
1217 +int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin)
1219 + return dundi_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in));
1222 +int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value)
1224 + unsigned int newval;
1225 + newval = htonl(value);
1226 + return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
1229 +int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value)
1231 + unsigned short newval;
1232 + newval = htons(value);
1233 + return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
1236 +int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, unsigned char *str)
1238 + return dundi_ie_append_raw(ied, ie, str, strlen(str));
1241 +int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid)
1243 + return dundi_ie_append_raw(ied, ie, (unsigned char *)eid, sizeof(dundi_eid));
1246 +int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat)
1248 + return dundi_ie_append_raw(ied, ie, &dat, 1);
1251 +int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie)
1253 + return dundi_ie_append_raw(ied, ie, NULL, 0);
1256 +void dundi_set_output(void (*func)(const char *))
1261 +void dundi_set_error(void (*func)(const char *))
1266 +int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen)
1268 + /* Parse data into information elements */
1272 + memset(ies, 0, (int)sizeof(struct dundi_ies));
1274 + ies->expiration = -1;
1275 + ies->unknowncmd = -1;
1277 + while(datalen >= 2) {
1280 + if (len > datalen - 2) {
1281 + errorf("Information element length exceeds message size\n");
1285 + case DUNDI_IE_EID:
1286 + case DUNDI_IE_EID_DIRECT:
1287 + if (len != (int)sizeof(dundi_eid)) {
1288 + errorf("Improper entity identifer, expecting 6 bytes!\n");
1289 + } else if (ies->eidcount < DUNDI_MAX_STACK) {
1290 + ies->eids[ies->eidcount] = (dundi_eid *)(data + 2);
1291 + ies->eid_direct[ies->eidcount] = (ie == DUNDI_IE_EID_DIRECT);
1294 + errorf("Too many entities in stack!\n");
1296 + case DUNDI_IE_REQEID:
1297 + if (len != (int)sizeof(dundi_eid)) {
1298 + errorf("Improper requested entity identifer, expecting 6 bytes!\n");
1300 + ies->reqeid = (dundi_eid *)(data + 2);
1302 + case DUNDI_IE_CALLED_CONTEXT:
1303 + ies->called_context = data + 2;
1305 + case DUNDI_IE_CALLED_NUMBER:
1306 + ies->called_number = data + 2;
1308 + case DUNDI_IE_ANSWER:
1309 + if (len < sizeof(struct dundi_answer)) {
1310 + snprintf(tmp, (int)sizeof(tmp), "Answer expected to be >=%d bytes long but was %d\n", (int)sizeof(struct dundi_answer), len);
1313 + if (ies->anscount < DUNDI_MAX_ANSWERS)
1314 + ies->answers[ies->anscount++]= (struct dundi_answer *)(data + 2);
1316 + errorf("Ignoring extra answers!\n");
1319 + case DUNDI_IE_TTL:
1320 + if (len != (int)sizeof(unsigned short)) {
1321 + snprintf(tmp, (int)sizeof(tmp), "Expecting ttl to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
1324 + ies->ttl = ntohs(*((unsigned short *)(data + 2)));
1326 + case DUNDI_IE_VERSION:
1327 + if (len != (int)sizeof(unsigned short)) {
1328 + snprintf(tmp, (int)sizeof(tmp), "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
1331 + ies->version = ntohs(*((unsigned short *)(data + 2)));
1333 + case DUNDI_IE_EXPIRATION:
1334 + if (len != (int)sizeof(unsigned short)) {
1335 + snprintf(tmp, (int)sizeof(tmp), "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
1338 + ies->expiration = ntohs(*((unsigned short *)(data + 2)));
1340 + case DUNDI_IE_KEYCRC32:
1341 + if (len != (int)sizeof(unsigned int)) {
1342 + snprintf(tmp, (int)sizeof(tmp), "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
1345 + ies->keycrc32 = ntohl(*((unsigned int *)(data + 2)));
1347 + case DUNDI_IE_UNKNOWN:
1349 + ies->unknowncmd = data[2];
1351 + snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len);
1355 + case DUNDI_IE_CAUSE:
1357 + ies->cause = data[2];
1358 + ies->causestr = data + 3;
1360 + snprintf(tmp, (int)sizeof(tmp), "Expected at least one byte cause, but was %d long\n", len);
1364 + case DUNDI_IE_HINT:
1366 + ies->hint = (struct dundi_hint *)(data + 2);
1368 + snprintf(tmp, (int)sizeof(tmp), "Expected at least two byte hint, but was %d long\n", len);
1372 + case DUNDI_IE_DEPARTMENT:
1373 + ies->q_dept = data + 2;
1375 + case DUNDI_IE_ORGANIZATION:
1376 + ies->q_org = data + 2;
1378 + case DUNDI_IE_LOCALITY:
1379 + ies->q_locality = data + 2;
1381 + case DUNDI_IE_STATE_PROV:
1382 + ies->q_stateprov = data + 2;
1384 + case DUNDI_IE_COUNTRY:
1385 + ies->q_country = data + 2;
1387 + case DUNDI_IE_EMAIL:
1388 + ies->q_email = data + 2;
1390 + case DUNDI_IE_PHONE:
1391 + ies->q_phone = data + 2;
1393 + case DUNDI_IE_IPADDR:
1394 + ies->q_ipaddr = data + 2;
1396 + case DUNDI_IE_ENCDATA:
1397 + /* Recalculate len as the remainder of the message, regardless of
1398 + theoretical length */
1399 + len = datalen - 2;
1400 + if ((len > 16) && !(len % 16)) {
1401 + ies->encblock = (struct dundi_encblock *)(data + 2);
1402 + ies->enclen = len - 16;
1404 + snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted data length %d\n", len);
1408 + case DUNDI_IE_SHAREDKEY:
1410 + ies->encsharedkey = (unsigned char *)(data + 2);
1412 + snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted shared key length %d\n", len);
1416 + case DUNDI_IE_SIGNATURE:
1418 + ies->encsig = (unsigned char *)(data + 2);
1420 + snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted signature length %d\n", len);
1424 + case DUNDI_IE_CACHEBYPASS:
1428 + snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", dundi_ie2str(ie), ie, len);
1431 + /* Overwrite information element with 0, to null terminate previous portion */
1433 + datalen -= (len + 2);
1434 + data += (len + 2);
1436 + /* Null-terminate last field */
1439 + errorf("Invalid information element contents, strange boundary\n");
1444 diff -ruN asterisk-1.0.7-orig/pbx/dundi-parser.h asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.h
1445 --- asterisk-1.0.7-orig/pbx/dundi-parser.h 1970-01-01 01:00:00.000000000 +0100
1446 +++ asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.h 2005-06-02 20:21:37.000000000 +0200
1449 + * Distributed Universal Number Discovery (DUNDi)
1451 + * Copyright (C) 2004, Digium Inc.
1453 + * Written by Mark Spencer <markster@digium.com>
1455 + * This program is Free Software distributed under the terms of
1456 + * of the GNU General Public License.
1459 +#ifndef _DUNDI_PARSER_H
1460 +#define _DUNDI_PARSER_H
1462 +#include <asterisk/dundi.h>
1463 +#include <asterisk/aes.h>
1465 +#define DUNDI_MAX_STACK 512
1466 +#define DUNDI_MAX_ANSWERS 100
1469 + dundi_eid *eids[DUNDI_MAX_STACK + 1];
1470 + int eid_direct[DUNDI_MAX_STACK + 1];
1471 + dundi_eid *reqeid;
1473 + char *called_context;
1474 + char *called_number;
1475 + struct dundi_answer *answers[DUNDI_MAX_ANSWERS + 1];
1476 + struct dundi_hint *hint;
1482 + unsigned char *pubkey;
1484 + unsigned char *q_dept;
1485 + unsigned char *q_org;
1486 + unsigned char *q_locality;
1487 + unsigned char *q_stateprov;
1488 + unsigned char *q_country;
1489 + unsigned char *q_email;
1490 + unsigned char *q_phone;
1491 + unsigned char *q_ipaddr;
1492 + unsigned char *causestr;
1493 + unsigned char *encsharedkey;
1494 + unsigned char *encsig;
1495 + unsigned long keycrc32;
1496 + struct dundi_encblock *encblock;
1501 +struct dundi_ie_data {
1503 + unsigned char buf[8192];
1506 +/* Choose a different function for output */
1507 +extern void dundi_set_output(void (*output)(const char *data));
1508 +/* Choose a different function for errors */
1509 +extern void dundi_set_error(void (*output)(const char *data));
1510 +extern void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen);
1512 +extern const char *dundi_ie2str(int ie);
1514 +extern int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen);
1515 +extern int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin);
1516 +extern int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value);
1517 +extern int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value);
1518 +extern int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, unsigned char *str);
1519 +extern int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid);
1520 +extern int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, unsigned char *desc);
1521 +extern int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, unsigned char *data);
1522 +extern int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, unsigned char *desc);
1523 +extern int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen);
1524 +extern int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat);
1525 +extern int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie);
1526 +extern int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen);
1527 +extern char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid);
1528 +extern char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid);
1529 +extern int dundi_str_to_eid(dundi_eid *eid, char *s);
1530 +extern int dundi_str_short_to_eid(dundi_eid *eid, char *s);
1531 +extern int dundi_eid_zero(dundi_eid *eid);
1532 +extern int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2);
1533 +extern char *dundi_flags2str(char *s, int maxlen, int flags);
1534 +extern char *dundi_hint2str(char *s, int maxlen, int flags);
1536 diff -ruN asterisk-1.0.7-orig/pbx/pbx_dundi.c asterisk-1.0.7-pbx_dundi/pbx/pbx_dundi.c
1537 --- asterisk-1.0.7-orig/pbx/pbx_dundi.c 1970-01-01 01:00:00.000000000 +0100
1538 +++ asterisk-1.0.7-pbx_dundi/pbx/pbx_dundi.c 2005-06-02 20:21:37.000000000 +0200
1541 + * Distributed Universal Number Discovery (DUNDi)
1543 + * Copyright (C) 2004, Digium Inc.
1545 + * Written by Mark Spencer <markster@digium.com>
1547 + * This program is Free Software distributed under the terms of
1548 + * of the GNU General Public License.
1551 +#include <asterisk/file.h>
1552 +#include <asterisk/logger.h>
1553 +#include <asterisk/channel.h>
1554 +#include <asterisk/config.h>
1555 +#include <asterisk/options.h>
1556 +#include <asterisk/pbx.h>
1557 +#include <asterisk/module.h>
1558 +#include <asterisk/frame.h>
1559 +#include <asterisk/file.h>
1560 +#include <asterisk/channel_pvt.h>
1561 +#include <asterisk/cli.h>
1562 +#include <asterisk/lock.h>
1563 +#include <asterisk/md5.h>
1564 +#include <asterisk/dundi.h>
1565 +#include <asterisk/sched.h>
1566 +#include <asterisk/io.h>
1567 +#include <asterisk/utils.h>
1568 +#include <asterisk/crypto.h>
1569 +#include <asterisk/astdb.h>
1570 +#include <asterisk/acl.h>
1571 +#include <asterisk/aes.h>
1573 +#include "dundi-parser.h"
1574 +#include <stdlib.h>
1575 +#include <unistd.h>
1576 +#include <arpa/inet.h>
1577 +#include <netinet/in.h>
1578 +#include <sys/socket.h>
1579 +#include <string.h>
1581 +#if defined(__FreeBSD__) || defined(__NetBSD__)
1582 +#include <sys/types.h>
1583 +#include <netinet/in_systm.h>
1585 +#include <netinet/ip.h>
1586 +#include <sys/ioctl.h>
1587 +#include <netinet/in.h>
1588 +#include <net/if.h>
1589 +#if defined(__FreeBSD__) || defined(__NetBSD__)
1590 +#include <net/if_dl.h>
1591 +#include <ifaddrs.h>
1595 +#define MAX_RESULTS 64
1597 +#define MAX_PACKET_SIZE 8192
1599 +extern char ast_config_AST_KEY_DIR[];
1601 +static char *tdesc = "Distributed Universal Number Discovery (DUNDi)";
1603 +static char *app = "DUNDiLookup";
1604 +static char *synopsis = "Look up a number with DUNDi";
1605 +static char *descrip =
1606 +"DUNDiLookup(number[|context[|options]])\n"
1607 +" Looks up a given number in the global context specified or in\n"
1608 +"the reserved 'e164' context if not specified. Returns -1 if the channel\n"
1609 +"is hungup during the lookup or 0 otherwise. On completion, the variable\n"
1610 +"${DUNDTECH} and ${DUNDDEST} will contain the technology and destination\n"
1611 +"of the appropriate technology and destination to access the number. If no\n"
1612 +"answer was found, and the priority n + 101 exists, execution will continue\n"
1613 +"at that location.\n";
1615 +#define DUNDI_MODEL_INBOUND (1 << 0)
1616 +#define DUNDI_MODEL_OUTBOUND (1 << 1)
1617 +#define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
1619 +/* Keep times of last 10 lookups */
1620 +#define DUNDI_TIMING_HISTORY 10
1622 +#define FLAG_ISREG (1 << 0) /* Transaction is register request */
1623 +#define FLAG_DEAD (1 << 1) /* Transaction is dead */
1624 +#define FLAG_FINAL (1 << 2) /* Transaction has final message sent */
1625 +#define FLAG_ISQUAL (1 << 3) /* Transaction is a qualification */
1626 +#define FLAG_ENCRYPT (1 << 4) /* Transaction is encrypted wiht ECX/DCX */
1627 +#define FLAG_SENDFULLKEY (1 << 5) /* Send full key on transaction */
1628 +#define FLAG_STOREHIST (1 << 6) /* Record historic performance */
1630 +#define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
1633 +#define DUNDI_SECRET_TIME 15 /* Testing only */
1635 +#define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
1641 +static struct io_context *io;
1642 +static struct sched_context *sched;
1643 +static int netsocket = -1;
1644 +static pthread_t netthreadid = AST_PTHREADT_NULL;
1645 +static pthread_t precachethreadid = AST_PTHREADT_NULL;
1646 +static int tos = 0;
1647 +static int dundidebug = 0;
1648 +static int authdebug = 0;
1649 +static int dundi_ttl = DUNDI_DEFAULT_TTL;
1650 +static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
1651 +static int global_autokilltimeout = 0;
1652 +static dundi_eid global_eid;
1653 +static int default_expiration = 60;
1654 +static int global_storehistory = 0;
1655 +static int map_update_interval = 0;
1656 +static int map_updates_per_pkt = 45;
1657 +static int map_peering_sid = -1;
1658 +static int map_contact_sid = -1;
1659 +static char map_context[80];
1660 +static struct sockaddr_in map_addr;
1661 +static char dept[80];
1662 +static char org[80];
1663 +static char locality[80];
1664 +static char stateprov[80];
1665 +static char country[80];
1666 +static char email[80];
1667 +static char phone[80];
1668 +static char secretpath[80];
1669 +static char cursecret[80];
1670 +static char ipaddr[80];
1671 +static time_t rotatetime;
1672 +static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
1673 +struct permission {
1674 + struct permission *next;
1679 +struct dundi_packet {
1680 + struct dundi_hdr *h;
1681 + struct dundi_packet *next;
1683 + struct dundi_transaction *parent;
1686 + unsigned char data[0];
1689 +struct dundi_hint_metadata {
1690 + unsigned short flags;
1691 + char exten[AST_MAX_EXTENSION];
1694 +struct dundi_precache_queue {
1695 + struct dundi_precache_queue *next;
1697 + time_t expiration;
1701 +struct dundi_request;
1703 +struct dundi_transaction {
1704 + struct sockaddr_in addr; /* Other end of transaction */
1705 + struct timeval start; /* When this transaction was created */
1706 + dundi_eid eids[DUNDI_MAX_STACK + 1];
1707 + int eidcount; /* Number of eids in eids */
1708 + dundi_eid us_eid; /* Our EID, to them */
1709 + dundi_eid them_eid; /* Their EID, to us */
1710 + aes_encrypt_ctx ecx; /* AES 128 Encryption context */
1711 + aes_decrypt_ctx dcx; /* AES 128 Decryption context */
1712 + int flags; /* Has final packet been sent */
1713 + int ttl; /* Remaining TTL for queries on this one */
1714 + int thread; /* We have a calling thread */
1715 + int retranstimer; /* How long to wait before retransmissions */
1716 + int autokillid; /* ID to kill connection if answer doesn't come back fast enough */
1717 + int autokilltimeout; /* Recommended timeout for autokill */
1718 + unsigned short strans; /* Our transaction identifier */
1719 + unsigned short dtrans; /* Their transaction identifer */
1720 + unsigned char iseqno; /* Next expected received seqno */
1721 + unsigned char oiseqno; /* Last received incoming seqno */
1722 + unsigned char oseqno; /* Next transmitted seqno */
1723 + unsigned char aseqno; /* Last acknowledge seqno */
1724 + struct dundi_packet *packets; /* Packets to be retransmitted */
1725 + struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
1726 + struct dundi_transaction *next; /* Next with respect to the parent */
1727 + struct dundi_request *parent; /* Parent request (if there is one) */
1728 + struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
1731 +struct dundi_request {
1732 + char dcontext[AST_MAX_EXTENSION];
1733 + char number[AST_MAX_EXTENSION];
1734 + dundi_eid query_eid;
1735 + dundi_eid root_eid;
1736 + struct dundi_result *dr;
1737 + struct dundi_entity_info *dei;
1738 + struct dundi_hint_metadata *hmd;
1744 + unsigned long crc32; /* CRC-32 of all but root EID's in avoid list */
1745 + struct dundi_transaction *trans; /* Transactions */
1746 + struct dundi_request *next;
1749 +static struct dundi_mapping {
1750 + char dcontext[AST_MAX_EXTENSION];
1751 + char lcontext[AST_MAX_EXTENSION];
1756 + char dest[AST_MAX_EXTENSION];
1757 + struct dundi_mapping *next;
1758 +} *mappings = NULL;
1760 +static struct dundi_peer {
1762 + struct sockaddr_in addr; /* Address of DUNDi peer */
1763 + struct permission *permit;
1764 + struct permission *include;
1765 + struct permission *precachesend;
1766 + struct permission *precachereceive;
1775 + unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
1776 + unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
1777 + unsigned long us_keycrc32; /* CRC-32 of our key */
1778 + aes_encrypt_ctx us_ecx; /* Cached AES 128 Encryption context */
1779 + aes_decrypt_ctx us_dcx; /* Cached AES 128 Decryption context */
1780 + unsigned long them_keycrc32;/* CRC-32 of our key */
1781 + aes_encrypt_ctx them_ecx; /* Cached AES 128 Encryption context */
1782 + aes_decrypt_ctx them_dcx; /* Cached AES 128 Decryption context */
1783 + time_t keyexpire; /* When to expire/recreate key */
1784 + int registerexpire;
1785 + int lookuptimes[DUNDI_TIMING_HISTORY];
1786 + char *lookups[DUNDI_TIMING_HISTORY];
1788 + struct dundi_transaction *regtrans; /* Registration transaction */
1789 + struct dundi_transaction *qualtrans; /* Qualify transaction */
1790 + struct dundi_transaction *keypending;
1791 + int model; /* Pull model */
1792 + int pcmodel; /* Push/precache model */
1793 + int dynamic; /* Are we dynamic? */
1794 + int lastms; /* Last measured latency */
1795 + int maxms; /* Max permissible latency */
1796 + struct timeval qualtx; /* Time of transmit */
1797 + struct dundi_peer *next;
1800 +static struct dundi_precache_queue *pcq;
1802 +AST_MUTEX_DEFINE_STATIC(peerlock);
1803 +AST_MUTEX_DEFINE_STATIC(pclock);
1805 +static int dundi_xmit(struct dundi_packet *pack);
1807 +static void dundi_debug_output(const char *data)
1810 + ast_verbose("%s", data);
1813 +static void dundi_error_output(const char *data)
1815 + ast_log(LOG_WARNING, "%s", data);
1818 +static int has_permission(struct permission *ps, char *cont)
1822 + if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
1829 +static char *tech2str(int tech)
1832 + case DUNDI_PROTO_NONE:
1834 + case DUNDI_PROTO_IAX:
1836 + case DUNDI_PROTO_SIP:
1838 + case DUNDI_PROTO_H323:
1845 +static int str2tech(char *str)
1847 + if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
1848 + return DUNDI_PROTO_IAX;
1849 + else if (!strcasecmp(str, "SIP"))
1850 + return DUNDI_PROTO_SIP;
1851 + else if (!strcasecmp(str, "H323"))
1852 + return DUNDI_PROTO_H323;
1857 +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[]);
1858 +static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
1859 +static struct dundi_transaction *create_transaction(struct dundi_peer *p);
1860 +static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
1862 + /* Look for an exact match first */
1863 + struct dundi_transaction *trans;
1866 + if (!inaddrcmp(&trans->addr, sin) &&
1867 + ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
1868 + ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
1870 + trans->dtrans = ntohs(hdr->strans) & 32767;
1873 + trans = trans->allnext;
1876 + switch(hdr->cmdresp & 0x7f) {
1877 + case DUNDI_COMMAND_DPDISCOVER:
1878 + case DUNDI_COMMAND_EIDQUERY:
1879 + case DUNDI_COMMAND_PRECACHERQ:
1880 + case DUNDI_COMMAND_REGREQ:
1881 + case DUNDI_COMMAND_NULL:
1882 + case DUNDI_COMMAND_ENCRYPT:
1883 + if (hdr->strans) {
1884 + /* Create new transaction */
1885 + trans = create_transaction(NULL);
1887 + memcpy(&trans->addr, sin, sizeof(trans->addr));
1888 + trans->dtrans = ntohs(hdr->strans) & 32767;
1890 + ast_log(LOG_WARNING, "Out of memory!\n");
1900 +static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
1902 +static int dundi_ack(struct dundi_transaction *trans, int final)
1904 + return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
1906 +static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
1909 + struct dundi_packet pack;
1910 + struct dundi_hdr hdr;
1912 + struct dundi_transaction trans;
1913 + /* Never respond to an INVALID with another INVALID */
1914 + if (h->cmdresp == DUNDI_COMMAND_INVALID)
1916 + memset(&tmp, 0, sizeof(tmp));
1917 + memset(&trans, 0, sizeof(trans));
1918 + memcpy(&trans.addr, sin, sizeof(trans.addr));
1919 + tmp.hdr.strans = h->dtrans;
1920 + tmp.hdr.dtrans = h->strans;
1921 + tmp.hdr.iseqno = h->oseqno;
1922 + tmp.hdr.oseqno = h->iseqno;
1923 + tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
1924 + tmp.hdr.cmdflags = 0;
1925 + tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
1926 + tmp.pack.datalen = sizeof(struct dundi_hdr);
1927 + tmp.pack.parent = &trans;
1928 + dundi_xmit(&tmp.pack);
1931 +static void reset_global_eid(void)
1933 +#if defined(SIOCGIFHWADDR)
1938 + s = socket(AF_INET, SOCK_STREAM, 0);
1941 + for(x=0;x<10;x++) {
1942 + memset(&ifr, 0, sizeof(ifr));
1943 + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
1944 + if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
1945 + memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
1946 + ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
1952 +#if defined(ifa_broadaddr)
1954 + struct ifaddrs *ifap;
1956 + if (getifaddrs(&ifap) == 0) {
1957 + struct ifaddrs *p;
1958 + for (p = ifap; p; p = p->ifa_next) {
1959 + if (p->ifa_addr->sa_family == AF_LINK) {
1960 + struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
1962 + &(global_eid.eid),
1963 + sdp->sdl_data + sdp->sdl_nlen, 6);
1964 + ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifap->ifa_name);
1965 + freeifaddrs(ifap);
1969 + freeifaddrs(ifap);
1973 + ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
1976 +static int get_trans_id(void)
1978 + struct dundi_transaction *t;
1979 + int stid = (rand() % 32766) + 1;
1984 + if (t->strans == tid)
1990 + tid = (tid % 32766) + 1;
1991 + } while (tid != stid);
1995 +static int reset_transaction(struct dundi_transaction *trans)
1998 + tid = get_trans_id();
2001 + trans->strans = tid;
2002 + trans->dtrans = 0;
2003 + trans->iseqno = 0;
2004 + trans->oiseqno = 0;
2005 + trans->oseqno = 0;
2006 + trans->aseqno = 0;
2007 + trans->flags &= ~FLAG_FINAL;
2011 +static struct dundi_peer *find_peer(dundi_eid *eid)
2013 + struct dundi_peer *cur;
2018 + if (!dundi_eid_cmp(&cur->eid,eid))
2025 +static void build_iv(unsigned char *iv)
2027 + /* XXX Would be nice to be more random XXX */
2028 + unsigned int *fluffy;
2030 + fluffy = (unsigned int *)(iv);
2032 + fluffy[x] = rand();
2035 +struct dundi_query_state {
2036 + dundi_eid *eids[DUNDI_MAX_STACK + 1];
2037 + int directs[DUNDI_MAX_STACK + 1];
2039 + char called_context[AST_MAX_EXTENSION];
2040 + char called_number[AST_MAX_EXTENSION];
2041 + struct dundi_mapping *maps;
2044 + struct dundi_transaction *trans;
2051 +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)
2055 + if (!ast_strlen_zero(map->lcontext)) {
2057 + if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
2058 + flags |= DUNDI_FLAG_EXISTS;
2059 + if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
2060 + flags |= DUNDI_FLAG_CANMATCH;
2061 + if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
2062 + flags |= DUNDI_FLAG_MATCHMORE;
2063 + if (ast_ignore_pattern(map->lcontext, called_number))
2064 + flags |= DUNDI_FLAG_IGNOREPAT;
2066 + /* Clearly we can't say 'don't ask' anymore if we found anything... */
2068 + hmd->flags &= ~DUNDI_HINT_DONT_ASK;
2070 + if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
2071 + /* Skip partial answers */
2072 + flags &= ~(DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
2075 + struct varshead headp;
2076 + struct ast_var_t *newvariable;
2077 + flags |= map->options & 0xffff;
2078 + dr[anscnt].flags = flags;
2079 + dr[anscnt].techint = map->tech;
2080 + dr[anscnt].weight = map->weight;
2081 + dr[anscnt].expiration = DUNDI_DEFAULT_CACHE_TIME;
2082 + strncpy(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
2083 + dr[anscnt].eid = *us_eid;
2084 + dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
2085 + if (flags & DUNDI_FLAG_EXISTS) {
2086 + AST_LIST_HEAD_INIT(&headp);
2087 + newvariable = ast_var_assign("NUMBER", called_number);
2088 + AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
2089 + newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
2090 + AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
2091 + newvariable = ast_var_assign("SECRET", cursecret);
2092 + AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
2093 + newvariable = ast_var_assign("IPADDR", ipaddr);
2094 + AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
2095 + pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
2096 + while (!AST_LIST_EMPTY(&headp)) { /* List Deletion. */
2097 + newvariable = AST_LIST_FIRST(&headp);
2098 + AST_LIST_REMOVE_HEAD(&headp, entries);
2099 + ast_var_delete(newvariable);
2102 + dr[anscnt].dest[0] = '\0';
2105 + /* No answers... Find the fewest number of digits from the
2106 + number for which we have no answer. */
2107 + char tmp[AST_MAX_EXTENSION]="";
2108 + for (x=0;x<AST_MAX_EXTENSION;x++) {
2109 + tmp[x] = called_number[x];
2112 + if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
2113 + /* Oops found something we can't match. If this is longer
2114 + than the running hint, we have to consider it */
2115 + if (strlen(tmp) > strlen(hmd->exten)) {
2116 + strncpy(hmd->exten, tmp, sizeof(hmd->exten) - 1);
2126 +static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
2128 +static void *dundi_lookup_thread(void *data)
2130 + struct dundi_query_state *st = data;
2131 + struct dundi_result dr[MAX_RESULTS];
2132 + struct dundi_ie_data ied;
2133 + struct dundi_hint_metadata hmd;
2138 + int expiration = DUNDI_DEFAULT_CACHE_TIME;
2140 + ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
2141 + st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
2142 + memset(&ied, 0, sizeof(ied));
2143 + memset(&dr, 0, sizeof(dr));
2144 + memset(&hmd, 0, sizeof(hmd));
2145 + /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
2146 + hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
2147 + for (x=0;x<st->nummaps;x++)
2148 + ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
2149 + if (ouranswers < 0)
2151 + for (x=0;x<ouranswers;x++) {
2152 + if (dr[x].weight < max)
2153 + max = dr[x].weight;
2157 + /* If we do not have a canonical result, keep looking */
2158 + 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);
2160 + /* Append answer in result */
2161 + ouranswers += res;
2163 + if ((res < -1) && (!ouranswers))
2164 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
2167 + ast_mutex_lock(&peerlock);
2168 + /* Truncate if "don't ask" isn't present */
2169 + if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
2170 + hmd.exten[0] = '\0';
2171 + if (st->trans->flags & FLAG_DEAD) {
2172 + ast_log(LOG_DEBUG, "Our transaction went away!\n");
2173 + st->trans->thread = 0;
2174 + destroy_trans(st->trans, 0);
2176 + for (x=0;x<ouranswers;x++) {
2178 + if (dr[x].expiration && (expiration > dr[x].expiration))
2179 + expiration = dr[x].expiration;
2180 + dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
2182 + dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
2183 + dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
2184 + dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
2185 + st->trans->thread = 0;
2187 + ast_mutex_unlock(&peerlock);
2192 +static void *dundi_precache_thread(void *data)
2194 + struct dundi_query_state *st = data;
2195 + struct dundi_ie_data ied;
2196 + struct dundi_hint_metadata hmd;
2199 + ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
2200 + st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
2201 + memset(&ied, 0, sizeof(ied));
2203 + /* Now produce precache */
2204 + dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
2206 + ast_mutex_lock(&peerlock);
2207 + /* Truncate if "don't ask" isn't present */
2208 + if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
2209 + hmd.exten[0] = '\0';
2210 + if (st->trans->flags & FLAG_DEAD) {
2211 + ast_log(LOG_DEBUG, "Our transaction went away!\n");
2212 + st->trans->thread = 0;
2213 + destroy_trans(st->trans, 0);
2215 + dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
2216 + st->trans->thread = 0;
2218 + ast_mutex_unlock(&peerlock);
2223 +static inline int calc_ms(struct timeval *start)
2225 + struct timeval tv;
2226 + gettimeofday(&tv, NULL);
2227 + return ((tv.tv_sec - start->tv_sec) * 1000 + (tv.tv_usec - start->tv_usec) / 1000);
2230 +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[]);
2232 +static void *dundi_query_thread(void *data)
2234 + struct dundi_query_state *st = data;
2235 + struct dundi_entity_info dei;
2236 + struct dundi_ie_data ied;
2237 + struct dundi_hint_metadata hmd;
2240 + ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
2241 + st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
2242 + memset(&ied, 0, sizeof(ied));
2243 + memset(&dei, 0, sizeof(dei));
2244 + memset(&hmd, 0, sizeof(hmd));
2245 + if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
2246 + /* Ooh, it's us! */
2247 + ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
2248 + strncpy(dei.orgunit, dept, sizeof(dei.orgunit));
2249 + strncpy(dei.org, org, sizeof(dei.org));
2250 + strncpy(dei.locality, locality, sizeof(dei.locality));
2251 + strncpy(dei.stateprov, stateprov, sizeof(dei.stateprov));
2252 + strncpy(dei.country, country, sizeof(dei.country));
2253 + strncpy(dei.email, email, sizeof(dei.email));
2254 + strncpy(dei.phone, phone, sizeof(dei.phone));
2257 + /* If we do not have a canonical result, keep looking */
2258 + res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
2260 + ast_mutex_lock(&peerlock);
2261 + if (st->trans->flags & FLAG_DEAD) {
2262 + ast_log(LOG_DEBUG, "Our transaction went away!\n");
2263 + st->trans->thread = 0;
2264 + destroy_trans(st->trans, 0);
2267 + dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
2268 + dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
2269 + dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
2270 + dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
2271 + dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
2272 + dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
2273 + dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
2274 + if (!ast_strlen_zero(dei.ipaddr))
2275 + dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
2277 + dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
2278 + dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
2279 + st->trans->thread = 0;
2281 + ast_mutex_unlock(&peerlock);
2286 +static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
2288 + struct dundi_query_state *st;
2292 + struct dundi_ie_data ied;
2295 + pthread_t lookupthread;
2296 + pthread_attr_t attr;
2297 + if (ies->eidcount > 1) {
2298 + /* Since it is a requirement that the first EID is the authenticating host
2299 + and the last EID is the root, it is permissible that the first and last EID
2300 + could be the same. In that case, we should go ahead copy only the "root" section
2301 + since we will not need it for authentication. */
2302 + if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
2305 + totallen = sizeof(struct dundi_query_state);
2306 + totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
2307 + st = malloc(totallen);
2309 + memset(st, 0, totallen);
2310 + strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
2311 + memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
2312 + st->trans = trans;
2313 + st->ttl = ies->ttl - 1;
2317 + for (x=skipfirst;ies->eids[x];x++) {
2318 + st->eids[x-skipfirst] = (dundi_eid *)s;
2319 + *st->eids[x-skipfirst] = *ies->eids[x];
2320 + s += sizeof(dundi_eid);
2322 + 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);
2323 + pthread_attr_init(&attr);
2324 + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2325 + trans->thread = 1;
2326 + if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
2327 + trans->thread = 0;
2328 + ast_log(LOG_WARNING, "Unable to create thread!\n");
2330 + memset(&ied, 0, sizeof(ied));
2331 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
2332 + dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
2336 + ast_log(LOG_WARNING, "Out of memory!\n");
2337 + memset(&ied, 0, sizeof(ied));
2338 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
2339 + dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
2345 +static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
2350 + char eidpeer_str[20];
2351 + char eidroot_str[20];
2355 + if (expiration < 0)
2356 + expiration = DUNDI_DEFAULT_CACHE_TIME;
2358 + /* Only cache hint if "don't ask" is there... */
2359 + if (!(ntohs(hint->flags)& DUNDI_HINT_DONT_ASK))
2362 + unaffected = ntohs(hint->flags) & DUNDI_HINT_UNAFFECTED;
2364 + dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
2365 + dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
2366 + snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
2367 + snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
2370 + timeout += expiration;
2371 + snprintf(data, sizeof(data), "%ld|", (long)(timeout));
2373 + ast_db_put("dundi/cache", key1, data);
2374 + ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
2375 + ast_db_put("dundi/cache", key2, data);
2376 + ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
2380 +static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
2385 + char data[1024]="";
2386 + char eidpeer_str[20];
2387 + char eidroot_str[20];
2390 + if (expiration < 1)
2391 + expiration = DUNDI_DEFAULT_CACHE_TIME;
2393 + /* Keep pushes a little longer, cut pulls a little short */
2398 + if (expiration < 1)
2400 + dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
2401 + dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
2402 + snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
2403 + snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
2404 + /* Build request string */
2406 + timeout += expiration;
2407 + snprintf(data, sizeof(data), "%ld|", (long)(timeout));
2408 + for (x=start;x<req->respcount;x++) {
2409 + /* Skip anything with an illegal pipe in it */
2410 + if (strchr(req->dr[x].dest, '|'))
2412 + snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
2413 + req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
2414 + dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
2416 + ast_db_put("dundi/cache", key1, data);
2417 + ast_db_put("dundi/cache", key2, data);
2421 +static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
2423 + struct dundi_query_state *st;
2426 + struct dundi_ie_data ied;
2428 + struct dundi_result dr2[MAX_RESULTS];
2429 + struct dundi_request dr;
2430 + struct dundi_hint_metadata hmd;
2432 + struct dundi_mapping *cur;
2434 + int skipfirst = 0;
2436 + pthread_t lookupthread;
2437 + pthread_attr_t attr;
2439 + memset(&dr2, 0, sizeof(dr2));
2440 + memset(&dr, 0, sizeof(dr));
2441 + memset(&hmd, 0, sizeof(hmd));
2443 + /* Forge request structure to hold answers for cache */
2444 + hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
2446 + dr.maxcount = MAX_RESULTS;
2447 + dr.expiration = DUNDI_DEFAULT_CACHE_TIME;
2449 + dr.pfds[0] = dr.pfds[1] = -1;
2450 + trans->parent = &dr;
2451 + strncpy(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
2452 + strncpy(dr.number, ies->called_number, sizeof(dr.number) - 1);
2454 + for (x=0;x<ies->anscount;x++) {
2455 + if (trans->parent->respcount < trans->parent->maxcount) {
2456 + /* Make sure it's not already there */
2457 + for (z=0;z<trans->parent->respcount;z++) {
2458 + if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
2459 + !strcmp(trans->parent->dr[z].dest, ies->answers[x]->data))
2462 + if (z == trans->parent->respcount) {
2463 + /* Copy into parent responses */
2464 + trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
2465 + trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
2466 + trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
2467 + trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
2468 + if (ies->expiration > 0)
2469 + trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
2471 + trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
2472 + dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
2473 + sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
2474 + &ies->answers[x]->eid);
2475 + strncpy(trans->parent->dr[trans->parent->respcount].dest, ies->answers[x]->data,
2476 + sizeof(trans->parent->dr[trans->parent->respcount].dest));
2477 + strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
2478 + sizeof(trans->parent->dr[trans->parent->respcount].tech));
2479 + trans->parent->respcount++;
2480 + trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
2481 + } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
2482 + /* Update weight if appropriate */
2483 + trans->parent->dr[z].weight = ies->answers[x]->weight;
2486 + ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
2487 + trans->parent->number, trans->parent->dcontext);
2490 + /* Save all the results (if any) we had. Even if no results, still cache lookup. */
2491 + cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
2493 + cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
2495 + totallen = sizeof(struct dundi_query_state);
2496 + /* Count matching map entries */
2500 + if (!strcasecmp(cur->dcontext, ccontext))
2505 + /* If no maps, return -1 immediately */
2509 + if (ies->eidcount > 1) {
2510 + /* Since it is a requirement that the first EID is the authenticating host
2511 + and the last EID is the root, it is permissible that the first and last EID
2512 + could be the same. In that case, we should go ahead copy only the "root" section
2513 + since we will not need it for authentication. */
2514 + if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
2518 + /* Prepare to run a query and then propagate that as necessary */
2519 + totallen += mapcount * sizeof(struct dundi_mapping);
2520 + totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
2521 + st = malloc(totallen);
2523 + memset(st, 0, totallen);
2524 + strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
2525 + strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
2526 + st->trans = trans;
2527 + st->ttl = ies->ttl - 1;
2528 + st->nocache = ies->cbypass;
2532 + for (x=skipfirst;ies->eids[x];x++) {
2533 + st->eids[x-skipfirst] = (dundi_eid *)s;
2534 + *st->eids[x-skipfirst] = *ies->eids[x];
2535 + st->directs[x-skipfirst] = ies->eid_direct[x];
2536 + s += sizeof(dundi_eid);
2538 + /* Append mappings */
2540 + st->maps = (struct dundi_mapping *)s;
2543 + if (!strcasecmp(cur->dcontext, ccontext)) {
2544 + if (x < mapcount) {
2545 + st->maps[x] = *cur;
2546 + st->maps[x].next = NULL;
2552 + st->nummaps = mapcount;
2553 + ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
2554 + pthread_attr_init(&attr);
2555 + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2556 + trans->thread = 1;
2557 + if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
2558 + trans->thread = 0;
2559 + ast_log(LOG_WARNING, "Unable to create thread!\n");
2561 + memset(&ied, 0, sizeof(ied));
2562 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
2563 + dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
2567 + ast_log(LOG_WARNING, "Out of memory!\n");
2568 + memset(&ied, 0, sizeof(ied));
2569 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
2570 + dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
2576 +static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
2578 + struct dundi_query_state *st;
2581 + struct dundi_ie_data ied;
2583 + struct dundi_mapping *cur;
2585 + int skipfirst = 0;
2587 + pthread_t lookupthread;
2588 + pthread_attr_t attr;
2589 + totallen = sizeof(struct dundi_query_state);
2590 + /* Count matching map entries */
2594 + if (!strcasecmp(cur->dcontext, ccontext))
2598 + /* If no maps, return -1 immediately */
2602 + if (ies->eidcount > 1) {
2603 + /* Since it is a requirement that the first EID is the authenticating host
2604 + and the last EID is the root, it is permissible that the first and last EID
2605 + could be the same. In that case, we should go ahead copy only the "root" section
2606 + since we will not need it for authentication. */
2607 + if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
2611 + totallen += mapcount * sizeof(struct dundi_mapping);
2612 + totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
2613 + st = malloc(totallen);
2615 + memset(st, 0, totallen);
2616 + strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
2617 + strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
2618 + st->trans = trans;
2619 + st->ttl = ies->ttl - 1;
2620 + st->nocache = ies->cbypass;
2624 + for (x=skipfirst;ies->eids[x];x++) {
2625 + st->eids[x-skipfirst] = (dundi_eid *)s;
2626 + *st->eids[x-skipfirst] = *ies->eids[x];
2627 + st->directs[x-skipfirst] = ies->eid_direct[x];
2628 + s += sizeof(dundi_eid);
2630 + /* Append mappings */
2632 + st->maps = (struct dundi_mapping *)s;
2635 + if (!strcasecmp(cur->dcontext, ccontext)) {
2636 + if (x < mapcount) {
2637 + st->maps[x] = *cur;
2638 + st->maps[x].next = NULL;
2644 + st->nummaps = mapcount;
2645 + ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
2646 + pthread_attr_init(&attr);
2647 + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2648 + trans->thread = 1;
2649 + if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
2650 + trans->thread = 0;
2651 + ast_log(LOG_WARNING, "Unable to create thread!\n");
2653 + memset(&ied, 0, sizeof(ied));
2654 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
2655 + dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
2659 + ast_log(LOG_WARNING, "Out of memory!\n");
2660 + memset(&ied, 0, sizeof(ied));
2661 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
2662 + dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
2668 +static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
2670 + char data[1024]="";
2671 + char *ptr, *term, *src;
2680 + /* Build request string */
2681 + if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
2683 + if (sscanf(ptr, "%ld|%n", &timeout, &length) == 1) {
2684 + expiration = timeout - now;
2685 + if (expiration > 0) {
2686 + ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
2688 + while((sscanf(ptr, "%d/%d/%d/%n", &flags, &weight, &tech, &length) == 3)) {
2690 + term = strchr(ptr, '|');
2693 + src = strrchr(ptr, '/');
2699 + ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
2700 + tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags), eid_str_full);
2701 + /* Make sure it's not already there */
2702 + for (z=0;z<req->respcount;z++) {
2703 + if ((req->dr[z].techint == tech) &&
2704 + !strcmp(req->dr[z].dest, ptr))
2707 + if (z == req->respcount) {
2708 + /* Copy into parent responses */
2709 + req->dr[req->respcount].flags = flags;
2710 + req->dr[req->respcount].weight = weight;
2711 + req->dr[req->respcount].techint = tech;
2712 + req->dr[req->respcount].expiration = expiration;
2713 + dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
2714 + dundi_eid_to_str(req->dr[req->respcount].eid_str,
2715 + sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
2716 + strncpy(req->dr[req->respcount].dest, ptr,
2717 + sizeof(req->dr[req->respcount].dest));
2718 + strncpy(req->dr[req->respcount].tech, tech2str(tech),
2719 + sizeof(req->dr[req->respcount].tech));
2721 + req->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
2722 + } else if (req->dr[z].weight > weight)
2723 + req->dr[z].weight = weight;
2727 + /* We found *something* cached */
2728 + if (expiration < *lowexpiration)
2729 + *lowexpiration = expiration;
2732 + ast_db_del("dundi/cache", key);
2734 + ast_db_del("dundi/cache", key);
2740 +static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
2744 + char eidroot_str[20];
2748 + char eid_str_full[20];
2753 + dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
2754 + dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
2755 + dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
2756 + snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
2757 + res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2758 + snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
2759 + res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2760 + snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
2761 + res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2763 + if (!req->respcount) {
2765 + /* Look and see if we have a hint that would preclude us from looking at this
2766 + peer for this number. */
2767 + if (!(tmp[x] = req->number[x]))
2770 + /* Check for hints */
2771 + snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
2772 + res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2773 + snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
2774 + res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2775 + snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
2776 + res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2778 + if (strlen(tmp) > strlen(req->hmd->exten)) {
2779 + /* Update meta data if appropriate */
2780 + strncpy(req->hmd->exten, tmp, sizeof(req->hmd->exten) - 1);
2790 +static void qualify_peer(struct dundi_peer *peer, int schedonly);
2792 +static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
2794 + if (!trans->addr.sin_addr.s_addr)
2795 + memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
2796 + trans->us_eid = p->us_eid;
2797 + trans->them_eid = p->eid;
2798 + /* Enable encryption if appropriate */
2799 + if (!ast_strlen_zero(p->inkey))
2800 + trans->flags |= FLAG_ENCRYPT;
2802 + trans->autokilltimeout = p->maxms;
2803 + trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2804 + if (p->lastms > 1) {
2805 + trans->retranstimer = p->lastms * 2;
2806 + /* Keep it from being silly */
2807 + if (trans->retranstimer < 150)
2808 + trans->retranstimer = 150;
2810 + if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
2811 + trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2813 + trans->autokilltimeout = global_autokilltimeout;
2816 +static int do_register_expire(void *data)
2818 + struct dundi_peer *peer = data;
2820 + /* Called with peerlock already held */
2821 + ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2822 + peer->registerexpire = -1;
2824 + memset(&peer->addr, 0, sizeof(peer->addr));
2828 +static int update_key(struct dundi_peer *peer)
2830 + unsigned char key[16];
2831 + struct ast_key *ekey, *skey;
2834 + if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
2836 + aes_encrypt_key128(key, &peer->us_ecx);
2837 + aes_decrypt_key128(key, &peer->us_dcx);
2838 + ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
2840 + ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
2841 + peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2844 + skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
2846 + ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
2847 + peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2850 + if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
2851 + ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
2854 + if ((res = ast_sign_bin(skey, peer->txenckey, 128, peer->txenckey + 128))) {
2855 + ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
2858 + peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
2859 + peer->sentfullkey = 0;
2861 + time(&peer->keyexpire);
2862 + peer->keyexpire += dundi_key_ttl;
2867 +static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
2869 + unsigned char curblock[16];
2871 + memcpy(curblock, iv, sizeof(curblock));
2873 + for (x=0;x<16;x++)
2874 + curblock[x] ^= src[x];
2875 + aes_encrypt(curblock, dst, ecx);
2876 + memcpy(curblock, dst, sizeof(curblock));
2883 +static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
2885 + unsigned char lastblock[16];
2887 + memcpy(lastblock, iv, sizeof(lastblock));
2889 + aes_decrypt(src, dst, dcx);
2890 + for (x=0;x<16;x++)
2891 + dst[x] ^= lastblock[x];
2892 + memcpy(lastblock, src, sizeof(lastblock));
2900 +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)
2902 + int space = *dstlen;
2903 + unsigned long bytes;
2904 + struct dundi_hdr *h;
2905 + unsigned char *decrypt_space;
2906 + decrypt_space = alloca(srclen);
2907 + if (!decrypt_space)
2909 + decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
2910 + /* Setup header */
2911 + h = (struct dundi_hdr *)dst;
2913 + bytes = space - 6;
2914 + if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
2915 + ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
2918 + /* Update length */
2919 + *dstlen = bytes + 6;
2920 + /* Return new header */
2924 +static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
2926 + unsigned char *compress_space;
2929 + unsigned long bytes;
2930 + struct dundi_ie_data ied;
2931 + struct dundi_peer *peer;
2932 + unsigned char iv[16];
2933 + len = pack->datalen + pack->datalen / 100 + 42;
2934 + compress_space = alloca(len);
2935 + if (compress_space) {
2936 + memset(compress_space, 0, len);
2937 + /* We care about everthing save the first 6 bytes of header */
2939 + res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
2940 + if (res != Z_OK) {
2941 + ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
2944 + memset(&ied, 0, sizeof(ied));
2945 + /* Say who we are */
2946 + if (!pack->h->iseqno && !pack->h->oseqno) {
2947 + /* Need the key in the first copy */
2948 + if (!(peer = find_peer(&trans->them_eid)))
2950 + if (update_key(peer))
2952 + if (!peer->sentfullkey)
2953 + trans->flags |= FLAG_SENDFULLKEY;
2954 + /* Append key data */
2955 + dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
2956 + if (trans->flags & FLAG_SENDFULLKEY) {
2957 + dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
2958 + dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
2960 + dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
2962 + /* Setup contexts */
2963 + trans->ecx = peer->us_ecx;
2964 + trans->dcx = peer->us_dcx;
2966 + /* We've sent the full key */
2967 + peer->sentfullkey = 1;
2969 + /* Build initialization vector */
2971 + /* Add the field, rounded up to 16 bytes */
2972 + dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
2973 + /* Copy the data */
2974 + if ((ied.pos + bytes) >= sizeof(ied.buf)) {
2975 + ast_log(LOG_NOTICE, "Final packet too large!\n");
2978 + encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
2979 + ied.pos += ((bytes + 15) / 16) * 16;
2980 + /* Reconstruct header */
2981 + pack->datalen = sizeof(struct dundi_hdr);
2982 + pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
2983 + pack->h->cmdflags = 0;
2984 + memcpy(pack->h->ies, ied.buf, ied.pos);
2985 + pack->datalen += ied.pos;
2991 +static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
2993 + unsigned char dst[128];
2995 + struct ast_key *key, *skey;
2998 + ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
2999 + if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
3002 + } else if (!newkey || !newsig)
3004 + if (!memcmp(peer->rxenckey, newkey, 128) &&
3005 + !memcmp(peer->rxenckey + 128, newsig, 128)) {
3006 + /* By definition, a match */
3010 + key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
3012 + ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
3013 + peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3017 + skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
3019 + ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
3020 + peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3024 + /* First check signature */
3025 + res = ast_check_signature_bin(skey, newkey, 128, newsig);
3029 + res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
3032 + ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
3035 + /* Decrypted, passes signature */
3036 + ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
3037 + memcpy(peer->rxenckey, newkey, 128);
3038 + memcpy(peer->rxenckey + 128, newsig, 128);
3039 + peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
3040 + aes_decrypt_key128(dst, &peer->them_dcx);
3041 + aes_encrypt_key128(dst, &peer->them_ecx);
3045 +static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
3047 + /* Handle canonical command / response */
3048 + int final = hdr->cmdresp & 0x80;
3049 + int cmd = hdr->cmdresp & 0x7f;
3054 + unsigned char *bufcpy;
3055 + struct dundi_ie_data ied;
3056 + struct dundi_ies ies;
3057 + struct dundi_peer *peer;
3059 + char eid_str2[20];
3060 + memset(&ied, 0, sizeof(ied));
3061 + memset(&ies, 0, sizeof(ies));
3063 + bufcpy = alloca(datalen);
3066 + /* Make a copy for parsing */
3067 + memcpy(bufcpy, hdr->ies, datalen);
3068 + ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
3069 + if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
3070 + ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
3075 + case DUNDI_COMMAND_DPDISCOVER:
3076 + case DUNDI_COMMAND_EIDQUERY:
3077 + case DUNDI_COMMAND_PRECACHERQ:
3078 + if (cmd == DUNDI_COMMAND_EIDQUERY)
3079 + resp = DUNDI_COMMAND_EIDRESPONSE;
3080 + else if (cmd == DUNDI_COMMAND_PRECACHERQ)
3081 + resp = DUNDI_COMMAND_PRECACHERP;
3083 + resp = DUNDI_COMMAND_DPRESPONSE;
3084 + /* A dialplan or entity discover -- qualify by highest level entity */
3085 + peer = find_peer(ies.eids[0]);
3087 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
3088 + dundi_send(trans, resp, 0, 1, &ied);
3091 + trans->us_eid = peer->us_eid;
3092 + if (strlen(peer->inkey)) {
3093 + hasauth = encrypted;
3097 + /* Okay we're authentiated and all, now we check if they're authorized */
3098 + if (!ies.called_context)
3099 + ies.called_context = "e164";
3100 + if (cmd == DUNDI_COMMAND_EIDQUERY) {
3101 + res = dundi_answer_entity(trans, &ies, ies.called_context);
3103 + if (!ies.called_number || ast_strlen_zero(ies.called_number)) {
3104 + /* They're not permitted to access that context */
3105 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
3106 + dundi_send(trans, resp, 0, 1, &ied);
3107 + } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
3108 + (peer->model & DUNDI_MODEL_INBOUND) &&
3109 + has_permission(peer->permit, ies.called_context)) {
3110 + res = dundi_answer_query(trans, &ies, ies.called_context);
3112 + /* There is no such dundi context */
3113 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
3114 + dundi_send(trans, resp, 0, 1, &ied);
3116 + } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
3117 + (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
3118 + has_permission(peer->include, ies.called_context)) {
3119 + res = dundi_prop_precache(trans, &ies, ies.called_context);
3121 + /* There is no such dundi context */
3122 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
3123 + dundi_send(trans, resp, 0, 1, &ied);
3126 + /* They're not permitted to access that context */
3127 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
3128 + dundi_send(trans, resp, 0, 1, &ied);
3132 + /* They're not permitted to access that context */
3133 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
3134 + dundi_send(trans, resp, 0, 1, &ied);
3138 + case DUNDI_COMMAND_REGREQ:
3139 + /* A register request -- should only have one entity */
3140 + peer = find_peer(ies.eids[0]);
3141 + if (!peer || !peer->dynamic) {
3142 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
3143 + dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
3146 + trans->us_eid = peer->us_eid;
3147 + if (!ast_strlen_zero(peer->inkey)) {
3148 + hasauth = encrypted;
3152 + int expire = default_expiration;
3153 + char iabuf[INET_ADDRSTRLEN];
3156 + if (peer->registerexpire > -1)
3157 + ast_sched_del(sched, peer->registerexpire);
3158 + peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
3159 + ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
3160 + snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
3161 + ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
3162 + if (inaddrcmp(&peer->addr, &trans->addr)) {
3163 + if (option_verbose > 2)
3164 + ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), iabuf, ntohs(trans->addr.sin_port));
3168 + memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
3169 + dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
3170 + dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
3172 + qualify_peer(peer, 1);
3176 + case DUNDI_COMMAND_DPRESPONSE:
3177 + /* A dialplan response, lets see what we got... */
3178 + if (ies.cause < 1) {
3179 + /* Success of some sort */
3180 + ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
3181 + if (trans->flags & FLAG_ENCRYPT) {
3182 + authpass = encrypted;
3186 + /* Pass back up answers */
3187 + if (trans->parent && trans->parent->dr) {
3188 + y = trans->parent->respcount;
3189 + for (x=0;x<ies.anscount;x++) {
3190 + if (trans->parent->respcount < trans->parent->maxcount) {
3191 + /* Make sure it's not already there */
3192 + for (z=0;z<trans->parent->respcount;z++) {
3193 + if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
3194 + !strcmp(trans->parent->dr[z].dest, ies.answers[x]->data))
3197 + if (z == trans->parent->respcount) {
3198 + /* Copy into parent responses */
3199 + trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
3200 + trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
3201 + trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
3202 + trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
3203 + if (ies.expiration > 0)
3204 + trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
3206 + trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
3207 + dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
3208 + sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
3209 + &ies.answers[x]->eid);
3210 + strncpy(trans->parent->dr[trans->parent->respcount].dest, ies.answers[x]->data,
3211 + sizeof(trans->parent->dr[trans->parent->respcount].dest));
3212 + strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
3213 + sizeof(trans->parent->dr[trans->parent->respcount].tech));
3214 + trans->parent->respcount++;
3215 + trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
3216 + } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
3217 + /* Update weight if appropriate */
3218 + trans->parent->dr[z].weight = ies.answers[x]->weight;
3221 + ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
3222 + trans->parent->number, trans->parent->dcontext);
3224 + /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
3225 + the cache know if this request was unaffected by our entity list. */
3226 + cache_save(&trans->them_eid, trans->parent, y,
3227 + ies.hint ? ntohs(ies.hint->flags) & DUNDI_HINT_UNAFFECTED : 0, ies.expiration, 0);
3229 + cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
3230 + if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
3231 + trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
3232 + if (ntohs(ies.hint->flags) & DUNDI_HINT_DONT_ASK) {
3233 + if (strlen(ies.hint->data) > strlen(trans->parent->hmd->exten)) {
3234 + strncpy(trans->parent->hmd->exten, ies.hint->data,
3235 + sizeof(trans->parent->hmd->exten) - 1);
3238 + trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
3241 + if (ies.expiration > 0) {
3242 + if (trans->parent->expiration > ies.expiration) {
3243 + trans->parent->expiration = ies.expiration;
3247 + /* Close connection if not final */
3249 + dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3253 + /* Auth failure, check for data */
3255 + /* Cancel if they didn't already */
3256 + dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3260 + case DUNDI_COMMAND_EIDRESPONSE:
3261 + /* A dialplan response, lets see what we got... */
3262 + if (ies.cause < 1) {
3263 + /* Success of some sort */
3264 + ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
3265 + if (trans->flags & FLAG_ENCRYPT) {
3266 + authpass = encrypted;
3270 + /* Pass back up answers */
3271 + if (trans->parent && trans->parent->dei && ies.q_org) {
3272 + if (!trans->parent->respcount) {
3273 + trans->parent->respcount++;
3275 + strncpy(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit) - 1);
3277 + strncpy(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org) - 1);
3278 + if (ies.q_locality)
3279 + strncpy(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality) - 1);
3280 + if (ies.q_stateprov)
3281 + strncpy(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov) - 1);
3282 + if (ies.q_country)
3283 + strncpy(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country) - 1);
3285 + strncpy(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email) - 1);
3287 + strncpy(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone) - 1);
3289 + strncpy(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr) - 1);
3290 + if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
3291 + /* If it's them, update our address */
3292 + ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
3293 + trans->addr.sin_addr);
3297 + if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
3298 + trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
3301 + /* Close connection if not final */
3303 + dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3307 + /* Auth failure, check for data */
3309 + /* Cancel if they didn't already */
3310 + dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3314 + case DUNDI_COMMAND_REGRESPONSE:
3315 + /* A dialplan response, lets see what we got... */
3316 + if (ies.cause < 1) {
3318 + /* Success of some sort */
3319 + if (trans->flags & FLAG_ENCRYPT) {
3320 + hasauth = encrypted;
3325 + ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
3327 + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
3328 + dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
3331 + 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),
3332 + dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
3333 + /* Close connection if not final */
3335 + dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3338 + /* Auth failure, cancel if they didn't for some reason */
3340 + dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3344 + case DUNDI_COMMAND_INVALID:
3345 + case DUNDI_COMMAND_NULL:
3346 + case DUNDI_COMMAND_PRECACHERP:
3347 + /* Do nothing special */
3349 + dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3351 + case DUNDI_COMMAND_ENCREJ:
3352 + if ((trans->flags & FLAG_SENDFULLKEY) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
3353 + /* No really, it's over at this point */
3355 + dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3357 + /* Send with full key */
3358 + trans->flags |= FLAG_SENDFULLKEY;
3360 + /* Ooops, we got a final message, start by sending ACK... */
3361 + dundi_ack(trans, hdr->cmdresp & 0x80);
3362 + trans->aseqno = trans->iseqno;
3363 + /* Now, we gotta create a new transaction */
3364 + if (!reset_transaction(trans)) {
3365 + /* Make sure handle_frame doesn't destroy us */
3366 + hdr->cmdresp &= 0x7f;
3367 + /* Parse the message we transmitted */
3368 + memset(&ies, 0, sizeof(ies));
3369 + dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
3370 + /* Reconstruct outgoing encrypted packet */
3371 + memset(&ied, 0, sizeof(ied));
3372 + dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3373 + dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
3374 + dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
3376 + dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
3377 + dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
3378 + peer->sentfullkey = 1;
3383 + case DUNDI_COMMAND_ENCRYPT:
3385 + /* No nested encryption! */
3386 + if ((trans->iseqno == 1) && !trans->oseqno) {
3387 + if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
3388 + ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
3389 + (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
3391 + dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
3395 + apply_peer(trans, peer);
3396 + /* Key passed, use new contexts for this session */
3397 + trans->ecx = peer->them_ecx;
3398 + trans->dcx = peer->them_dcx;
3400 + if ((trans->flags & FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
3401 + struct dundi_hdr *dhdr;
3402 + unsigned char decoded[MAX_PACKET_SIZE];
3404 + ddatalen = sizeof(decoded);
3405 + dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
3407 + /* Handle decrypted response */
3409 + dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
3410 + handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
3411 + /* Carry back final flag */
3412 + hdr->cmdresp |= dhdr->cmdresp & 0x80;
3415 + ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
3419 + /* Turn off encryption */
3420 + trans->flags &= ~FLAG_ENCRYPT;
3421 + dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
3425 + /* Send unknown command if we don't know it, with final flag IFF it's the
3426 + first command in the dialog and only if we haven't recieved final notification */
3428 + dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
3429 + dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
3435 +static void destroy_packet(struct dundi_packet *pack, int needfree);
3436 +static void destroy_packets(struct dundi_packet *p)
3438 + struct dundi_packet *prev;
3442 + if (prev->retransid > -1)
3443 + ast_sched_del(sched, prev->retransid);
3449 +static int ack_trans(struct dundi_transaction *trans, int iseqno)
3451 + /* Ack transmitted packet corresponding to iseqno */
3452 + struct dundi_packet *pack;
3453 + pack = trans->packets;
3455 + if ((pack->h->oseqno + 1) % 255 == iseqno) {
3456 + destroy_packet(pack, 0);
3457 + if (trans->lasttrans) {
3458 + ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
3459 + destroy_packets(trans->lasttrans);
3461 + trans->lasttrans = pack;
3462 + if (trans->autokillid > -1)
3463 + ast_sched_del(sched, trans->autokillid);
3464 + trans->autokillid = -1;
3467 + pack = pack->next;
3472 +static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
3474 + struct dundi_transaction *trans;
3475 + trans = find_transaction(h, sin);
3477 + dundi_reject(h, sin);
3480 + /* Got a transaction, see where this header fits in */
3481 + if (h->oseqno == trans->iseqno) {
3482 + /* Just what we were looking for... Anything but ack increments iseqno */
3483 + if (ack_trans(trans, h->iseqno) && (trans->flags & FLAG_FINAL)) {
3484 + /* If final, we're done */
3485 + destroy_trans(trans, 0);
3488 + if (h->cmdresp != DUNDI_COMMAND_ACK) {
3489 + trans->oiseqno = trans->iseqno;
3491 + handle_command_response(trans, h, datalen, 0);
3493 + if (trans->aseqno != trans->iseqno) {
3494 + dundi_ack(trans, h->cmdresp & 0x80);
3495 + trans->aseqno = trans->iseqno;
3497 + /* Delete any saved last transmissions */
3498 + destroy_packets(trans->lasttrans);
3499 + trans->lasttrans = NULL;
3500 + if (h->cmdresp & 0x80) {
3501 + /* Final -- destroy now */
3502 + destroy_trans(trans, 0);
3504 + } else if (h->oseqno == trans->oiseqno) {
3505 + /* Last incoming sequence number -- send ACK without processing */
3506 + dundi_ack(trans, 0);
3508 + /* Out of window -- simply drop */
3509 + ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
3514 +static int socket_read(int *id, int fd, short events, void *cbdata)
3516 + struct sockaddr_in sin;
3518 + struct dundi_hdr *h;
3519 + unsigned char buf[MAX_PACKET_SIZE];
3521 + len = sizeof(sin);
3522 + res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
3524 + if (errno != ECONNREFUSED)
3525 + ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
3528 + if (res < sizeof(struct dundi_hdr)) {
3529 + ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
3533 + h = (struct dundi_hdr *)buf;
3535 + dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
3536 + ast_mutex_lock(&peerlock);
3537 + handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
3538 + ast_mutex_unlock(&peerlock);
3542 +static void build_secret(char *secret, int seclen)
3548 + ast_base64encode(secret ,tmp, sizeof(tmp), seclen);
3549 + /* Eliminate potential bad characters */
3550 + while((s = strchr(secret, ';'))) *s = '+';
3551 + while((s = strchr(secret, '/'))) *s = '+';
3552 + while((s = strchr(secret, ':'))) *s = '+';
3553 + while((s = strchr(secret, '@'))) *s = '+';
3557 +static void save_secret(const char *newkey, const char *oldkey)
3561 + snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
3563 + snprintf(tmp, sizeof(tmp), "%s", newkey);
3564 + rotatetime = time(NULL) + DUNDI_SECRET_TIME;
3565 + ast_db_put(secretpath, "secret", tmp);
3566 + snprintf(tmp, sizeof(tmp), "%ld", rotatetime);
3567 + ast_db_put(secretpath, "secretexpiry", tmp);
3570 +static void load_password(void)
3572 + char *current=NULL;
3577 + ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
3578 + if (sscanf(tmp, "%ld", &expired) == 1) {
3579 + ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
3580 + current = strchr(tmp, ';');
3587 + if ((time(NULL) - expired) < 0) {
3588 + if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
3589 + expired = time(NULL) + DUNDI_SECRET_TIME;
3590 + } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
3599 + /* Current key is still valid, just setup rotatation properly */
3600 + strncpy(cursecret, current, sizeof(cursecret) - 1);
3601 + rotatetime = expired;
3603 + /* Current key is out of date, rotate or eliminate all together */
3604 + build_secret(cursecret, sizeof(cursecret));
3605 + save_secret(cursecret, last);
3609 +static void check_password(void)
3611 + char oldsecret[80];
3616 + printf("%ld/%ld\n", now, rotatetime);
3618 + if ((now - rotatetime) >= 0) {
3619 + /* Time to rotate keys */
3620 + strncpy(oldsecret, cursecret, sizeof(oldsecret) - 1);
3621 + build_secret(cursecret, sizeof(cursecret));
3622 + save_secret(cursecret, oldsecret);
3626 +static void *network_thread(void *ignore)
3628 + /* Our job is simple: Send queued messages, retrying if necessary. Read frames
3629 + from the network, and queue them for delivery to the channels */
3631 + /* Establish I/O callback for socket read */
3632 + ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
3634 + res = ast_sched_wait(sched);
3635 + if ((res > 1000) || (res < 0))
3637 + res = ast_io_wait(io, res);
3639 + ast_mutex_lock(&peerlock);
3640 + ast_sched_runq(sched);
3641 + ast_mutex_unlock(&peerlock);
3648 +static void *process_precache(void *ign)
3650 + struct dundi_precache_queue *qe;
3652 + char context[256]="";
3653 + char number[256]="";
3658 + ast_mutex_lock(&pclock);
3660 + if (!pcq->expiration) {
3661 + /* Gone... Remove... */
3665 + } else if (pcq->expiration < now) {
3666 + /* Process this entry */
3667 + pcq->expiration = 0;
3668 + strncpy(context, pcq->context, sizeof(context) - 1);
3669 + strncpy(number, pcq->number, sizeof(number) - 1);
3673 + ast_mutex_unlock(&pclock);
3675 + dundi_precache(context, number);
3682 +static int start_network_thread(void)
3684 + ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
3685 + ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
3689 +static int dundi_do_debug(int fd, int argc, char *argv[])
3692 + return RESULT_SHOWUSAGE;
3694 + ast_cli(fd, "DUNDi Debugging Enabled\n");
3695 + return RESULT_SUCCESS;
3698 +static int dundi_do_store_history(int fd, int argc, char *argv[])
3701 + return RESULT_SHOWUSAGE;
3702 + global_storehistory = 1;
3703 + ast_cli(fd, "DUNDi History Storage Enabled\n");
3704 + return RESULT_SUCCESS;
3707 +static int dundi_flush(int fd, int argc, char *argv[])
3710 + if ((argc < 2) || (argc > 3))
3711 + return RESULT_SHOWUSAGE;
3713 + if (!strcasecmp(argv[2], "stats"))
3716 + return RESULT_SHOWUSAGE;
3719 + /* Flush statistics */
3720 + struct dundi_peer *p;
3722 + ast_mutex_lock(&peerlock);
3725 + for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
3726 + if (p->lookups[x])
3727 + free(p->lookups[x]);
3728 + p->lookups[x] = NULL;
3729 + p->lookuptimes[x] = 0;
3734 + ast_mutex_unlock(&peerlock);
3736 + ast_db_deltree("dundi/cache", NULL);
3737 + ast_cli(fd, "DUNDi Cache Flushed\n");
3739 + return RESULT_SUCCESS;
3742 +static int dundi_no_debug(int fd, int argc, char *argv[])
3745 + return RESULT_SHOWUSAGE;
3747 + ast_cli(fd, "DUNDi Debugging Disabled\n");
3748 + return RESULT_SUCCESS;
3751 +static int dundi_no_store_history(int fd, int argc, char *argv[])
3754 + return RESULT_SHOWUSAGE;
3755 + global_storehistory = 0;
3756 + ast_cli(fd, "DUNDi History Storage Disabled\n");
3757 + return RESULT_SUCCESS;
3760 +static char *model2str(int model)
3763 + case DUNDI_MODEL_INBOUND:
3765 + case DUNDI_MODEL_OUTBOUND:
3766 + return "Outbound";
3767 + case DUNDI_MODEL_SYMMETRIC:
3768 + return "Symmetric";
3774 +static char *complete_peer_helper(char *line, char *word, int pos, int state, int rpos)
3778 + struct dundi_peer *p;
3782 + ast_mutex_lock(&peerlock);
3785 + if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
3786 + if (++which > state)
3792 + ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
3795 + ast_mutex_unlock(&peerlock);
3799 +static char *complete_peer_4(char *line, char *word, int pos, int state)
3801 + return complete_peer_helper(line, word, pos, state, 3);
3804 +static int rescomp(const void *a, const void *b)
3806 + const struct dundi_result *resa, *resb;
3809 + if (resa->weight < resb->weight)
3811 + if (resa->weight > resb->weight)
3816 +static void sort_results(struct dundi_result *results, int count)
3818 + qsort(results, count, sizeof(results[0]), rescomp);
3821 +static int dundi_do_lookup(int fd, int argc, char *argv[])
3824 + char tmp[256] = "";
3829 + struct dundi_result dr[MAX_RESULTS];
3830 + struct timeval start;
3831 + if ((argc < 3) || (argc > 4))
3832 + return RESULT_SHOWUSAGE;
3834 + if (!strcasecmp(argv[3], "bypass"))
3837 + return RESULT_SHOWUSAGE;
3839 + strncpy(tmp, argv[2], sizeof(tmp) - 1);
3840 + context = strchr(tmp, '@');
3845 + gettimeofday(&start, NULL);
3846 + res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
3849 + ast_cli(fd, "DUNDi lookup returned error.\n");
3851 + ast_cli(fd, "DUNDi lookup returned no results.\n");
3853 + sort_results(dr, res);
3854 + for (x=0;x<res;x++) {
3855 + 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));
3856 + ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
3858 + ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
3859 + return RESULT_SUCCESS;
3862 +static int dundi_do_precache(int fd, int argc, char *argv[])
3865 + char tmp[256] = "";
3867 + struct timeval start;
3868 + if ((argc < 3) || (argc > 3))
3869 + return RESULT_SHOWUSAGE;
3870 + strncpy(tmp, argv[2], sizeof(tmp) - 1);
3871 + context = strchr(tmp, '@');
3876 + gettimeofday(&start, NULL);
3877 + res = dundi_precache(context, tmp);
3880 + ast_cli(fd, "DUNDi precache returned error.\n");
3882 + ast_cli(fd, "DUNDi precache returned no error.\n");
3883 + ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
3884 + return RESULT_SUCCESS;
3887 +static int dundi_do_query(int fd, int argc, char *argv[])
3890 + char tmp[256] = "";
3893 + struct dundi_entity_info dei;
3894 + if ((argc < 3) || (argc > 3))
3895 + return RESULT_SHOWUSAGE;
3896 + if (dundi_str_to_eid(&eid, argv[2])) {
3897 + ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
3898 + return RESULT_SHOWUSAGE;
3900 + strncpy(tmp, argv[2], sizeof(tmp) - 1);
3901 + context = strchr(tmp, '@');
3906 + res = dundi_query_eid(&dei, context, eid);
3908 + ast_cli(fd, "DUNDi Query EID returned error.\n");
3910 + ast_cli(fd, "DUNDi Query EID returned no results.\n");
3912 + ast_cli(fd, "DUNDi Query EID succeeded:\n");
3913 + ast_cli(fd, "Department: %s\n", dei.orgunit);
3914 + ast_cli(fd, "Organization: %s\n", dei.org);
3915 + ast_cli(fd, "City/Locality: %s\n", dei.locality);
3916 + ast_cli(fd, "State/Province: %s\n", dei.stateprov);
3917 + ast_cli(fd, "Country: %s\n", dei.country);
3918 + ast_cli(fd, "E-mail: %s\n", dei.email);
3919 + ast_cli(fd, "Phone: %s\n", dei.phone);
3920 + ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
3922 + return RESULT_SUCCESS;
3925 +static int dundi_show_peer(int fd, int argc, char *argv[])
3927 + struct dundi_peer *peer;
3928 + struct permission *p;
3930 + char iabuf[INET_ADDRSTRLEN];
3935 + return RESULT_SHOWUSAGE;
3936 + ast_mutex_lock(&peerlock);
3939 + if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
3941 + peer = peer->next;
3944 + switch(peer->order) {
3946 + order = "Primary";
3949 + order = "Secondary";
3952 + order = "Tertiary";
3955 + order = "Quartiary";
3958 + order = "Unknown";
3960 + ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3961 + ast_cli(fd, "Model: %s\n", model2str(peer->model));
3962 + ast_cli(fd, "Order: %s\n", order);
3963 + ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
3964 + ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
3965 + ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
3966 + ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
3967 + ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
3968 + ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
3969 + if (peer->include) {
3970 + ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
3972 + p = peer->include;
3974 + ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
3977 + if (peer->permit) {
3978 + ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
3982 + ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
3986 + for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
3987 + if (peer->lookups[x]) {
3989 + ast_cli(fd, "Last few query times:\n");
3990 + ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
3995 + ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
3997 + ast_cli(fd, "No such peer '%s'\n", argv[3]);
3998 + ast_mutex_unlock(&peerlock);
3999 + return RESULT_SUCCESS;
4002 +static int dundi_show_peers(int fd, int argc, char *argv[])
4004 +#define FORMAT2 "%-20.20s %-15.15s %-10.10s %-10.10s %-8.8s %-15.15s\n"
4005 +#define FORMAT "%-20.20s %-15.15s %s %-10.10s %-10.10s %-8.8s %-15.15s\n"
4006 + struct dundi_peer *peer;
4007 + char iabuf[INET_ADDRSTRLEN];
4008 + int registeredonly=0;
4011 + char *order = NULL;
4013 + if ((argc != 3) && (argc != 4) && (argc != 5))
4014 + return RESULT_SHOWUSAGE;
4015 + if ((argc == 4)) {
4016 + if (!strcasecmp(argv[3], "registered")) {
4017 + registeredonly = 1;
4019 + return RESULT_SHOWUSAGE;
4021 + ast_mutex_lock(&peerlock);
4022 + ast_cli(fd, FORMAT2, "EID", "Host", "Model", "Order", "AvgTime", "Status");
4023 + for (peer = peers;peer;peer = peer->next) {
4024 + char status[20] = "";
4025 + int print_line = -1;
4026 + char srch[2000] = "";
4027 + if (registeredonly && !peer->addr.sin_addr.s_addr)
4029 + if (peer->maxms) {
4030 + if (peer->lastms < 0)
4031 + strncpy(status, "UNREACHABLE", sizeof(status) - 1);
4032 + else if (peer->lastms > peer->maxms)
4033 + snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
4034 + else if (peer->lastms)
4035 + snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
4037 + strncpy(status, "UNKNOWN", sizeof(status) - 1);
4039 + strncpy(status, "Unmonitored", sizeof(status) - 1);
4041 + snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
4043 + strcpy(avgms, "Unavail");
4044 + switch(peer->order) {
4046 + order = "Primary";
4049 + order = "Secondary";
4052 + order = "Tertiary";
4055 + order = "Quartiary";
4058 + order = "Unknown";
4060 + snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
4061 + peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
4062 + peer->dynamic ? "(D)" : "(S)", model2str(peer->model), order, avgms, status);
4065 + if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
4067 + } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
4069 + } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
4077 + ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
4078 + peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
4079 + peer->dynamic ? "(D)" : "(S)", model2str(peer->model), order, avgms, status);
4082 + ast_mutex_unlock(&peerlock);
4083 + return RESULT_SUCCESS;
4088 +static int dundi_show_trans(int fd, int argc, char *argv[])
4090 +#define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
4091 +#define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
4092 + struct dundi_transaction *trans;
4093 + char iabuf[INET_ADDRSTRLEN];
4095 + return RESULT_SHOWUSAGE;
4096 + ast_mutex_lock(&peerlock);
4097 + ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
4098 + for (trans = alltrans;trans;trans = trans->allnext) {
4099 + ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr),
4100 + ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
4102 + ast_mutex_unlock(&peerlock);
4103 + return RESULT_SUCCESS;
4108 +static int dundi_show_entityid(int fd, int argc, char *argv[])
4112 + return RESULT_SHOWUSAGE;
4113 + ast_mutex_lock(&peerlock);
4114 + dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
4115 + ast_mutex_unlock(&peerlock);
4116 + ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
4117 + return RESULT_SUCCESS;
4120 +static int dundi_show_requests(int fd, int argc, char *argv[])
4122 +#define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
4123 +#define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
4124 + struct dundi_request *req;
4127 + return RESULT_SHOWUSAGE;
4128 + ast_mutex_lock(&peerlock);
4129 + ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
4130 + for (req = requests;req;req = req->next) {
4131 + ast_cli(fd, FORMAT, req->number, req->dcontext,
4132 + dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
4134 + ast_mutex_unlock(&peerlock);
4135 + return RESULT_SUCCESS;
4140 +/* Grok-a-dial DUNDi */
4142 +static int dundi_show_mappings(int fd, int argc, char *argv[])
4144 +#define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
4145 +#define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
4146 + struct dundi_mapping *map;
4149 + return RESULT_SHOWUSAGE;
4150 + ast_mutex_lock(&peerlock);
4151 + ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
4152 + for (map = mappings;map;map = map->next) {
4153 + ast_cli(fd, FORMAT, map->dcontext, map->weight,
4154 + ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
4155 + dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
4157 + ast_mutex_unlock(&peerlock);
4158 + return RESULT_SUCCESS;
4163 +static int dundi_show_precache(int fd, int argc, char *argv[])
4165 +#define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
4166 +#define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
4167 + struct dundi_precache_queue *qe;
4172 + return RESULT_SHOWUSAGE;
4174 + ast_mutex_lock(&pclock);
4175 + ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
4176 + for (qe = pcq;qe;qe = qe->next) {
4177 + s = qe->expiration - now;
4182 + ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
4184 + ast_mutex_unlock(&pclock);
4185 + return RESULT_SUCCESS;
4190 +static char debug_usage[] =
4191 +"Usage: dundi debug\n"
4192 +" Enables dumping of DUNDi packets for debugging purposes\n";
4194 +static char no_debug_usage[] =
4195 +"Usage: dundi no debug\n"
4196 +" Disables dumping of DUNDi packets for debugging purposes\n";
4198 +static char store_history_usage[] =
4199 +"Usage: dundi store history\n"
4200 +" Enables storing of DUNDi requests and times for debugging\n"
4203 +static char no_store_history_usage[] =
4204 +"Usage: dundi no store history\n"
4205 +" Disables storing of DUNDi requests and times for debugging\n"
4208 +static char show_peers_usage[] =
4209 +"Usage: dundi show peers\n"
4210 +" Lists all known DUNDi peers.\n";
4212 +static char show_trans_usage[] =
4213 +"Usage: dundi show trans\n"
4214 +" Lists all known DUNDi transactions.\n";
4216 +static char show_mappings_usage[] =
4217 +"Usage: dundi show mappings\n"
4218 +" Lists all known DUNDi mappings.\n";
4220 +static char show_precache_usage[] =
4221 +"Usage: dundi show precache\n"
4222 +" Lists all known DUNDi scheduled precache updates.\n";
4224 +static char show_entityid_usage[] =
4225 +"Usage: dundi show entityid\n"
4226 +" Displays the global entityid for this host.\n";
4228 +static char show_peer_usage[] =
4229 +"Usage: dundi show peer [peer]\n"
4230 +" Provide a detailed description of a specifid DUNDi peer.\n";
4232 +static char show_requests_usage[] =
4233 +"Usage: dundi show requests\n"
4234 +" Lists all known pending DUNDi requests.\n";
4236 +static char lookup_usage[] =
4237 +"Usage: dundi lookup <number>[@context] [bypass]\n"
4238 +" Lookup the given number within the given DUNDi context\n"
4239 +"(or e164 if none is specified). Bypasses cache if 'bypass'\n"
4240 +"keyword is specified.\n";
4242 +static char precache_usage[] =
4243 +"Usage: dundi precache <number>[@context]\n"
4244 +" Lookup the given number within the given DUNDi context\n"
4245 +"(or e164 if none is specified) and precaches the results to any\n"
4246 +"upstream DUNDi push servers.\n";
4248 +static char query_usage[] =
4249 +"Usage: dundi query <entity>[@context]\n"
4250 +" Attempts to retrieve contact information for a specific\n"
4251 +"DUNDi entity identifier (EID) within a given DUNDi context (or\n"
4252 +"e164 if none is specified).\n";
4254 +static char flush_usage[] =
4255 +"Usage: dundi flush [stats]\n"
4256 +" Flushes DUNDi answer cache, used primarily for debug. If\n"
4257 +"'stats' is present, clears timer statistics instead of normal\n"
4260 +static struct ast_cli_entry cli_debug =
4261 + { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
4263 +static struct ast_cli_entry cli_store_history =
4264 + { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
4266 +static struct ast_cli_entry cli_no_store_history =
4267 + { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
4269 +static struct ast_cli_entry cli_flush =
4270 + { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
4272 +static struct ast_cli_entry cli_no_debug =
4273 + { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
4275 +static struct ast_cli_entry cli_show_peers =
4276 + { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
4278 +static struct ast_cli_entry cli_show_trans =
4279 + { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
4281 +static struct ast_cli_entry cli_show_entityid =
4282 + { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
4284 +static struct ast_cli_entry cli_show_mappings =
4285 + { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
4287 +static struct ast_cli_entry cli_show_precache =
4288 + { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
4290 +static struct ast_cli_entry cli_show_requests =
4291 + { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
4293 +static struct ast_cli_entry cli_show_peer =
4294 + { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
4296 +static struct ast_cli_entry cli_lookup =
4297 + { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
4299 +static struct ast_cli_entry cli_precache =
4300 + { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
4302 +static struct ast_cli_entry cli_queryeid =
4303 + { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
4305 +STANDARD_LOCAL_USER;
4309 +static struct dundi_transaction *create_transaction(struct dundi_peer *p)
4311 + struct dundi_transaction *trans;
4314 + /* Don't allow creation of transactions to non-registered peers */
4315 + if (p && !p->addr.sin_addr.s_addr)
4317 + tid = get_trans_id();
4320 + trans = malloc(sizeof(struct dundi_transaction));
4322 + memset(trans, 0, sizeof(struct dundi_transaction));
4323 + if (global_storehistory) {
4324 + gettimeofday(&trans->start, NULL);
4325 + trans->flags |= FLAG_STOREHIST;
4327 + trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
4328 + trans->autokillid = -1;
4330 + apply_peer(trans, p);
4331 + if (!p->sentfullkey)
4332 + trans->flags |= FLAG_SENDFULLKEY;
4334 + trans->strans = tid;
4335 + trans->allnext = alltrans;
4341 +static int dundi_xmit(struct dundi_packet *pack)
4344 + char iabuf[INET_ADDRSTRLEN];
4346 + dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
4347 + res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
4349 + ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
4350 + ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
4351 + ntohs(pack->parent->addr.sin_port), strerror(errno));
4358 +static inline char *dstatus_append_long(char *buf, long val) {
4360 + memcpy(buf, &val, sizeof(long));
4361 + return (buf + sizeof(long));
4364 +static inline char *dstatus_append_short(char *buf, short val) {
4366 + memcpy(buf, &val, sizeof(short));
4367 + return (buf + sizeof(short));
4370 +static inline char *dstatus_append_string(char *buf, char *str) {
4371 + unsigned short len = (unsigned short)strlen(str);
4372 + buf = dstatus_append_short(buf, len);
4373 + memcpy(buf, str, len);
4374 + return (buf + len);
4377 +/* Create a packet for the DUNDi Status Updates */
4378 +static char *dstatus_v2_pkt_header(char *buf, char pkt_type, time_t ts, short seq, short totseq) {
4380 + *buf++ = pkt_type;
4382 + /* Timestamp this sequence */
4383 + buf = dstatus_append_long(buf, ts);
4385 + /* Setup sequence headers */
4386 + buf = dstatus_append_short(buf, seq);
4387 + buf = dstatus_append_short(buf, totseq);
4389 + /* Append our global EID */
4390 + memcpy(buf, &global_eid, sizeof(global_eid));
4391 + buf += sizeof(global_eid);
4393 + buf = dstatus_append_string(buf, map_context);
4398 +static int dundi_xmit_peering(void *data)
4402 + short seq = 1, totseq = 0;
4403 + int need_send = 0, peer_count = 0;
4404 + struct dundi_peer *peer;
4405 + time_t send_t = time(NULL);
4407 + /* If we have no where to send, don't bother */
4408 + if(!map_addr.sin_addr.s_addr) {
4409 + if(option_verbose)
4410 + ast_verbose(VERBOSE_PREFIX_1 "No server to send mapping update to...\n");
4411 + map_peering_sid = ast_sched_add(sched, map_update_interval, dundi_xmit_peering, 0);
4415 + /* Provide a sequence number for the packet and totals */
4416 + ast_mutex_lock(&peerlock);
4417 + for (peer = peers;peer;peer = peer->next) {
4418 + if(has_permission(peer->include, map_context) || has_permission(peer->permit, map_context)) {
4421 + if(peer_count == map_updates_per_pkt) {
4428 + totseq += need_send;
4430 + /* Initialize the packet header */
4431 + ptr = dstatus_v2_pkt_header(buf, 1, send_t, seq, totseq);
4433 + /* Include our update interval, in seconds */
4434 + ptr = dstatus_append_short(ptr, map_update_interval/1000);
4436 + /* Include peers/packet configured */
4437 + *ptr++ = (unsigned char)map_updates_per_pkt;
4441 + for (peer = peers;peer;peer = peer->next) {
4442 + if(!has_permission(peer->include, map_context) && !has_permission(peer->permit, map_context))
4448 + /* Copy the peers EID */
4449 + memcpy(ptr, &peer->eid, sizeof(peer->eid));
4450 + ptr += sizeof(peer->eid);
4452 + /* This is the remote peer */
4453 + *ptr++ = (peer->dynamic?0:1);
4454 + memcpy(ptr, &peer->addr.sin_addr.s_addr, sizeof(peer->addr.sin_addr.s_addr));
4455 + ptr += sizeof(peer->addr.sin_addr.s_addr);
4457 + /* Append the model and order */
4458 + *ptr++ = peer->model;
4459 + *ptr++ = peer->order;
4461 + /* Do some long encoding of the timings */
4462 + ptr = dstatus_append_long(ptr, peer->maxms);
4463 + ptr = dstatus_append_long(ptr, peer->lastms);
4464 + ptr = dstatus_append_long(ptr, peer->avgms);
4466 + if(peer_count == map_updates_per_pkt) { /* Okay, it's actually arbitrary */
4469 + sendto(netsocket, buf, ptr - buf, 0, (struct sockaddr *)&map_addr, sizeof(map_addr));
4470 + seq++; /* We sent one, so move on */
4472 + ptr = dstatus_v2_pkt_header(buf, 1, send_t, seq, totseq);
4473 + /* Include our update interval, in seconds */
4474 + ptr = dstatus_append_short(ptr, map_update_interval/1000);
4476 + /* Include peers/packet configured */
4477 + *ptr++ = (unsigned char)map_updates_per_pkt;
4481 + /* If we get here, and haven't sent the packet, send it now */
4483 + sendto(netsocket, buf, ptr - buf, 0, (struct sockaddr *)&map_addr, sizeof(map_addr));
4486 + ast_mutex_unlock(&peerlock);
4488 + /* Reschedule yourselves */
4489 + map_peering_sid = ast_sched_add(sched, map_update_interval, dundi_xmit_peering, 0);
4490 + return RESULT_SUCCESS;
4493 +static int dundi_xmit_contact(void *data) {
4497 + /* Packet type 2, sent now, 1/1 packets */
4498 + ptr = dstatus_v2_pkt_header(buf, 2, time(NULL), 1, 1);
4500 + ptr = dstatus_append_string(ptr, dept);
4501 + ptr = dstatus_append_string(ptr, org);
4502 + ptr = dstatus_append_string(ptr, locality);
4503 + ptr = dstatus_append_string(ptr, stateprov);
4504 + ptr = dstatus_append_string(ptr, country);
4505 + ptr = dstatus_append_string(ptr, email);
4506 + ptr = dstatus_append_string(ptr, phone);
4508 + sendto(netsocket, buf, ptr - buf, 0, (struct sockaddr *)&map_addr, sizeof(map_addr));
4510 + map_contact_sid = ast_sched_add(sched, map_update_interval * 5, dundi_xmit_contact, 0);
4511 + return RESULT_SUCCESS;
4514 +static void destroy_packet(struct dundi_packet *pack, int needfree)
4516 + struct dundi_packet *prev, *cur;
4517 + if (pack->parent) {
4519 + cur = pack->parent->packets;
4521 + if (cur == pack) {
4523 + prev->next = cur->next;
4525 + pack->parent->packets = cur->next;
4532 + if (pack->retransid > -1)
4533 + ast_sched_del(sched, pack->retransid);
4537 + pack->retransid = -1;
4538 + pack->next = NULL;
4542 +static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
4544 + struct dundi_transaction *cur, *prev;
4545 + struct dundi_peer *peer;
4546 + struct timeval tv;
4551 + if (trans->flags & (FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
4554 + if (peer->regtrans == trans)
4555 + peer->regtrans = NULL;
4556 + if (peer->keypending == trans)
4557 + peer->keypending = NULL;
4558 + if (peer->qualtrans == trans) {
4559 + if (fromtimeout) {
4560 + if (peer->lastms > -1)
4561 + ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4562 + peer->lastms = -1;
4564 + gettimeofday(&tv, NULL);
4565 + ms = (tv.tv_sec - peer->qualtx.tv_sec) * 1000 +
4566 + (tv.tv_usec - peer->qualtx.tv_usec) / 1000;
4569 + if (ms < peer->maxms) {
4570 + if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
4571 + ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4572 + } else if (peer->lastms < peer->maxms) {
4573 + 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);
4575 + peer->lastms = ms;
4577 + peer->qualtrans = NULL;
4579 + if (trans->flags & FLAG_STOREHIST) {
4580 + if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
4581 + if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
4584 + if (peer->lookups[DUNDI_TIMING_HISTORY-1])
4585 + free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
4586 + for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
4587 + peer->lookuptimes[x] = peer->lookuptimes[x-1];
4588 + peer->lookups[x] = peer->lookups[x-1];
4589 + if (peer->lookups[x]) {
4590 + peer->avgms += peer->lookuptimes[x];
4594 + peer->lookuptimes[0] = calc_ms(&trans->start);
4595 + peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
4596 + if (peer->lookups[0]) {
4597 + sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
4598 + peer->avgms += peer->lookuptimes[0];
4602 + peer->avgms /= cnt;
4606 + peer = peer->next;
4609 + if (trans->parent) {
4610 + /* Unlink from parent if appropriate */
4612 + cur = trans->parent->trans;
4614 + if (cur == trans) {
4616 + prev->next = trans->next;
4618 + trans->parent->trans = trans->next;
4624 + if (!trans->parent->trans) {
4625 + /* Wake up sleeper */
4626 + if (trans->parent->pfds[1] > -1) {
4627 + write(trans->parent->pfds[1], "killa!", 6);
4631 + /* Unlink from all trans */
4635 + if (cur == trans) {
4637 + prev->allnext = trans->allnext;
4639 + alltrans = trans->allnext;
4643 + cur = cur->allnext;
4645 + destroy_packets(trans->packets);
4646 + destroy_packets(trans->lasttrans);
4647 + trans->packets = NULL;
4648 + if (trans->autokillid > -1)
4649 + ast_sched_del(sched, trans->autokillid);
4650 + trans->autokillid = -1;
4651 + if (trans->thread) {
4652 + /* If used by a thread, mark as dead and be done */
4653 + trans->flags |= FLAG_DEAD;
4658 +static int dundi_rexmit(void *data)
4660 + struct dundi_packet *pack;
4661 + char iabuf[INET_ADDRSTRLEN];
4663 + ast_mutex_lock(&peerlock);
4665 + if (pack->retrans < 1) {
4666 + pack->retransid = -1;
4667 + if (!(pack->parent->flags & FLAG_ISQUAL))
4668 + ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
4669 + ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
4670 + ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
4671 + destroy_trans(pack->parent, 1);
4674 + /* Decrement retransmission, try again */
4679 + ast_mutex_unlock(&peerlock);
4683 +static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
4685 + struct dundi_packet *pack;
4689 + len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
4690 + /* Reserve enough space for encryption */
4691 + if (trans->flags & FLAG_ENCRYPT)
4693 + pack = malloc(len);
4695 + memset(pack, 0, len);
4696 + pack->h = (struct dundi_hdr *)(pack->data);
4697 + if (cmdresp != DUNDI_COMMAND_ACK) {
4698 + pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
4699 + pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
4700 + pack->next = trans->packets;
4701 + trans->packets = pack;
4703 + pack->parent = trans;
4704 + pack->h->strans = htons(trans->strans);
4705 + pack->h->dtrans = htons(trans->dtrans);
4706 + pack->h->iseqno = trans->iseqno;
4707 + pack->h->oseqno = trans->oseqno;
4708 + pack->h->cmdresp = cmdresp;
4709 + pack->datalen = sizeof(struct dundi_hdr);
4711 + memcpy(pack->h->ies, ied->buf, ied->pos);
4712 + pack->datalen += ied->pos;
4715 + pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
4716 + trans->flags |= FLAG_FINAL;
4718 + pack->h->cmdflags = flags;
4719 + if (cmdresp != DUNDI_COMMAND_ACK) {
4721 + trans->oseqno = trans->oseqno % 256;
4723 + trans->aseqno = trans->iseqno;
4724 + /* If we have their public key, encrypt */
4725 + if (trans->flags & FLAG_ENCRYPT) {
4727 + case DUNDI_COMMAND_REGREQ:
4728 + case DUNDI_COMMAND_REGRESPONSE:
4729 + case DUNDI_COMMAND_DPDISCOVER:
4730 + case DUNDI_COMMAND_DPRESPONSE:
4731 + case DUNDI_COMMAND_EIDQUERY:
4732 + case DUNDI_COMMAND_EIDRESPONSE:
4733 + case DUNDI_COMMAND_PRECACHERQ:
4734 + case DUNDI_COMMAND_PRECACHERP:
4736 + dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
4737 + res = dundi_encrypt(trans, pack);
4745 + res = dundi_xmit(pack);
4747 + ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
4749 + if (cmdresp == DUNDI_COMMAND_ACK)
4756 +static int do_autokill(void *data)
4758 + struct dundi_transaction *trans = data;
4760 + ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
4761 + dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
4762 + trans->autokillid = -1;
4763 + destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
4767 +static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
4769 + struct dundi_peer *p;
4770 + if (!dundi_eid_cmp(eid, us)) {
4771 + dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
4774 + ast_mutex_lock(&peerlock);
4777 + if (!dundi_eid_cmp(&p->eid, eid)) {
4778 + if (has_permission(p->include, context))
4779 + dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
4781 + dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
4787 + dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
4788 + ast_mutex_unlock(&peerlock);
4791 +static int dundi_discover(struct dundi_transaction *trans)
4793 + struct dundi_ie_data ied;
4795 + if (!trans->parent) {
4796 + ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
4799 + memset(&ied, 0, sizeof(ied));
4800 + dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
4801 + if (!dundi_eid_zero(&trans->us_eid))
4802 + dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
4803 + for (x=0;x<trans->eidcount;x++)
4804 + dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
4805 + dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
4806 + dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
4807 + dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
4808 + if (trans->parent->cbypass)
4809 + dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
4810 + if (trans->autokilltimeout)
4811 + trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
4812 + return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
4815 +static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
4817 + struct dundi_ie_data ied;
4820 + int expiration = DUNDI_DEFAULT_CACHE_TIME;
4822 + dundi_eid *avoid[1] = { NULL, };
4823 + int direct[1] = { 0, };
4824 + struct dundi_result dr[MAX_RESULTS];
4825 + struct dundi_hint_metadata hmd;
4826 + if (!trans->parent) {
4827 + ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
4830 + memset(&hmd, 0, sizeof(hmd));
4831 + memset(&dr, 0, sizeof(dr));
4832 + /* Look up the answers we're going to include */
4833 + for (x=0;x<mapcount;x++)
4834 + ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
4835 + if (ouranswers < 0)
4837 + for (x=0;x<ouranswers;x++) {
4838 + if (dr[x].weight < max)
4839 + max = dr[x].weight;
4842 + /* If we do not have a canonical result, keep looking */
4843 + 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);
4845 + /* Append answer in result */
4846 + ouranswers += res;
4850 + if (ouranswers > 0) {
4851 + *foundanswers += ouranswers;
4852 + memset(&ied, 0, sizeof(ied));
4853 + dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
4854 + if (!dundi_eid_zero(&trans->us_eid))
4855 + dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
4856 + for (x=0;x<trans->eidcount;x++)
4857 + dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
4858 + dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
4859 + dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
4860 + dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
4861 + for (x=0;x<ouranswers;x++) {
4863 + if (dr[x].expiration && (expiration > dr[x].expiration))
4864 + expiration = dr[x].expiration;
4865 + dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
4867 + dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
4868 + dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
4869 + if (trans->autokilltimeout)
4870 + trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
4871 + if (expiration < *minexp)
4872 + *minexp = expiration;
4873 + return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
4875 + /* Oops, nothing to send... */
4876 + destroy_trans(trans, 0);
4881 +static int dundi_query(struct dundi_transaction *trans)
4883 + struct dundi_ie_data ied;
4885 + if (!trans->parent) {
4886 + ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
4889 + memset(&ied, 0, sizeof(ied));
4890 + dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
4891 + if (!dundi_eid_zero(&trans->us_eid))
4892 + dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
4893 + for (x=0;x<trans->eidcount;x++)
4894 + dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
4895 + dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
4896 + dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
4897 + dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
4898 + if (trans->autokilltimeout)
4899 + trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
4900 + return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
4903 +static int discover_transactions(struct dundi_request *dr)
4905 + struct dundi_transaction *trans;
4906 + trans = dr->trans;
4908 + dundi_discover(trans);
4909 + trans = trans->next;
4914 +static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
4916 + struct dundi_transaction *trans;
4917 + trans = dr->trans;
4919 + precache_trans(trans, maps, mapcount, expiration, foundanswers);
4920 + trans = trans->next;
4925 +static int query_transactions(struct dundi_request *dr)
4927 + struct dundi_transaction *trans;
4928 + ast_mutex_lock(&peerlock);
4929 + trans = dr->trans;
4931 + dundi_query(trans);
4932 + trans = trans->next;
4934 + ast_mutex_unlock(&peerlock);
4938 +static int optimize_transactions(struct dundi_request *dr, int order)
4940 + /* Minimize the message propagation through DUNDi by
4941 + alerting the network to hops which should be not be considered */
4942 + struct dundi_transaction *trans;
4943 + struct dundi_peer *peer;
4947 + ast_mutex_lock(&peerlock);
4948 + trans = dr->trans;
4950 + /* Pop off the true root */
4951 + if (trans->eidcount) {
4952 + tmp = trans->eids[--trans->eidcount];
4955 + tmp = trans->us_eid;
4961 + if (has_permission(peer->include, dr->dcontext) &&
4962 + dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
4963 + (peer->order <= order)) {
4964 + /* For each other transaction, make sure we don't
4965 + ask this EID about the others if they're not
4966 + already in the list */
4967 + if (!dundi_eid_cmp(&tmp, &peer->eid))
4970 + for (x=0;x<trans->eidcount;x++) {
4971 + if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
4975 + if (x == trans->eidcount) {
4976 + /* Nope not in the list, if needed, add us at the end since we're the source */
4977 + if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
4978 + trans->eids[trans->eidcount++] = peer->eid;
4979 + /* Need to insert the real root (or us) at the bottom now as
4980 + a requirement now. */
4985 + peer = peer->next;
4987 + /* If necessary, push the true root back on the end */
4989 + trans->eids[trans->eidcount++] = tmp;
4990 + trans = trans->next;
4992 + ast_mutex_unlock(&peerlock);
4996 +static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
4998 + struct dundi_transaction *trans;
5001 + char eid_str2[20];
5002 + /* Ignore if not registered */
5003 + if (!p->addr.sin_addr.s_addr)
5005 + if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
5007 + if (ast_strlen_zero(dr->number))
5008 + 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);
5010 + 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);
5011 + trans = create_transaction(p);
5014 + trans->next = dr->trans;
5015 + trans->parent = dr;
5017 + for (x=0;avoid[x] && (x <DUNDI_MAX_STACK);x++)
5018 + trans->eids[x] = *avoid[x];
5019 + trans->eidcount = x;
5020 + dr->trans = trans;
5024 +static void cancel_request(struct dundi_request *dr)
5026 + struct dundi_transaction *trans, *next;
5028 + ast_mutex_lock(&peerlock);
5029 + trans = dr->trans;
5032 + next = trans->next;
5033 + /* Orphan transaction from request */
5034 + trans->parent = NULL;
5035 + trans->next = NULL;
5036 + /* Send final cancel */
5037 + dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
5040 + ast_mutex_unlock(&peerlock);
5043 +static void abort_request(struct dundi_request *dr)
5045 + ast_mutex_lock(&peerlock);
5047 + destroy_trans(dr->trans, 0);
5048 + ast_mutex_unlock(&peerlock);
5051 +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[])
5053 + struct dundi_peer *p;
5059 + ast_mutex_lock(&peerlock);
5062 + if (modeselect == 1) {
5063 + /* Send the precache to push upstreams only! */
5064 + pass = has_permission(p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
5067 + /* Normal lookup / EID query */
5068 + pass = has_permission(p->include, dr->dcontext);
5069 + allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
5072 + if (!dundi_eid_cmp(skip, &p->eid))
5076 + if (p->order <= order) {
5077 + /* Check order first, then check cache, regardless of
5078 + omissions, this gets us more likely to not have an
5079 + affected answer. */
5080 + if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
5082 + /* Make sure we haven't already seen it and that it won't
5083 + affect our answer */
5084 + for (x=0;avoid[x];x++) {
5085 + if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
5086 + /* If not a direct connection, it affects our answer */
5087 + if (directs && !directs[x])
5088 + dr->hmd->flags &= ~DUNDI_HINT_UNAFFECTED;
5092 + /* Make sure we can ask */
5093 + if (allowconnect) {
5094 + if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
5095 + /* Check for a matching or 0 cache entry */
5096 + append_transaction(dr, p, ttl, avoid);
5098 + ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
5101 + *foundcache |= res;
5102 + } else if (!*skipped || (p->order < *skipped))
5103 + *skipped = p->order;
5107 + ast_mutex_unlock(&peerlock);
5110 +static int register_request(struct dundi_request *dr, struct dundi_request **pending)
5112 + struct dundi_request *cur;
5115 + ast_mutex_lock(&peerlock);
5119 + ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
5120 + dr->dcontext, dr->number);
5121 + if (!strcasecmp(cur->dcontext, dr->dcontext) &&
5122 + !strcasecmp(cur->number, dr->number) &&
5123 + (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
5124 + ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n",
5125 + cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
5133 + ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n",
5134 + dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
5135 + /* Go ahead and link us in since nobody else is searching for this */
5136 + dr->next = requests;
5140 + ast_mutex_unlock(&peerlock);
5144 +static void unregister_request(struct dundi_request *dr)
5146 + struct dundi_request *cur, *prev;
5147 + ast_mutex_lock(&peerlock);
5153 + prev->next = cur->next;
5155 + requests = cur->next;
5161 + ast_mutex_unlock(&peerlock);
5164 +static int check_request(struct dundi_request *dr)
5166 + struct dundi_request *cur;
5168 + ast_mutex_lock(&peerlock);
5177 + ast_mutex_unlock(&peerlock);
5181 +static unsigned long avoid_crc32(dundi_eid *avoid[])
5183 + /* Idea is that we're calculating a checksum which is independent of
5184 + the order that the EID's are listed in */
5185 + unsigned long acrc32 = 0;
5187 + for (x=0;avoid[x];x++) {
5188 + /* Order doesn't matter */
5190 + acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
5196 +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[])
5199 + struct dundi_request dr, *pending;
5200 + dundi_eid *rooteid=NULL;
5208 + struct timeval start;
5210 + /* Don't do anthing for a hungup channel */
5211 + if (chan && chan->_softhangup)
5214 + ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
5216 + for (x=0;avoid[x];x++)
5217 + rooteid = avoid[x];
5218 + /* Now perform real check */
5219 + memset(&dr, 0, sizeof(dr));
5220 + if (pipe(dr.pfds)) {
5221 + ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
5226 + dr.maxcount = maxret;
5227 + dr.expiration = *expiration;
5228 + dr.cbypass = cbypass;
5229 + dr.crc32 = avoid_crc32(avoid);
5230 + strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
5231 + strncpy(dr.number, number, sizeof(dr.number) - 1);
5233 + dr.root_eid = *rooteid;
5234 + res = register_request(&dr, &pending);
5236 + /* Already a request */
5237 + if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
5238 + /* This is on behalf of someone else. Go ahead and close this out since
5239 + they'll get their answer anyway. */
5240 + ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
5241 + dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
5242 + close(dr.pfds[0]);
5243 + close(dr.pfds[1]);
5246 + /* Wait for the cache to populate */
5247 + ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
5248 + dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
5249 + gettimeofday(&start, NULL);
5250 + while(check_request(pending) && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup)) {
5251 + /* XXX Would be nice to have a way to poll/select here XXX */
5254 + /* Continue on as normal, our cache should kick in */
5257 + /* Create transactions */
5262 + build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
5263 + } while (skipped && !foundcache && !dr.trans);
5264 + /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
5265 + do this earlier because we didn't know if we were going to have transactions
5268 + hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
5269 + abort_request(&dr);
5270 + unregister_request(&dr);
5271 + close(dr.pfds[0]);
5272 + close(dr.pfds[1]);
5276 + /* Optimize transactions */
5277 + optimize_transactions(&dr, order);
5278 + /* Actually perform transactions */
5279 + discover_transactions(&dr);
5280 + /* Wait for transaction to come back */
5281 + gettimeofday(&start, NULL);
5282 + while(dr.trans && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup)) {
5284 + ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
5286 + if (chan && chan->_softhangup)
5287 + ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
5288 + cancel_request(&dr);
5289 + unregister_request(&dr);
5290 + res = dr.respcount;
5291 + *expiration = dr.expiration;
5292 + close(dr.pfds[0]);
5293 + close(dr.pfds[1]);
5297 +int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
5299 + struct dundi_hint_metadata hmd;
5300 + dundi_eid *avoid[1] = { NULL, };
5301 + int direct[1] = { 0, };
5302 + int expiration = DUNDI_DEFAULT_CACHE_TIME;
5303 + memset(&hmd, 0, sizeof(hmd));
5304 + hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
5305 + return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
5308 +static void reschedule_precache(const char *number, const char *context, int expiration)
5311 + struct dundi_precache_queue *qe, *prev=NULL;
5312 + ast_mutex_lock(&pclock);
5315 + if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
5317 + prev->next = qe->next;
5327 + len = sizeof(struct dundi_precache_queue);
5328 + len += strlen(number) + 1;
5329 + len += strlen(context) + 1;
5332 + memset(qe, 0, len);
5333 + strcpy(qe->number, number);
5334 + qe->context = qe->number + strlen(number) + 1;
5335 + strcpy(qe->context, context);
5338 + time(&qe->expiration);
5339 + qe->expiration += expiration;
5342 + while(prev->next && (prev->next->expiration <= qe->expiration))
5343 + prev = prev->next;
5344 + qe->next = prev->next;
5348 + ast_mutex_unlock(&pclock);
5352 +static void dundi_precache_full(void)
5354 + struct dundi_mapping *cur;
5355 + struct ast_context *con;
5356 + struct ast_exten *e;
5359 + ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
5360 + ast_lock_contexts();
5361 + con = ast_walk_contexts(NULL);
5363 + if (!strcasecmp(cur->lcontext, ast_get_context_name(con))) {
5364 + /* Found the match, now queue them all up */
5365 + ast_lock_context(con);
5366 + e = ast_walk_context_extensions(con, NULL);
5368 + reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
5369 + e = ast_walk_context_extensions(con, e);
5371 + ast_unlock_context(con);
5373 + con = ast_walk_contexts(con);
5375 + ast_unlock_contexts();
5380 +static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
5382 + struct dundi_request dr;
5383 + struct dundi_hint_metadata hmd;
5384 + struct dundi_result dr2[MAX_RESULTS];
5385 + struct timeval start;
5386 + struct dundi_mapping *maps=NULL, *cur;
5389 + int foundcache, skipped, ttlms, ms;
5392 + ast_log(LOG_DEBUG, "Precache internal (%s@%s)!\n", number, context);
5394 + ast_mutex_lock(&peerlock);
5398 + if (!strcasecmp(cur->dcontext, context))
5403 + maps = alloca(nummaps * sizeof(struct dundi_mapping));
5408 + if (!strcasecmp(cur->dcontext, context))
5409 + maps[nummaps++] = *cur;
5414 + ast_mutex_unlock(&peerlock);
5415 + if (!nummaps || !maps)
5417 + ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
5418 + memset(&dr2, 0, sizeof(dr2));
5419 + memset(&dr, 0, sizeof(dr));
5420 + memset(&hmd, 0, sizeof(hmd));
5422 + strncpy(dr.number, number, sizeof(dr.number) - 1);
5423 + strncpy(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext) - 1);
5424 + dr.maxcount = MAX_RESULTS;
5425 + dr.expiration = DUNDI_DEFAULT_CACHE_TIME;
5428 + dr.pfds[0] = dr.pfds[1] = -1;
5429 + build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
5430 + optimize_transactions(&dr, 0);
5432 + precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
5433 + if (foundanswers) {
5434 + if (dr.expiration > 0)
5435 + reschedule_precache(dr.number, dr.dcontext, dr.expiration);
5437 + ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
5439 + gettimeofday(&start, NULL);
5440 + while(dr.trans && (calc_ms(&start) < ttlms)) {
5441 + if (dr.pfds[0] > -1) {
5443 + ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
5447 + cancel_request(&dr);
5448 + if (dr.pfds[0] > -1) {
5449 + close(dr.pfds[0]);
5450 + close(dr.pfds[1]);
5455 +int dundi_precache(const char *context, const char *number)
5457 + dundi_eid *avoid[1] = { NULL, };
5458 + return dundi_precache_internal(context, number, dundi_ttl, avoid);
5461 +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[])
5464 + struct dundi_request dr;
5465 + dundi_eid *rooteid=NULL;
5470 + struct timeval start;
5472 + ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
5474 + for (x=0;avoid[x];x++)
5475 + rooteid = avoid[x];
5476 + /* Now perform real check */
5477 + memset(&dr, 0, sizeof(dr));
5480 + strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
5481 + memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
5483 + dr.root_eid = *rooteid;
5484 + /* Create transactions */
5485 + build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
5487 + /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
5488 + do this earlier because we didn't know if we were going to have transactions
5491 + hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
5495 + /* Optimize transactions */
5496 + optimize_transactions(&dr, 9999);
5497 + /* Actually perform transactions */
5498 + query_transactions(&dr);
5499 + /* Wait for transaction to come back */
5500 + gettimeofday(&start, NULL);
5501 + while(dr.trans && (calc_ms(&start) < ttlms))
5503 + res = dr.respcount;
5507 +int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
5509 + dundi_eid *avoid[1] = { NULL, };
5510 + struct dundi_hint_metadata hmd;
5511 + memset(&hmd, 0, sizeof(hmd));
5512 + return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
5515 +static int dundi_lookup_exec(struct ast_channel *chan, void *data)
5521 + struct localuser *u;
5523 + if (!data || !strlen(data)) {
5524 + ast_log(LOG_WARNING, "DUNDiLookup requires an argument (number)\n");
5527 + LOCAL_USER_ADD(u);
5528 + tmp = ast_strdupa(data);
5530 + context = strchr(tmp, '|');
5534 + opts = strchr(context, '|');
5541 + if (!context || !strlen(context))
5547 + LOCAL_USER_REMOVE(u);
5552 +static void mark_peers(void)
5554 + struct dundi_peer *peer;
5555 + ast_mutex_lock(&peerlock);
5559 + peer = peer->next;
5561 + ast_mutex_unlock(&peerlock);
5564 +static void mark_mappings(void)
5566 + struct dundi_mapping *map;
5567 + ast_mutex_lock(&peerlock);
5573 + ast_mutex_unlock(&peerlock);
5576 +static void destroy_permissions(struct permission *p)
5578 + struct permission *prev;
5586 +static void destroy_peer(struct dundi_peer *peer)
5588 + if (peer->registerid > -1)
5589 + ast_sched_del(sched, peer->registerid);
5590 + if (peer->regtrans)
5591 + destroy_trans(peer->regtrans, 0);
5592 + if (peer->keypending)
5593 + destroy_trans(peer->keypending, 0);
5594 + if (peer->qualifyid > -1)
5595 + ast_sched_del(sched, peer->qualifyid);
5596 + destroy_permissions(peer->permit);
5597 + destroy_permissions(peer->include);
5601 +static void destroy_map(struct dundi_mapping *map)
5606 +static void prune_peers(void)
5608 + struct dundi_peer *peer, *prev, *next;
5609 + ast_mutex_lock(&peerlock);
5613 + next = peer->next;
5616 + prev->next = peer->next;
5618 + peers = peer->next;
5619 + destroy_peer(peer);
5624 + ast_mutex_unlock(&peerlock);
5627 +static void prune_mappings(void)
5629 + struct dundi_mapping *map, *prev, *next;
5630 + ast_mutex_lock(&peerlock);
5637 + prev->next = map->next;
5639 + mappings = map->next;
5645 + ast_mutex_unlock(&peerlock);
5648 +static struct permission *append_permission(struct permission *p, char *s, int allow)
5650 + struct permission *start;
5657 + p->next = malloc(sizeof(struct permission) + strlen(s) + 1);
5660 + p = malloc(sizeof(struct permission) + strlen(s) + 1);
5663 + memset(p, 0, sizeof(struct permission));
5664 + memcpy(p->name, s, strlen(s) + 1);
5667 + return start ? start : p;
5670 +#define MAX_OPTS 128
5672 +static void build_mapping(char *name, char *value)
5674 + char *t, *fields[MAX_OPTS];
5675 + struct dundi_mapping *map;
5678 + t = ast_strdupa(value);
5682 + /* Find a double match */
5683 + if (!strcasecmp(map->dcontext, name) &&
5684 + (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
5685 + (!value[strlen(map->lcontext)] ||
5686 + (value[strlen(map->lcontext)] == ','))))
5691 + map = malloc(sizeof(struct dundi_mapping));
5693 + memset(map, 0, sizeof(struct dundi_mapping));
5694 + map->next = mappings;
5701 + memset(fields, 0, sizeof(fields));
5703 + while(t && x < MAX_OPTS) {
5705 + t = strchr(t, ',');
5710 + } /* Russell was here, arrrr! */
5711 + if ((x == 1) && ast_strlen_zero(fields[0])) {
5712 + /* Placeholder mapping */
5713 + strncpy(map->dcontext, name, sizeof(map->dcontext) - 1);
5715 + } else if (x >= 4) {
5716 + strncpy(map->dcontext, name, sizeof(map->dcontext) - 1);
5717 + strncpy(map->lcontext, fields[0], sizeof(map->lcontext) - 1);
5718 + if ((sscanf(fields[1], "%i", &map->weight) == 1) && (map->weight >= 0) && (map->weight < 60000)) {
5719 + strncpy(map->dest, fields[3], sizeof(map->dest) - 1);
5720 + if ((map->tech = str2tech(fields[2]))) {
5724 + ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
5726 + for (y=4;y<x;y++) {
5727 + if (!strcasecmp(fields[y], "nounsolicited"))
5728 + map->options |= DUNDI_FLAG_NOUNSOLICITED;
5729 + else if (!strcasecmp(fields[y], "nocomunsolicit"))
5730 + map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
5731 + else if (!strcasecmp(fields[y], "residential"))
5732 + map->options |= DUNDI_FLAG_RESIDENTIAL;
5733 + else if (!strcasecmp(fields[y], "commercial"))
5734 + map->options |= DUNDI_FLAG_COMMERCIAL;
5735 + else if (!strcasecmp(fields[y], "mobile"))
5736 + map->options |= DUNDI_FLAG_MOBILE;
5737 + else if (!strcasecmp(fields[y], "nopartial"))
5738 + map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
5740 + ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
5743 + ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
5748 +static int do_register(void *data)
5750 + struct dundi_ie_data ied;
5751 + struct dundi_peer *peer = data;
5753 + char eid_str2[20];
5754 + /* Called with peerlock already held */
5755 + 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));
5756 + peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
5757 + /* Destroy old transaction if there is one */
5758 + if (peer->regtrans)
5759 + destroy_trans(peer->regtrans, 0);
5760 + peer->regtrans = create_transaction(peer);
5761 + if (peer->regtrans) {
5762 + peer->regtrans->flags |= FLAG_ISREG;
5763 + memset(&ied, 0, sizeof(ied));
5764 + dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
5765 + dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
5766 + dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
5767 + dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
5770 + 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));
5775 +static int do_qualify(void *data)
5777 + struct dundi_peer *peer;
5779 + peer->qualifyid = -1;
5780 + qualify_peer(peer, 0);
5784 +static void qualify_peer(struct dundi_peer *peer, int schedonly)
5787 + if (peer->qualifyid > -1)
5788 + ast_sched_del(sched, peer->qualifyid);
5789 + peer->qualifyid = -1;
5790 + if (peer->qualtrans)
5791 + destroy_trans(peer->qualtrans, 0);
5792 + peer->qualtrans = NULL;
5793 + if (peer->maxms > 0) {
5795 + if (peer->lastms < 0)
5799 + peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
5801 + peer->qualtrans = create_transaction(peer);
5802 + if (peer->qualtrans) {
5803 + gettimeofday(&peer->qualtx, NULL);
5804 + peer->qualtrans->flags |= FLAG_ISQUAL;
5805 + dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
5809 +static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
5815 + dundi_eid_to_str(eid_str, sizeof(eid_str), eid);
5816 + if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
5817 + c = strchr(data, ':');
5821 + if (sscanf(c, "%d:%d", &port, &expire) == 2) {
5823 + inet_aton(data, &peer->addr.sin_addr);
5824 + peer->addr.sin_family = AF_INET;
5825 + peer->addr.sin_port = htons(port);
5826 + peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
5833 +static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
5835 + struct dundi_peer *peer;
5836 + struct ast_hostent he;
5837 + struct hostent *hp;
5838 + dundi_eid testeid;
5839 + int needregister=0;
5842 + ast_mutex_lock(&peerlock);
5845 + if (!dundi_eid_cmp(&peer->eid, eid)) {
5848 + peer = peer->next;
5851 + /* Add us into the list */
5852 + peer = malloc(sizeof(struct dundi_peer));
5854 + memset(peer, 0, sizeof(struct dundi_peer));
5855 + peer->registerid = -1;
5856 + peer->registerexpire = -1;
5857 + peer->qualifyid = -1;
5858 + peer->addr.sin_family = AF_INET;
5859 + peer->addr.sin_port = htons(DUNDI_PORT);
5860 + populate_addr(peer, eid);
5861 + peer->next = peers;
5868 + peer->us_eid = global_eid;
5869 + destroy_permissions(peer->permit);
5870 + destroy_permissions(peer->include);
5871 + peer->permit = NULL;
5872 + peer->include = NULL;
5873 + if (peer->registerid > -1)
5874 + ast_sched_del(sched, peer->registerid);
5875 + peer->registerid = -1;
5877 + if (!strcasecmp(v->name, "inkey")) {
5878 + strncpy(peer->inkey, v->value, sizeof(peer->inkey) - 1);
5879 + } else if (!strcasecmp(v->name, "outkey")) {
5880 + strncpy(peer->outkey, v->value, sizeof(peer->outkey) - 1);
5881 + } else if (!strcasecmp(v->name, "host")) {
5882 + if (!strcasecmp(v->value, "dynamic")) {
5883 + peer->dynamic = 1;
5885 + hp = ast_gethostbyname(v->value, &he);
5887 + memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
5888 + peer->dynamic = 0;
5890 + ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
5894 + } else if (!strcasecmp(v->name, "ustothem")) {
5895 + if (!dundi_str_to_eid(&testeid, v->value))
5896 + peer->us_eid = testeid;
5898 + ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
5899 + } else if (!strcasecmp(v->name, "include")) {
5900 + peer->include = append_permission(peer->include, v->value, 1);
5901 + } else if (!strcasecmp(v->name, "permit")) {
5902 + peer->permit = append_permission(peer->permit, v->value, 1);
5903 + } else if (!strcasecmp(v->name, "noinclude")) {
5904 + peer->include = append_permission(peer->include, v->value, 0);
5905 + } else if (!strcasecmp(v->name, "deny")) {
5906 + peer->permit = append_permission(peer->permit, v->value, 0);
5907 + } else if (!strcasecmp(v->name, "register")) {
5908 + needregister = ast_true(v->value);
5909 + } else if (!strcasecmp(v->name, "order")) {
5910 + if (!strcasecmp(v->value, "primary"))
5912 + else if (!strcasecmp(v->value, "secondary"))
5914 + else if (!strcasecmp(v->value, "tertiary"))
5916 + else if (!strcasecmp(v->value, "quartiary"))
5919 + 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);
5921 + } else if (!strcasecmp(v->name, "qualify")) {
5922 + if (!strcasecmp(v->value, "no")) {
5924 + } else if (!strcasecmp(v->value, "yes")) {
5925 + peer->maxms = DEFAULT_MAXMS;
5926 + } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
5927 + ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
5928 + dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
5931 + } else if (!strcasecmp(v->name, "model")) {
5932 + if (!strcasecmp(v->value, "inbound"))
5933 + peer->model = DUNDI_MODEL_INBOUND;
5934 + else if (!strcasecmp(v->value, "outbound"))
5935 + peer->model = DUNDI_MODEL_OUTBOUND;
5936 + else if (!strcasecmp(v->value, "symmetric"))
5937 + peer->model = DUNDI_MODEL_SYMMETRIC;
5938 + else if (!strcasecmp(v->value, "none"))
5941 + ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
5942 + v->value, v->lineno);
5944 + } else if (!strcasecmp(v->name, "precache")) {
5945 + if (!strcasecmp(v->value, "inbound"))
5946 + peer->pcmodel = DUNDI_MODEL_INBOUND;
5947 + else if (!strcasecmp(v->value, "outbound"))
5948 + peer->pcmodel = DUNDI_MODEL_OUTBOUND;
5949 + else if (!strcasecmp(v->value, "symmetric"))
5950 + peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
5951 + else if (!strcasecmp(v->value, "none"))
5952 + peer->pcmodel = 0;
5954 + ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
5955 + v->value, v->lineno);
5960 + (*globalpcmode) |= peer->pcmodel;
5961 + if (!peer->model && !peer->pcmodel) {
5962 + ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
5963 + dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5965 + } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
5966 + ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
5967 + dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5969 + } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
5970 + ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
5971 + dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5973 + } else if (peer->include && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
5974 + ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
5975 + dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5976 + } else if (peer->permit && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
5977 + 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",
5978 + dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5980 + if (needregister) {
5981 + peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
5983 + qualify_peer(peer, 1);
5986 + ast_mutex_unlock(&peerlock);
5989 +static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
5991 + struct dundi_result results[MAX_RESULTS];
5995 + if (!strncasecmp(context, "macro-", 6)) {
5997 + ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
6000 + /* If done as a macro, use macro extension */
6001 + if (!strcasecmp(exten, "s")) {
6002 + exten = pbx_builtin_getvar_helper(chan, "ARG1");
6003 + if (!exten || ast_strlen_zero(exten))
6004 + exten = chan->macroexten;
6005 + if (!exten || ast_strlen_zero(exten))
6006 + exten = chan->exten;
6007 + if (!exten || ast_strlen_zero(exten)) {
6008 + ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
6012 + if (!data || ast_strlen_zero(data))
6015 + if (!data || ast_strlen_zero(data))
6018 + res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
6019 + for (x=0;x<res;x++) {
6020 + if (results[x].flags & flag)
6023 + if (found >= priority)
6028 +static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
6030 + return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
6033 +static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
6035 + return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
6038 +static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, int newstack, const char *data)
6040 + struct dundi_result results[MAX_RESULTS];
6044 + struct ast_app *dial;
6046 + if (!strncasecmp(context, "macro-", 6)) {
6048 + ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
6051 + /* If done as a macro, use macro extension */
6052 + if (!strcasecmp(exten, "s")) {
6053 + exten = pbx_builtin_getvar_helper(chan, "ARG1");
6054 + if (!exten || ast_strlen_zero(exten))
6055 + exten = chan->macroexten;
6056 + if (!exten || ast_strlen_zero(exten))
6057 + exten = chan->exten;
6058 + if (!exten || ast_strlen_zero(exten)) {
6059 + ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
6063 + if (!data || ast_strlen_zero(data))
6066 + if (!data || ast_strlen_zero(data))
6069 + res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
6071 + sort_results(results, res);
6072 + for (x=0;x<res;x++) {
6073 + if (results[x].flags & DUNDI_FLAG_EXISTS) {
6081 + snprintf(req, sizeof(req), "%s/%s", results[x].tech, results[x].dest);
6082 + dial = pbx_findapp("Dial");
6084 + res = pbx_exec(chan, dial, req, newstack);
6090 +static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
6092 + return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
6095 +static struct ast_switch dundi_switch =
6098 + description: "DUNDi Discovered Dialplan Switch",
6099 + exists: dundi_exists,
6100 + canmatch: dundi_canmatch,
6102 + matchmore: dundi_matchmore,
6105 +static int set_config(char *config_file, struct sockaddr_in* sin)
6107 + struct ast_config *cfg;
6108 + struct ast_variable *v;
6113 + struct ast_hostent he;
6114 + struct hostent *hp;
6115 + struct sockaddr_in sin2;
6116 + static int last_port = 0;
6117 + int globalpcmodel = 0;
6118 + dundi_eid testeid;
6120 + dundi_ttl = DUNDI_DEFAULT_TTL;
6121 + cfg = ast_load(config_file);
6125 + ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
6129 + if (!gethostname(hn, sizeof(hn))) {
6130 + hp = ast_gethostbyname(hn, &he);
6132 + memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
6133 + ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin2.sin_addr);
6135 + ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
6137 + ast_log(LOG_WARNING, "Unable to get host name!\n");
6138 + ast_mutex_lock(&peerlock);
6139 + reset_global_eid();
6140 + global_storehistory = 0;
6141 + strncpy(secretpath, "dundi", sizeof(secretpath) - 1);
6142 + v = ast_variable_browse(cfg, "general");
6144 + if (!strcasecmp(v->name, "port")){
6145 + sin->sin_port = ntohs(atoi(v->value));
6147 + last_port=sin->sin_port;
6148 + } else if(sin->sin_port != last_port)
6149 + ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
6150 + } else if (!strcasecmp(v->name, "bindaddr")) {
6151 + struct hostent *hp;
6152 + struct ast_hostent he;
6153 + hp = ast_gethostbyname(v->value, &he);
6155 + memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
6157 + ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
6158 + } else if (!strcasecmp(v->name, "authdebug")) {
6159 + authdebug = ast_true(v->value);
6160 + } else if (!strcasecmp(v->name, "ttl")) {
6161 + if ((sscanf(v->value, "%i", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
6164 + ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
6165 + v->value, v->lineno, DUNDI_DEFAULT_TTL);
6167 + } else if (!strcasecmp(v->name, "autokill")) {
6168 + if (sscanf(v->value, "%i", &x) == 1) {
6170 + global_autokilltimeout = x;
6172 + ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
6173 + } else if (ast_true(v->value)) {
6174 + global_autokilltimeout = DEFAULT_MAXMS;
6176 + global_autokilltimeout = 0;
6178 + } else if (!strcasecmp(v->name, "entityid")) {
6179 + if (!dundi_str_to_eid(&testeid, v->value))
6180 + global_eid = testeid;
6182 + ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
6183 + } else if (!strcasecmp(v->name, "tos")) {
6184 + if (sscanf(v->value, "%i", &format) == 1)
6185 + tos = format & 0xff;
6186 + else if (!strcasecmp(v->value, "lowdelay"))
6187 + tos = IPTOS_LOWDELAY;
6188 + else if (!strcasecmp(v->value, "throughput"))
6189 + tos = IPTOS_THROUGHPUT;
6190 + else if (!strcasecmp(v->value, "reliability"))
6191 + tos = IPTOS_RELIABILITY;
6192 +#if !defined(__NetBSD__)
6193 + else if (!strcasecmp(v->value, "mincost"))
6194 + tos = IPTOS_MINCOST;
6196 + else if (!strcasecmp(v->value, "none"))
6199 +#if defined(__NetBSD__)
6200 + ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno);
6202 + ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', or 'none'\n", v->lineno);
6204 + } else if (!strcasecmp(v->name, "department")) {
6205 + strncpy(dept, v->value, sizeof(dept) - 1);
6206 + } else if (!strcasecmp(v->name, "organization")) {
6207 + strncpy(org, v->value, sizeof(org) - 1);
6208 + } else if (!strcasecmp(v->name, "locality")) {
6209 + strncpy(locality, v->value, sizeof(locality) - 1);
6210 + } else if (!strcasecmp(v->name, "stateprov")) {
6211 + strncpy(stateprov, v->value, sizeof(stateprov) - 1);
6212 + } else if (!strcasecmp(v->name, "country")) {
6213 + strncpy(country, v->value, sizeof(country) - 1);
6214 + } else if (!strcasecmp(v->name, "email")) {
6215 + strncpy(email, v->value, sizeof(email) - 1);
6216 + } else if (!strcasecmp(v->name, "phone")) {
6217 + strncpy(phone, v->value, sizeof(phone) - 1);
6218 + } else if (!strcasecmp(v->name, "storehistory")) {
6219 + global_storehistory = ast_true(v->value);
6220 + } else if (!strcasecmp(v->name, "mapserver")) {
6221 + hp = ast_gethostbyname(v->value, &he);
6223 + memcpy(&map_addr.sin_addr, hp->h_addr, sizeof(map_addr.sin_addr));
6224 + if (option_verbose > 1)
6225 + ast_verbose(VERBOSE_PREFIX_2 "Using mapping server at %s\n", v->value);
6227 + memset(&map_addr.sin_addr, 0, sizeof(map_addr.sin_addr));
6228 + ast_log(LOG_WARNING, "Could not find host '%s' for mapping host\n", v->value);
6230 + } else if (!strcasecmp(v->name, "mappeers_per_pkt")) {
6231 + map_updates_per_pkt = atoi(v->value);
6232 + if(map_updates_per_pkt < 1 || map_updates_per_pkt > 45) {
6233 + ast_log(LOG_WARNING, "Map updates must be between 1 and 45 inclusive. Setting to 45.\n");
6234 + map_updates_per_pkt = 45;
6236 + } else if (!strcasecmp(v->name, "mapport")) {
6237 + if (option_verbose > 1)
6238 + ast_verbose(VERBOSE_PREFIX_2 "Using mapping server at port %d\n", atoi(v->value));
6239 + map_addr.sin_port = htons(atoi(v->value));
6240 + } else if (!strcasecmp(v->name, "mapinterval")) {
6241 + map_update_interval = atoi(v->value) * 1000;
6242 + if(map_update_interval < 5000 && map_update_interval != 0)
6243 + map_update_interval = 5000;
6244 + if (option_verbose > 1)
6245 + ast_verbose(VERBOSE_PREFIX_2 "Using mapping update interval of %d ms\n", map_update_interval);
6246 + } else if(!strcasecmp(v->name, "mapcontext")) {
6247 + strncpy(map_context, v->value, sizeof(map_context) - 1);
6251 + ast_mutex_unlock(&peerlock);
6253 + v = ast_variable_browse(cfg, "mappings");
6255 + build_mapping(v->name, v->value);
6260 + cat = ast_category_browse(cfg, NULL);
6262 + if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
6264 + if (!dundi_str_to_eid(&testeid, cat))
6265 + build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
6267 + ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
6269 + cat = ast_category_browse(cfg, cat);
6275 + /* Schedule updates */
6276 + if(map_peering_sid > -1)
6277 + ast_sched_del(sched, map_peering_sid);
6278 + if(map_contact_sid > -1)
6279 + ast_sched_del(sched, map_contact_sid);
6280 + if(map_update_interval) {
6281 + map_peering_sid = ast_sched_add(sched, 1000, dundi_xmit_peering, 0);
6282 + map_contact_sid = ast_sched_add(sched, 1000, dundi_xmit_contact, 0);
6285 + if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
6286 + dundi_precache_full();
6291 +int unload_module(void)
6294 + STANDARD_HANGUP_LOCALUSERS;
6295 + ast_cli_unregister(&cli_debug);
6296 + ast_cli_unregister(&cli_store_history);
6297 + ast_cli_unregister(&cli_flush);
6298 + ast_cli_unregister(&cli_no_debug);
6299 + ast_cli_unregister(&cli_no_store_history);
6300 + ast_cli_unregister(&cli_show_peers);
6301 + ast_cli_unregister(&cli_show_entityid);
6302 + ast_cli_unregister(&cli_show_trans);
6303 + ast_cli_unregister(&cli_show_requests);
6304 + ast_cli_unregister(&cli_show_mappings);
6305 + ast_cli_unregister(&cli_show_precache);
6306 + ast_cli_unregister(&cli_show_peer);
6307 + ast_cli_unregister(&cli_lookup);
6308 + ast_cli_unregister(&cli_precache);
6309 + ast_cli_unregister(&cli_queryeid);
6310 + ast_unregister_switch(&dundi_switch);
6311 + res = ast_unregister_application(app);
6317 + struct sockaddr_in sin;
6318 + set_config("dundi.conf",&sin);
6322 +int load_module(void)
6325 + struct sockaddr_in sin;
6326 + char iabuf[INET_ADDRSTRLEN];
6328 + dundi_set_output(dundi_debug_output);
6329 + dundi_set_error(dundi_error_output);
6331 + /* Seed random number generator */
6332 + srand(time(NULL));
6334 + sin.sin_family = AF_INET;
6335 + sin.sin_port = ntohs(DUNDI_PORT);
6336 + sin.sin_addr.s_addr = INADDR_ANY;
6338 + /* INADDR_ANY should be 0.0.0.0 */
6339 + map_addr.sin_family = AF_INET;
6340 + map_addr.sin_port = ntohs(4525);
6341 + map_addr.sin_addr.s_addr = INADDR_ANY;
6342 + strncpy(map_context, "open-e164", sizeof(map_context) - 1);
6344 + /* Make a UDP socket */
6345 + io = io_context_create();
6346 + sched = sched_context_create();
6348 + if (!io || !sched) {
6349 + ast_log(LOG_ERROR, "Out of memory\n");
6353 + ast_cli_register(&cli_debug);
6354 + ast_cli_register(&cli_store_history);
6355 + ast_cli_register(&cli_flush);
6356 + ast_cli_register(&cli_no_debug);
6357 + ast_cli_register(&cli_no_store_history);
6358 + ast_cli_register(&cli_show_peers);
6359 + ast_cli_register(&cli_show_entityid);
6360 + ast_cli_register(&cli_show_trans);
6361 + ast_cli_register(&cli_show_requests);
6362 + ast_cli_register(&cli_show_mappings);
6363 + ast_cli_register(&cli_show_precache);
6364 + ast_cli_register(&cli_show_peer);
6365 + ast_cli_register(&cli_lookup);
6366 + ast_cli_register(&cli_precache);
6367 + ast_cli_register(&cli_queryeid);
6368 + if (ast_register_switch(&dundi_switch))
6369 + ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
6371 + set_config("dundi.conf",&sin);
6373 + netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
6375 + if (netsocket < 0) {
6376 + ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
6379 + if (bind(netsocket,(struct sockaddr *)&sin, sizeof(sin))) {
6380 + ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
6384 + if (option_verbose > 1)
6385 + ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
6387 + if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)))
6388 + ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
6391 + res = start_network_thread();
6392 + if (option_verbose > 1)
6393 + ast_verbose(VERBOSE_PREFIX_2 "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
6395 + ast_log(LOG_ERROR, "Unable to start network thread\n");
6398 + res = ast_register_application(app, dundi_lookup_exec, synopsis, descrip);
6403 +char *description(void)
6411 + /* XXX DUNDi cannot be unloaded XXX */
6413 + STANDARD_USECOUNT(res);
6419 + return ASTERISK_GPL_KEY;
6421 diff -ruN asterisk-1.0.7-orig/pbx.c asterisk-1.0.7-pbx_dundi/pbx.c
6422 --- asterisk-1.0.7-orig/pbx.c 2005-02-19 01:27:52.000000000 +0100
6423 +++ asterisk-1.0.7-pbx_dundi/pbx.c 2005-06-02 20:21:37.000000000 +0200
6425 /*--- pbx_retrieve_variable: Support for Asterisk built-in variables and
6426 functions in the dialplan
6428 -static void pbx_substitute_variables_temp(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen)
6429 +static void pbx_substitute_variables_temp(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
6431 char *first,*second;
6432 char tmpvar[80] = "";
6435 struct ast_var_t *variables;
6436 char *name, *num; /* for callerid name + num variables */
6437 - struct varshead *headp=NULL;
6443 first = tmpvar + strlen(tmpvar);
6445 - pbx_substitute_variables_temp(c,tmpvar,ret,workspace,workspacelen - 1);
6446 + pbx_substitute_variables_temp(c,tmpvar,ret,workspace,workspacelen - 1, headp);
6449 offset=atoi(first+1); /* The number of characters,
6451 strncpy(workspace, c->language, workspacelen - 1);
6456 AST_LIST_TRAVERSE(headp,variables,entries) {
6458 ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",var,ast_var_name(variables));
6459 @@ -1040,7 +1039,7 @@
6463 -void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
6464 +static void pbx_substitute_variables_helper_full(struct ast_channel *c, const char *cp1, char *cp2, int count, struct varshead *headp)
6467 const char *tmp, *whereweare;
6468 @@ -1131,7 +1130,7 @@
6470 /* Retrieve variable value */
6471 workspace[0] = '\0';
6472 - pbx_substitute_variables_temp(c,vars,&cp4, workspace, sizeof(workspace));
6473 + pbx_substitute_variables_temp(c,vars,&cp4, workspace, sizeof(workspace), headp);
6475 length = strlen(cp4);
6477 @@ -1206,6 +1205,16 @@
6481 +void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
6483 + pbx_substitute_variables_helper_full(c, cp1, cp2, count, NULL);
6486 +void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
6488 + pbx_substitute_variables_helper_full(NULL, cp1, cp2, count, headp);
6491 static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e) {
6493 memset(passdata, 0, datalen);