summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--openwrt/package/asterisk/Config.in63
-rw-r--r--openwrt/package/asterisk/Makefile114
-rw-r--r--openwrt/package/asterisk/ipkg/asterisk-codec-ilbc.control7
-rw-r--r--openwrt/package/asterisk/ipkg/asterisk-codec-lpc10.control7
-rw-r--r--openwrt/package/asterisk/ipkg/asterisk-pbx-dundi.conffiles1
-rw-r--r--openwrt/package/asterisk/ipkg/asterisk-pbx-dundi.control7
-rw-r--r--openwrt/package/asterisk/patches/asterisk-1.0.7-pbx_dundi.patch6493
7 files changed, 6634 insertions, 58 deletions
diff --git a/openwrt/package/asterisk/Config.in b/openwrt/package/asterisk/Config.in
index 27a10b89eb..a8c5d311c8 100644
--- a/openwrt/package/asterisk/Config.in
+++ b/openwrt/package/asterisk/Config.in
@@ -1,5 +1,8 @@
+menu "asterisk - Complete open source PBX"
+
config BR2_PACKAGE_ASTERISK
- tristate "asterisk - Complete open source PBX"
+ prompt "asterisk - Complete open source PBX"
+ tristate
default m if CONFIG_DEVEL
help
Asterisk is a complete PBX in software. It provides all of the features
@@ -10,41 +13,71 @@ config BR2_PACKAGE_ASTERISK
http://www.asterisk.org/
-config BR2_PACKAGE_ASTERISK_SPEEX
- tristate "Speex codec"
+config BR2_PACKAGE_ASTERISK_CODEC_ILBC
+ prompt "...-codec-ilbc - Internet Low Bitrate Codec (ILBC) Translator"
+ tristate
+ default m if CONFIG_DEVEL
+ depends BR2_PACKAGE_ASTERISK
+ help
+ The Internet Low Bitrate Codec (ILBC) Translator for Asterisk
+
+config BR2_PACKAGE_ASTERISK_CODEC_LPC10
+ prompt "...-codec-lpc10 - LPC10 (Linear Predictor Code) 2.4kbps Voice Coder"
+ tristate
+ default m if CONFIG_DEVEL
+ depends BR2_PACKAGE_ASTERISK
+ help
+ The LPC10 (Linear Predictor Code) 2.4kbps Voice Coder for Asterisk
+
+config BR2_PACKAGE_ASTERISK_CODEC_SPEEX
+ prompt "...-codec-speex - Speex/PCM16 Codec Translator"
+ tristate
default m if CONFIG_DEVEL
depends BR2_PACKAGE_ASTERISK
select BR2_PACKAGE_LIBSPEEX
help
The Speex speech compression codec for Asterisk
+config BR2_PACKAGE_ASTERISK_PBX_DUNDI
+ prompt "...-pbx-dundi - Distributed Universal Number Discovery (DUNDi) support"
+ tristate
+ default m if CONFIG_DEVEL
+ depends BR2_PACKAGE_ASTERISK
+ help
+ Distributed Universal Number Discovery (DUNDi) support for Asterisk
+
config BR2_PACKAGE_ASTERISK_MYSQL
- tristate "MySQL plugins"
+ prompt "...-mysql - MySQL modules"
+ tristate
default m if CONFIG_DEVEL
depends BR2_PACKAGE_ASTERISK
select BR2_PACKAGE_LIBMYSQLCLIENT
help
- MySQL plugins for Asterisk
+ MySQL modules for Asterisk
config BR2_PACKAGE_ASTERISK_PGSQL
- tristate "PostgreSQL plugins"
+ prompt "...-pgsql - PostgreSQL modules"
+ tristate
default m if CONFIG_DEVEL
depends BR2_PACKAGE_ASTERISK
select BR2_PACKAGE_LIBPQ
help
- PostgreSQL plugins for Asterisk
-
-config BR2_PACKAGE_ASTERISK_VOICEMAIL
- tristate "Voicemail support"
- default m if CONFIG_DEVEL
- depends BR2_PACKAGE_ASTERISK
- help
- Voicemail related plugins for Asterisk
+ PostgreSQL modules for Asterisk
config BR2_PACKAGE_ASTERISK_SOUNDS
- tristate "Sound files"
+ prompt "...-sounds - Sound files"
+ tristate
default m if CONFIG_DEVEL
depends BR2_PACKAGE_ASTERISK
help
Sound files for Asterisk
+config BR2_PACKAGE_ASTERISK_VOICEMAIL
+ prompt "...-voicemail - Voicemail support"
+ tristate
+ default m if CONFIG_DEVEL
+ depends BR2_PACKAGE_ASTERISK
+ help
+ Voicemail related modules for Asterisk
+
+endmenu
diff --git a/openwrt/package/asterisk/Makefile b/openwrt/package/asterisk/Makefile
index 5a4603bc9d..5c5dd7bf01 100644
--- a/openwrt/package/asterisk/Makefile
+++ b/openwrt/package/asterisk/Makefile
@@ -18,7 +18,10 @@ $(eval $(call PKG_template,ASTERISK_MYSQL,asterisk-mysql,$(PKG_VERSION)-$(PKG_RE
$(eval $(call PKG_template,ASTERISK_PGSQL,asterisk-pgsql,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
$(eval $(call PKG_template,ASTERISK_VOICEMAIL,asterisk-voicemail,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
$(eval $(call PKG_template,ASTERISK_SOUNDS,asterisk-sounds,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
-$(eval $(call PKG_template,ASTERISK_SPEEX,asterisk-codec-speex,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
+$(eval $(call PKG_template,ASTERISK_CODEC_ILBC,asterisk-codec-ilbc,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
+$(eval $(call PKG_template,ASTERISK_CODEC_LPC10,asterisk-codec-lpc10,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
+$(eval $(call PKG_template,ASTERISK_CODEC_SPEEX,asterisk-codec-speex,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
+$(eval $(call PKG_template,ASTERISK_PBX_DUNDI,asterisk-pbx-dundi,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
APPS:=
MODS:=
@@ -30,11 +33,14 @@ ifneq ($(BR2_PACKAGE_ASTERISK_PGSQL),)
APPS += app_sql_postgres.so
MODS += cdr_pgsql.so
endif
-ifneq ($(BR2_PACKAGE_ASTERISK_SPEEX),)
+ifneq ($(BR2_PACKAGE_ASTERISK_CODEC_SPEEX),)
SPEEX:=codec_speex.so
endif
-asterisk-compile: $(PKG_BUILD_DIR)/.prepared
+$(PKG_BUILD_DIR)/.configured:
+ touch $@
+
+$(PKG_BUILD_DIR)/.built:
$(MAKE) -C "$(PKG_BUILD_DIR)/channels" \
CC="$(HOSTCC)" \
gentone
@@ -49,14 +55,9 @@ asterisk-compile: $(PKG_BUILD_DIR)/.prepared
EXTRA_APPS="$(APPS)" \
EXTRA_MODS="$(MODS)" \
MODSPEEX="$(SPEEX)"
-
-$(PKG_BUILD_DIR)/.configured:
- touch $@
-
-$(PKG_BUILD_DIR)/.built: asterisk-compile
touch $@
-$(IPKG_ASTERISK): asterisk-compile
+$(IPKG_ASTERISK):
$(MAKE) -C $(PKG_BUILD_DIR) \
DESTDIR="$(IDIR_ASTERISK)" \
install samples
@@ -76,54 +77,81 @@ $(IPKG_ASTERISK): asterisk-compile
rm -rf *adsi* *festival* *modem* *meetme* *oss* *phone* *intercom* \
*mp3* *nbscat* *mysql* *postgres* *pgsql* *voicemail* *speex* \
*musiconhold* *zapateller* *jpeg*; \
+ rm -f {codec,format}_ilbc.so ; \
+ rm -f codec_lpc10.so ; \
+ rm -f pbx_dundi.so ; \
)
(cd $(IDIR_ASTERISK)/etc/asterisk; \
rm -f *odbc* *mysql* *postgres* *pgsql* *voicemail* *adsi* *oss* *alsa* \
*festival* *modem* *meetme* *phone* *tds* *vofr* *rpt* *vpb* \
*zapata* *musiconhold*; \
+ rm -f dundi.conf ; \
)
- -$(STRIP) $(IDIR_ASTERISK)/usr/sbin/asterisk
- -$(STRIP) $(IDIR_ASTERISK)/usr/lib/asterisk/modules/*
- cp -a ./files/* $(IDIR_ASTERISK)/
- find $(IDIR_ASTERISK) -name CVS | xargs rm -rf
- chmod +x $(IDIR_ASTERISK)/etc/init.d/*
+ install -d -m0755 $(IDIR_ASTERISK)/etc/default
+ install -m0644 ./files/asterisk.default $(IDIR_ASTERISK)/etc/default/asterisk
+ install -d -m0755 $(IDIR_ASTERISK)/etc/init.d
+ install -m0755 ./files/asterisk.init $(IDIR_ASTERISK)/etc/init.d/asterisk
+ ln -sf asterisk $(IDIR_ASTERISK)/etc/init.d/S60asterisk
+ $(RSTRIP) $(IDIR_ASTERISK)
$(IPKG_BUILD) $(IDIR_ASTERISK) $(PACKAGE_DIR)
-$(IPKG_ASTERISK_MYSQL): asterisk-compile
- mkdir -p $(IDIR_ASTERISK_MYSQL)/usr/lib/asterisk/modules
- mkdir -p $(IDIR_ASTERISK_MYSQL)/etc/asterisk
- cp $(PKG_BUILD_DIR)/apps/app_sql_mysql.so $(IDIR_ASTERISK_MYSQL)/usr/lib/asterisk/modules/
- cp $(PKG_BUILD_DIR)/cdr/cdr_mysql.so $(IDIR_ASTERISK_MYSQL)/usr/lib/asterisk/modules/
- $(STRIP) $(IDIR_ASTERISK_MYSQL)/usr/lib/asterisk/modules/*
- cp $(PKG_BUILD_DIR)/configs/cdr_mysql.conf.sample $(IDIR_ASTERISK_MYSQL)/etc/asterisk/cdr_mysql.conf
+$(IPKG_ASTERISK_MYSQL):
+ install -d -m0755 $(IDIR_ASTERISK_MYSQL)/etc/asterisk
+ install -m0600 $(PKG_BUILD_DIR)/configs/cdr_mysql.conf.sample $(IDIR_ASTERISK_MYSQL)/etc/asterisk/cdr_mysql.conf
+ install -d -m0755 $(IDIR_ASTERISK_MYSQL)/usr/lib/asterisk/modules
+ install -m0755 $(PKG_BUILD_DIR)/apps/app_sql_mysql.so $(IDIR_ASTERISK_MYSQL)/usr/lib/asterisk/modules/
+ install -m0755 $(PKG_BUILD_DIR)/cdr/cdr_mysql.so $(IDIR_ASTERISK_MYSQL)/usr/lib/asterisk/modules/
+ $(RSTRIP) $(IDIR_ASTERISK_MYSQL)
$(IPKG_BUILD) $(IDIR_ASTERISK_MYSQL) $(PACKAGE_DIR)
-$(IPKG_ASTERISK_PGSQL): asterisk-compile
- mkdir -p $(IDIR_ASTERISK_PGSQL)/usr/lib/asterisk/modules
- mkdir -p $(IDIR_ASTERISK_PGSQL)/etc/asterisk
- cp $(PKG_BUILD_DIR)/apps/app_sql_postgres.so $(IDIR_ASTERISK_PGSQL)/usr/lib/asterisk/modules/
- cp $(PKG_BUILD_DIR)/cdr/cdr_pgsql.so $(IDIR_ASTERISK_PGSQL)/usr/lib/asterisk/modules/
- $(STRIP) $(IDIR_ASTERISK_PGSQL)/usr/lib/asterisk/modules/*
- cp $(PKG_BUILD_DIR)/configs/cdr_pgsql.conf.sample $(IDIR_ASTERISK_PGSQL)/etc/asterisk/cdr_pgsql.conf
+$(IPKG_ASTERISK_PGSQL):
+ install -d -m0755 $(IDIR_ASTERISK_PGSQL)/etc/asterisk
+ install -m0600 $(PKG_BUILD_DIR)/configs/cdr_pgsql.conf.sample $(IDIR_ASTERISK_PGSQL)/etc/asterisk/cdr_pgsql.conf
+ install -d -m0755 $(IDIR_ASTERISK_PGSQL)/usr/lib/asterisk/modules
+ install -m0755 $(PKG_BUILD_DIR)/apps/app_sql_postgres.so $(IDIR_ASTERISK_PGSQL)/usr/lib/asterisk/modules/
+ install -m0755 $(PKG_BUILD_DIR)/cdr/cdr_pgsql.so $(IDIR_ASTERISK_PGSQL)/usr/lib/asterisk/modules/
+ $(RSTRIP) $(IDIR_ASTERISK_PGSQL)
$(IPKG_BUILD) $(IDIR_ASTERISK_PGSQL) $(PACKAGE_DIR)
-$(IPKG_ASTERISK_SOUNDS): asterisk-compile
- mkdir -p $(IDIR_ASTERISK_SOUNDS)/usr/lib/asterisk/sounds
- cp -a $(PKG_BUILD_DIR)/sounds/* $(IDIR_ASTERISK_SOUNDS)/usr/lib/asterisk/sounds/
+$(IPKG_ASTERISK_SOUNDS):
+ install -d -m0755 $(IDIR_ASTERISK_SOUNDS)/usr/lib/asterisk/sounds
+ cp -fpR $(PKG_BUILD_DIR)/sounds/* $(IDIR_ASTERISK_SOUNDS)/usr/lib/asterisk/sounds/
rm -f $(IDIR_ASTERISK_SOUNDS)/usr/lib/asterisk/sounds/*.mp3
rm -f $(IDIR_ASTERISK_SOUNDS)/usr/lib/asterisk/sounds/vm-*
$(IPKG_BUILD) $(IDIR_ASTERISK_SOUNDS) $(PACKAGE_DIR)
-$(IPKG_ASTERISK_SPEEX): asterisk-compile
- mkdir -p $(IDIR_ASTERISK_SPEEX)/usr/lib/asterisk/modules
- cp $(PKG_BUILD_DIR)/codecs/*speex.so $(IDIR_ASTERISK_SPEEX)/usr/lib/asterisk/modules
- $(STRIP) $(IDIR_ASTERISK_SPEEX)/usr/lib/asterisk/modules/*
- $(IPKG_BUILD) $(IDIR_ASTERISK_SPEEX) $(PACKAGE_DIR)
-
-$(IPKG_ASTERISK_VOICEMAIL): asterisk-compile
- mkdir -p $(IDIR_ASTERISK_VOICEMAIL)/etc/asterisk
- cp $(PKG_BUILD_DIR)/configs/voicemail.conf.sample $(IDIR_ASTERISK_VOICEMAIL)/etc/asterisk/voicemail.conf
- mkdir -p $(IDIR_ASTERISK_VOICEMAIL)/usr/lib/asterisk/modules
- cp $(PKG_BUILD_DIR)/apps/*voicemail.so $(IDIR_ASTERISK_VOICEMAIL)/usr/lib/asterisk/modules
- $(STRIP) $(IDIR_ASTERISK_VOICEMAIL)/usr/lib/asterisk/modules/*
+$(IPKG_ASTERISK_VOICEMAIL):
+ install -d -m0755 $(IDIR_ASTERISK_VOICEMAIL)/etc/asterisk
+ install -m0644 $(PKG_BUILD_DIR)/configs/voicemail.conf.sample $(IDIR_ASTERISK_VOICEMAIL)/etc/asterisk/voicemail.conf
+ install -d -m0755 $(IDIR_ASTERISK_VOICEMAIL)/usr/lib/asterisk/modules
+ install -m0755 $(PKG_BUILD_DIR)/apps/*voicemail.so $(IDIR_ASTERISK_VOICEMAIL)/usr/lib/asterisk/modules/
+ $(RSTRIP) $(IDIR_ASTERISK_VOICEMAIL)
$(IPKG_BUILD) $(IDIR_ASTERISK_VOICEMAIL) $(PACKAGE_DIR)
+
+$(IPKG_ASTERISK_CODEC_ILBC):
+ install -d -m0755 $(IDIR_ASTERISK_CODEC_ILBC)/usr/lib/asterisk/modules
+ install -m0755 $(PKG_BUILD_DIR)/codecs/codec_ilbc.so $(IDIR_ASTERISK_CODEC_ILBC)/usr/lib/asterisk/modules/
+ install -m0755 $(PKG_BUILD_DIR)/formats/format_ilbc.so $(IDIR_ASTERISK_CODEC_ILBC)/usr/lib/asterisk/modules/
+ $(RSTRIP) $(IDIR_ASTERISK_CODEC_ILBC)
+ $(IPKG_BUILD) $(IDIR_ASTERISK_CODEC_ILBC) $(PACKAGE_DIR)
+
+$(IPKG_ASTERISK_CODEC_LPC10):
+ install -d -m0755 $(IDIR_ASTERISK_CODEC_LPC10)/usr/lib/asterisk/modules
+ install -m0755 $(PKG_BUILD_DIR)/codecs/codec_lpc10.so $(IDIR_ASTERISK_CODEC_LPC10)/usr/lib/asterisk/modules/
+ $(RSTRIP) $(IDIR_ASTERISK_CODEC_LPC10)
+ $(IPKG_BUILD) $(IDIR_ASTERISK_CODEC_LPC10) $(PACKAGE_DIR)
+
+$(IPKG_ASTERISK_CODEC_SPEEX):
+ install -d -m0755 $(IDIR_ASTERISK_CODEC_SPEEX)/usr/lib/asterisk/modules
+ install -m0755 $(PKG_BUILD_DIR)/codecs/codec_speex.so $(IDIR_ASTERISK_CODEC_SPEEX)/usr/lib/asterisk/modules/
+ $(RSTRIP) $(IDIR_ASTERISK_CODEC_SPEEX)
+ $(IPKG_BUILD) $(IDIR_ASTERISK_CODEC_SPEEX) $(PACKAGE_DIR)
+
+$(IPKG_ASTERISK_PBX_DUNDI):
+ install -d -m0755 $(IDIR_ASTERISK_PBX_DUNDI)/etc/asterisk
+ install -m0644 $(PKG_BUILD_DIR)/configs/dundi.conf.sample $(IDIR_ASTERISK_PBX_DUNDI)/etc/asterisk/dundi.conf
+ install -d -m0755 $(IDIR_ASTERISK_PBX_DUNDI)/usr/lib/asterisk/modules
+ install -m0755 $(PKG_BUILD_DIR)/pbx/pbx_dundi.so $(IDIR_ASTERISK_PBX_DUNDI)/usr/lib/asterisk/modules/
+ $(RSTRIP) $(IDIR_ASTERISK_PBX_DUNDI)
+ $(IPKG_BUILD) $(IDIR_ASTERISK_PBX_DUNDI) $(PACKAGE_DIR)
+
diff --git a/openwrt/package/asterisk/ipkg/asterisk-codec-ilbc.control b/openwrt/package/asterisk/ipkg/asterisk-codec-ilbc.control
new file mode 100644
index 0000000000..cc0dd61910
--- /dev/null
+++ b/openwrt/package/asterisk/ipkg/asterisk-codec-ilbc.control
@@ -0,0 +1,7 @@
+Package: asterisk-codec-ilbc
+Priority: optional
+Section: net
+Maintainer: Felix Fietkau <nbd@vd-s.ath.cx>
+Source: buildroot internal
+Description: an Internet Low Bitrate Codec (ILBC) Translator for Asterisk
+Depends: asterisk
diff --git a/openwrt/package/asterisk/ipkg/asterisk-codec-lpc10.control b/openwrt/package/asterisk/ipkg/asterisk-codec-lpc10.control
new file mode 100644
index 0000000000..fa7146e570
--- /dev/null
+++ b/openwrt/package/asterisk/ipkg/asterisk-codec-lpc10.control
@@ -0,0 +1,7 @@
+Package: asterisk-codec-lpc10
+Priority: optional
+Section: net
+Maintainer: Felix Fietkau <nbd@vd-s.ath.cx>
+Source: buildroot internal
+Description: an LPC10 (Linear Predictor Code) 2.4kbps Voice Coder for Asterisk
+Depends: asterisk
diff --git a/openwrt/package/asterisk/ipkg/asterisk-pbx-dundi.conffiles b/openwrt/package/asterisk/ipkg/asterisk-pbx-dundi.conffiles
new file mode 100644
index 0000000000..ab116b5d18
--- /dev/null
+++ b/openwrt/package/asterisk/ipkg/asterisk-pbx-dundi.conffiles
@@ -0,0 +1 @@
+/etc/asterisk/dundi.conf
diff --git a/openwrt/package/asterisk/ipkg/asterisk-pbx-dundi.control b/openwrt/package/asterisk/ipkg/asterisk-pbx-dundi.control
new file mode 100644
index 0000000000..1575b29f66
--- /dev/null
+++ b/openwrt/package/asterisk/ipkg/asterisk-pbx-dundi.control
@@ -0,0 +1,7 @@
+Package: asterisk-pbx-dundi
+Priority: optional
+Section: net
+Maintainer: Felix Fietkau <nbd@vd-s.ath.cx>
+Source: buildroot internal
+Description: Distributed Universal Number Discovery (DUNDi) support for Asterisk
+Depends: asterisk
diff --git a/openwrt/package/asterisk/patches/asterisk-1.0.7-pbx_dundi.patch b/openwrt/package/asterisk/patches/asterisk-1.0.7-pbx_dundi.patch
new file mode 100644
index 0000000000..f8066ba40c
--- /dev/null
+++ b/openwrt/package/asterisk/patches/asterisk-1.0.7-pbx_dundi.patch
@@ -0,0 +1,6493 @@
+diff -ruN asterisk-1.0.7-orig/channels/chan_iax2.c asterisk-1.0.7-pbx_dundi/channels/chan_iax2.c
+--- asterisk-1.0.7-orig/channels/chan_iax2.c 2005-03-18 18:30:05.000000000 +0100
++++ asterisk-1.0.7-pbx_dundi/channels/chan_iax2.c 2005-06-02 20:21:37.000000000 +0200
+@@ -197,6 +197,7 @@
+ struct iax2_user {
+ char name[80];
+ char secret[80];
++ char dbsecret[80];
+ int authmethods;
+ char accountcode[20];
+ char inkeys[80]; /* Key(s) this user can use to authenticate to us */
+@@ -219,6 +220,7 @@
+ char name[80];
+ char username[80];
+ char secret[80];
++ char dbsecret[80];
+ char outkey[80]; /* What key we use to talk to this peer */
+ char context[AST_MAX_EXTENSION]; /* Default context (for transfer really) */
+ char regexten[AST_MAX_EXTENSION]; /* Extension to register (if regcontext is used) */
+@@ -2194,6 +2196,26 @@
+ *notransfer=p->notransfer;
+ if (usejitterbuf)
+ *usejitterbuf=p->usejitterbuf;
++ if (secret) {
++ if (!ast_strlen_zero(p->dbsecret)) {
++ char *family, *key=NULL;
++ family = ast_strdupa(p->dbsecret);
++ if (family) {
++ key = strchr(family, '/');
++ if (key) {
++ *key = '\0';
++ key++;
++ }
++ }
++ if (!family || !key || ast_db_get(family, key, secret, seclen)) {
++ ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", p->dbsecret);
++ if (p->temponly)
++ free(p);
++ p = NULL;
++ }
++ } else
++ strncpy(secret, p->secret, seclen); /* safe */
++ }
+ } else {
+ if (p->temponly)
+ free(p);
+@@ -3624,6 +3646,15 @@
+ return 0;
+ }
+
++static void free_context(struct iax2_context *con)
++{
++ struct iax2_context *conl;
++ while(con) {
++ conl = con;
++ con = con->next;
++ free(conl);
++ }
++}
+
+ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies)
+ {
+@@ -3769,6 +3800,28 @@
+ strncpy(iaxs[callno]->language, user->language, sizeof(iaxs[callno]->language)-1);
+ iaxs[callno]->notransfer = user->notransfer;
+ iaxs[callno]->usejitterbuf = user->usejitterbuf;
++ /* Keep this check last */
++ if (!ast_strlen_zero(user->dbsecret)) {
++ char *family, *key=NULL;
++ family = ast_strdupa(user->dbsecret);
++ if (family) {
++ key = strchr(family, '/');
++ if (key) {
++ *key = '\0';
++ key++;
++ }
++ }
++ if (!family || !key || ast_db_get(family, key, iaxs[callno]->secret, sizeof(iaxs[callno]->secret))) {
++ ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", user->dbsecret);
++ if (user->temponly) {
++ ast_free_ha(user->ha);
++ free_context(user->contexts);
++ free(user);
++ user = NULL;
++ }
++ }
++ } else
++ strncpy(iaxs[callno]->secret, user->secret, sizeof(iaxs[callno]->secret) - 1);
+ res = 0;
+ }
+ iaxs[callno]->trunk = iax2_getpeertrunk(*sin);
+@@ -3844,15 +3897,23 @@
+ } else if (p->authmethods & IAX_AUTH_MD5) {
+ struct MD5Context md5;
+ unsigned char digest[16];
+- MD5Init(&md5);
+- MD5Update(&md5, p->challenge, strlen(p->challenge));
+- MD5Update(&md5, p->secret, strlen(p->secret));
+- MD5Final(digest, &md5);
+- /* If they support md5, authenticate with it. */
+- for (x=0;x<16;x++)
+- sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
+- if (!strcasecmp(requeststr, md5secret))
+- res = 0;
++ char *tmppw, *stringp;
++
++ tmppw = ast_strdupa(p->secret);
++ stringp = tmppw;
++ while((tmppw = strsep(&stringp, ";"))) {
++ MD5Init(&md5);
++ MD5Update(&md5, p->challenge, strlen(p->challenge));
++ MD5Update(&md5, tmppw, strlen(tmppw));
++ MD5Final(digest, &md5);
++ /* If they support md5, authenticate with it. */
++ for (x=0;x<16;x++)
++ sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
++ if (!strcasecmp(requeststr, md5secret)) {
++ res = 0;
++ break;
++ }
++ }
+ } else if (p->authmethods & IAX_AUTH_PLAINTEXT) {
+ if (!strcmp(secret, p->secret))
+ res = 0;
+@@ -6237,16 +6298,6 @@
+ return 0;
+ }
+
+-static void free_context(struct iax2_context *con)
+-{
+- struct iax2_context *conl;
+- while(con) {
+- conl = con;
+- con = con->next;
+- free(conl);
+- }
+-}
+-
+ static struct ast_channel *iax2_request(char *type, int format, void *data)
+ {
+ int callno;
+@@ -6469,6 +6520,8 @@
+ strncpy(peer->secret, v->value, sizeof(peer->secret)-1);
+ else if (!strcasecmp(v->name, "mailbox"))
+ strncpy(peer->mailbox, v->value, sizeof(peer->mailbox) - 1);
++ else if (!strcasecmp(v->name, "dbsecret"))
++ strncpy(peer->dbsecret, v->value, sizeof(peer->dbsecret)-1);
+ else if (!strcasecmp(v->name, "mailboxdetail"))
+ peer->messagedetail = ast_true(v->value);
+ else if (!strcasecmp(v->name, "trunk")) {
+@@ -6665,6 +6718,8 @@
+ user->notransfer = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "jitterbuffer")) {
+ user->usejitterbuf = ast_true(v->value);
++ } else if (!strcasecmp(v->name, "dbsecret")) {
++ strncpy(user->dbsecret, v->value, sizeof(user->dbsecret)-1);
+ } else if (!strcasecmp(v->name, "secret")) {
+ strncpy(user->secret, v->value, sizeof(user->secret)-1);
+ } else if (!strcasecmp(v->name, "callerid")) {
+diff -ruN asterisk-1.0.7-orig/configs/dundi.conf.sample asterisk-1.0.7-pbx_dundi/configs/dundi.conf.sample
+--- asterisk-1.0.7-orig/configs/dundi.conf.sample 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.0.7-pbx_dundi/configs/dundi.conf.sample 2005-06-02 20:21:37.000000000 +0200
+@@ -0,0 +1,225 @@
++;
++; DUNDi configuration file
++;
++;
++[general]
++;
++; The "general" section contains general parameters relating
++; to the operation of the dundi client and server.
++;
++; The first part should be your complete contact information
++; should someone else in your peer group need to contact you.
++;
++;department=Your Department
++;organization=Your Company, Inc.
++;locality=Your City
++;stateprov=ST
++;country=US
++;email=your@email.com
++;phone=+12565551212
++;
++;
++; Specify bind address and port number. Default is
++; 4520
++;
++;bindaddr=0.0.0.0
++;port=4520
++;
++; Our entity identifier (Should generally be the MAC address of the
++; machine it's running on. Defaults to the first eth address, but you
++; can override it here, as long as you set it to the MAC of *something*
++; you own!)
++;
++;entityid=00:07:E9:3B:76:60
++;
++; Define the max depth in which to search the DUNDi system (also max # of
++; seconds to wait for a reply)
++;
++ttl=32
++;
++; If we don't get ACK to our DPREQUEST within 2000ms, and autokill is set
++; to yes, then we cancel the whole thing (that's enough time for one
++; retransmission only). This is used to keep things from stalling for a long
++; time for a host that is not available, but would be ill advised for bad
++; connections. In addition to 'yes' or 'no' you can also specify a number
++; of milliseconds. See 'qualify' for individual peers to turn on for just
++; a specific peer.
++;
++autokill=yes
++;
++; pbx_dundi creates a rotating key called "secret", under the family
++; 'secretpath'. The default family is dundi (resulting in
++; the key being held at dundi/secret).
++;
++;secretpath=dundi
++;
++; The 'storehistory' option (also changeable at runtime with
++; 'dundi store history' and 'dundi no store history') will
++; cause the DUNDi engine to keep track of the last several
++; queries and the amount of time each query took to execute
++; for the purpose of tracking slow nodes. This option is
++; off by default due to performance impacts.
++;
++;storehistory=yes
++
++[mappings]
++;
++; The "mappings" section maps DUNDi contexts
++; to contexts on the local asterisk system. Remember
++; that numbers that are made available under the e164
++; DUNDi context are regulated by the DUNDi General Peering
++; Agreement (GPA) if you are a member of the DUNDi E.164
++; Peering System.
++;
++; dundi_context => local_context,weight,tech,dest[,options]]
++;
++; dundi_context is the name of the context being requested
++; within the DUNDi request
++;
++; local_context is the name of the context on the local system
++; in which numbers can be looked up for which responses shall be given.
++;
++; tech is the technology to use (IAX, SIP, H323)
++;
++; dest is the destination to supply for reaching that number. The
++; following variables can be used in the destination string and will
++; be automatically substituted:
++; ${NUMBER}: The number being requested
++; ${IPADDR}: The IP address to connect to
++; ${SECRET}: The current rotating secret key to be used
++;
++; Further options may include:
++;
++; nounsolicited: No unsolicited calls of any type permitted via this
++; route
++; nocomunsolicit: No commercial unsolicited calls permitted via
++; this route
++; residential: This number is known to be a residence
++; commercial: This number is known to be a business
++; mobile: This number is known to be a mobile phone
++; nocomunsolicit: No commercial unsolicited calls permitted via
++; this route
++; nopartial: Do not search for partial matches
++;
++; There *must* exist an entry in mappings for DUNDi to respond
++; to any request, although it may be empty.
++;
++;e164 => dundi-e164-canonical,0,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
++;e164 => dundi-e164-customers,100,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
++;e164 => dundi-e164-via-pstn,400,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
++
++;digexten => default,0,IAX2,guest@lappy/${NUMBER}
++;asdf =>
++
++
++;
++;
++; The remaining sections represent the peers
++; that we fundamentally trust. The section name
++; represents the name and optionally at a specific
++; DUNDi context if you want the trust to be established
++; for only a specific DUNDi context.
++;
++; inkey - What key they will be authenticating to us with
++;
++; outkey - What key we use to authenticate to them
++;
++; host - What their host is
++;
++; order - What search order to use. May be 'primary', 'secondary',
++; 'tertiary' or 'quartiary'. In large systems, it is beneficial
++; to only query one up-stream host in order to maximize caching
++; value. Adding one with primary and one with secondary gives you
++; redundancy without sacraficing performance.
++;
++; include - Includes this peer when searching a particular context
++; for lookup (set "all" to perform all lookups with that
++; host. This is also the context in which peers are permitted
++; to precache.
++;
++; noinclude - Disincludes this peer when searching a particular context
++; for lookup (set "all" to perform no lookups with that
++; host.
++;
++; permit - Permits this peer to search a given DUNDi context on
++; the local system. Set "all" to permit this host to
++; lookup all contexts. This is also a context for which
++; we will create/forward PRECACHE commands.
++;
++; deny - Denies this peer to search a given DUNDi context on
++; the local system. Set "all" to deny this host to
++; lookup all contexts.
++;
++; model - inbound, outbound, or symmetric for whether we receive
++; requests only, transmit requests only, or do both.
++;
++; precache - Utilize/Permit precaching with this peer (to pre
++; cache means to provide an answer when no request
++; was made and is used so that machines with few
++; routes can push those routes up a to a higher level).
++; outgoing means we send precache routes to this peer,
++; incoming means we permit this peer to send us
++; precache routes. symmetric means we do both.
++;
++; Note: You cannot mix symmetric/outbound model with symmetric/inbound
++; precache, nor can you mix symmetric/inbound model with symmetric/outbound
++; precache.
++;
++;
++; The '*' peer is special and matches an unspecified entity
++;
++
++;
++; Sample Primary e164 DUNDi peer
++;
++[00:50:8B:F3:75:BB]
++model = symmetric
++host = 64.215.96.114
++inkey = digium
++outkey = misery
++include = e164
++permit = e164
++qualify = yes
++
++;
++; Sample Secondary e164 DUNDi peer
++;
++;[00:A0:C9:96:92:84]
++;model = symmetric
++;host = misery.digium.com
++;inkey = misery
++;outkey = ourkey
++;include = e164
++;permit = e164
++;qualify = yes
++;order = secondary
++
++;
++; Sample "push mode" downstream host
++;
++;[00:0C:76:96:75:28]
++;model = incoming
++;host = dynamic
++;precache = incoming
++;inkey = littleguy
++;outkey = ourkey
++;include = e164 ; In this case used only for precaching
++;permit = e164
++;qualify = yes
++
++;
++; Sample "push mode" upstream host
++;
++;[00:07:E9:3B:76:60]
++;model = outbound
++;precache = outbound
++;host = 216.207.245.34
++;register = yes
++;inkey = dhcp34
++;permit = all ; In this case used only for precaching
++;include = all
++;qualify = yes
++;outkey=foo
++
++;[*]
++;
+diff -ruN asterisk-1.0.7-orig/include/asterisk/dundi.h asterisk-1.0.7-pbx_dundi/include/asterisk/dundi.h
+--- asterisk-1.0.7-orig/include/asterisk/dundi.h 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.0.7-pbx_dundi/include/asterisk/dundi.h 2005-06-02 20:21:37.000000000 +0200
+@@ -0,0 +1,212 @@
++/*
++ * Distributed Universal Number Discovery (DUNDi)
++ *
++ * Copyright (C) 2004, Digium Inc.
++ *
++ * Written by Mark Spencer <markster@digium.com>
++ *
++ * This program is Free Software distributed under the terms of
++ * of the GNU General Public License.
++ */
++#ifndef _ASTERISK_DUNDI_H
++#define _ASTERISK_DUNDI_H
++
++#include <asterisk/channel.h>
++
++#define DUNDI_PORT 4520
++
++/* A DUNDi Entity ID is essentially a MAC address, brief and unique */
++struct _dundi_eid {
++ unsigned char eid[6];
++} __attribute__ ((__packed__));
++
++typedef struct _dundi_eid dundi_eid;
++
++struct dundi_hdr {
++ unsigned short strans; /* Source transaction */
++ unsigned short dtrans; /* Destination transaction */
++ unsigned char iseqno; /* Next expected incoming sequence number */
++ unsigned char oseqno; /* Outgoing sequence number */
++ unsigned char cmdresp; /* Command / Response */
++ unsigned char cmdflags; /* Command / Response specific flags*/
++ unsigned char ies[0];
++} __attribute__ ((__packed__));
++
++struct dundi_ie_hdr {
++ unsigned char ie;
++ unsigned char len;
++ unsigned char iedata[0];
++} __attribute__ ((__packed__));
++
++#define DUNDI_FLAG_RETRANS (1 << 16) /* Applies to dtrans */
++#define DUNDI_FLAG_RESERVED (1 << 16) /* Applies to strans */
++
++#define DUNDI_PROTO_NONE 0 /* No answer yet */
++#define DUNDI_PROTO_IAX 1 /* IAX version 2 */
++#define DUNDI_PROTO_SIP 2 /* Session Initiation Protocol */
++#define DUNDI_PROTO_H323 3 /* ITU H.323 */
++
++#define DUNDI_FLAG_NONEXISTANT (0) /* Isn't and can't be a valid number */
++#define DUNDI_FLAG_EXISTS (1 << 0) /* Is a valid number */
++#define DUNDI_FLAG_MATCHMORE (1 << 1) /* Might be valid if you add more digits */
++#define DUNDI_FLAG_CANMATCH (1 << 2) /* Might be a match */
++#define DUNDI_FLAG_IGNOREPAT (1 << 3) /* Keep dialtone */
++#define DUNDI_FLAG_RESIDENTIAL (1 << 4) /* Destination known to be residential */
++#define DUNDI_FLAG_COMMERCIAL (1 << 5) /* Destination known to be commercial */
++#define DUNDI_FLAG_MOBILE (1 << 6) /* Destination known to be cellular/mobile */
++#define DUNDI_FLAG_NOUNSOLICITED (1 << 7) /* No unsolicited calls of any kind through this route */
++#define DUNDI_FLAG_NOCOMUNSOLICIT (1 << 8) /* No commercial unsolicited calls through this route */
++
++#define DUNDI_HINT_NONE (0)
++#define DUNDI_HINT_TTL_EXPIRED (1 << 0) /* TTL Expired */
++#define DUNDI_HINT_DONT_ASK (1 << 1) /* Don't ask for anything beginning with data */
++#define DUNDI_HINT_UNAFFECTED (1 << 2) /* Answer not affected by entity list */
++
++struct dundi_encblock { /* AES-128 encrypted block */
++ unsigned char iv[16]; /* Initialization vector of random data */
++ unsigned char encdata[0]; /* Encrypted / compressed data */
++} __attribute__ ((__packed__));
++
++struct dundi_answer {
++ dundi_eid eid; /* Original source of answer */
++ unsigned char protocol; /* Protocol (DUNDI_PROTO_*) */
++ unsigned short flags; /* Flags relating to answer */
++ unsigned short weight; /* Weight of answers */
++ unsigned char data[0]; /* Protocol specific URI */
++} __attribute__ ((__packed__));
++
++struct dundi_hint {
++ unsigned short flags; /* Flags relating to answer */
++ unsigned char data[0]; /* For data for hint */
++} __attribute__ ((__packed__));
++
++#define DUNDI_CAUSE_SUCCESS 0 /* Success */
++#define DUNDI_CAUSE_GENERAL 1 /* General unspecified failure */
++#define DUNDI_CAUSE_DYNAMIC 2 /* Requested entity is dynamic */
++#define DUNDI_CAUSE_NOAUTH 3 /* No or improper authorization */
++#define DUNDI_CAUSE_DUPLICATE 4 /* Duplicate request */
++#define DUNDI_CAUSE_TTL_EXPIRED 5 /* Expired TTL */
++#define DUNDI_CAUSE_NEEDKEY 6 /* Need new session key to decode */
++#define DUNDI_CAUSE_BADENCRYPT 7 /* Badly encrypted data */
++
++struct dundi_cause {
++ unsigned char causecode; /* Numerical cause (DUNDI_CAUSE_*) */
++ char desc[0]; /* Textual description */
++} __attribute__ ((__packed__));
++
++struct dundi_peer_status {
++ unsigned int flags;
++ unsigned short netlag;
++ unsigned short querylag;
++ dundi_eid peereid;
++} __attribute__ ((__packed__));
++
++#define DUNDI_PEER_PRIMARY (1 << 0)
++#define DUNDI_PEER_SECONDARY (1 << 1)
++#define DUNDI_PEER_UNAVAILABLE (1 << 2)
++#define DUNDI_PEER_REGISTERED (1 << 3)
++#define DUNDI_PEER_MOD_OUTBOUND (1 << 4)
++#define DUNDI_PEER_MOD_INBOUND (1 << 5)
++#define DUNDI_PEER_PCMOD_OUTBOUND (1 << 6)
++#define DUNDI_PEER_PCMOD_INBOUND (1 << 7)
++
++#define DUNDI_COMMAND_FINAL (0x80) /* Or'd with other flags */
++
++#define DUNDI_COMMAND_ACK (0 | 0x40) /* Ack a message */
++#define DUNDI_COMMAND_DPDISCOVER 1 /* Request discovery */
++#define DUNDI_COMMAND_DPRESPONSE (2 | 0x40) /* Respond to a discovery request */
++#define DUNDI_COMMAND_EIDQUERY 3 /* Request information for a peer */
++#define DUNDI_COMMAND_EIDRESPONSE (4 | 0x40) /* Response to a peer query */
++#define DUNDI_COMMAND_PRECACHERQ 5 /* Pre-cache Request */
++#define DUNDI_COMMAND_PRECACHERP (6 | 0x40) /* Pre-cache Response */
++#define DUNDI_COMMAND_INVALID (7 | 0x40) /* Invalid dialog state (does not require ack) */
++#define DUNDI_COMMAND_UNKNOWN (8 | 0x40) /* Unknown command */
++#define DUNDI_COMMAND_NULL 9 /* No-op */
++#define DUNDI_COMMAND_REGREQ (10) /* Register Request */
++#define DUNDI_COMMAND_REGRESPONSE (11 | 0x40) /* Register Response */
++#define DUNDI_COMMAND_CANCEL (12) /* Cancel transaction entirely */
++#define DUNDI_COMMAND_ENCRYPT (13) /* Send an encrypted message */
++#define DUNDI_COMMAND_ENCREJ (14 | 0x40) /* Reject an encrypted message */
++
++#define DUNDI_COMMAND_STATUS 15 /* Status command */
++
++/*
++ * Remember that some information elements may occur
++ * more than one time within a message
++ */
++
++#define DUNDI_IE_EID 1 /* Entity identifier (dundi_eid) */
++#define DUNDI_IE_CALLED_CONTEXT 2 /* DUNDi Context (string) */
++#define DUNDI_IE_CALLED_NUMBER 3 /* Number of equivalent (string) */
++#define DUNDI_IE_EID_DIRECT 4 /* Entity identifier (dundi_eid), direct connect */
++#define DUNDI_IE_ANSWER 5 /* An answer (struct dundi_answer) */
++#define DUNDI_IE_TTL 6 /* Max TTL for this request / Remaining TTL for the response (short)*/
++#define DUNDI_IE_VERSION 10 /* DUNDi version (should be 1) (short) */
++#define DUNDI_IE_EXPIRATION 11 /* Recommended expiration (short) */
++#define DUNDI_IE_UNKNOWN 12 /* Unknown command (byte) */
++#define DUNDI_IE_CAUSE 14 /* Success or cause of failure */
++#define DUNDI_IE_REQEID 15 /* EID being requested for EIDQUERY*/
++#define DUNDI_IE_ENCDATA 16 /* AES-128 encrypted data */
++#define DUNDI_IE_SHAREDKEY 17 /* RSA encrypted AES-128 key */
++#define DUNDI_IE_SIGNATURE 18 /* RSA Signature of encrypted shared key */
++#define DUNDI_IE_KEYCRC32 19 /* CRC32 of encrypted key (int) */
++#define DUNDI_IE_HINT 20 /* Answer hints (struct ast_hint) */
++
++#define DUNDI_IE_DEPARTMENT 21 /* Department, for EIDQUERY (string) */
++#define DUNDI_IE_ORGANIZATION 22 /* Organization, for EIDQUERY (string) */
++#define DUNDI_IE_LOCALITY 23 /* City/Locality, for EIDQUERY (string) */
++#define DUNDI_IE_STATE_PROV 24 /* State/Province, for EIDQUERY (string) */
++#define DUNDI_IE_COUNTRY 25 /* Country, for EIDQUERY (string) */
++#define DUNDI_IE_EMAIL 26 /* E-mail addy, for EIDQUERY (string) */
++#define DUNDI_IE_PHONE 27 /* Contact Phone, for EIDQUERY (string) */
++#define DUNDI_IE_IPADDR 28 /* IP Address, for EIDQUERY (string) */
++#define DUNDI_IE_CACHEBYPASS 29 /* Bypass cache (empty) */
++
++#define DUNDI_IE_PEERSTATUS 30 /* Peer/peer status (struct dundi_peer_status) */
++
++#define DUNDI_FLUFF_TIME 2000 /* Amount of time for answer */
++#define DUNDI_TTL_TIME 200 /* Incremental average time */
++
++#define DUNDI_DEFAULT_RETRANS 5
++#define DUNDI_DEFAULT_RETRANS_TIMER 1000
++#define DUNDI_DEFAULT_TTL 120 /* In seconds/hops like TTL */
++#define DUNDI_DEFAULT_VERSION 1
++#define DUNDI_DEFAULT_CACHE_TIME 3600 /* In seconds */
++#define DUNDI_DEFAULT_KEY_EXPIRE 3600 /* Life of shared key In seconds */
++#define DUNDI_DEF_EMPTY_CACHE_TIME 60 /* In seconds, cache of empty answer */
++#define DUNDI_WINDOW 1 /* Max 1 message in window */
++
++#define DEFAULT_MAXMS 2000
++
++struct dundi_result {
++ int flags;
++ int weight;
++ int expiration;
++ int techint;
++ dundi_eid eid;
++ char eid_str[20];
++ char tech[10];
++ char dest[256];
++};
++
++struct dundi_entity_info {
++ char country[80];
++ char stateprov[80];
++ char locality[80];
++ char org[80];
++ char orgunit[80];
++ char email[80];
++ char phone[80];
++ char ipaddr[80];
++};
++
++/* 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.
++ returns the number of results found or -1 on a hangup of teh channel. */
++int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int nocache);
++
++/* Retrieve information on a specific EID */
++int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid);
++
++/* Pre-cache to push upstream peers */
++int dundi_precache(const char *dcontext, const char *number);
++#endif /* _ASTERISK_DUNDI_H */
+diff -ruN asterisk-1.0.7-orig/pbx/Makefile asterisk-1.0.7-pbx_dundi/pbx/Makefile
+--- asterisk-1.0.7-orig/pbx/Makefile 2003-10-26 19:50:49.000000000 +0100
++++ asterisk-1.0.7-pbx_dundi/pbx/Makefile 2005-06-02 20:21:37.000000000 +0200
+@@ -13,7 +13,7 @@
+
+
+
+-PBX_LIBS=pbx_config.so pbx_wilcalu.so pbx_spool.so # pbx_gtkconsole.so pbx_kdeconsole.so
++PBX_LIBS=pbx_config.so pbx_wilcalu.so pbx_spool.so pbx_dundi.so # pbx_gtkconsole.so pbx_kdeconsole.so
+
+ # Add GTK console if appropriate
+ PBX_LIBS+=$(shell gtk-config --cflags >/dev/null 2>/dev/null && echo "pbx_gtkconsole.so")
+@@ -51,6 +51,9 @@
+ pbx_kdeconsole.so: $(KDE_CONSOLE_OBJS)
+ $(CC) $(SOLINK) -o $@ $(KDE_CONSOLE_OBJS) $(KDE_LIBS)
+
++pbx_dundi.so: dundi-parser.o pbx_dundi.o
++ $(CC) $(SOLINK) -o $@ pbx_dundi.o dundi-parser.o $(LDFLAGS_EXTRA) -lz
++
+ %.moc : %.h
+ $(MOC) $< -o $@
+
+diff -ruN asterisk-1.0.7-orig/pbx/dundi-parser.c asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.c
+--- asterisk-1.0.7-orig/pbx/dundi-parser.c 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.c 2005-06-02 20:21:37.000000000 +0200
+@@ -0,0 +1,813 @@
++/*
++ * Distributed Universal Number Discovery (DUNDi)
++ *
++ * Copyright (C) 2004, Digium Inc.
++ *
++ * Written by Mark Spencer <markster@digium.com>
++ *
++ * This program is Free Software distributed under the terms of
++ * of the GNU General Public License.
++ */
++
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <string.h>
++#include <netinet/in.h>
++#include <asterisk/frame.h>
++#include <asterisk/utils.h>
++#include <arpa/inet.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <asterisk/dundi.h>
++#include "dundi-parser.h"
++#include <asterisk/dundi.h>
++
++static void internaloutput(const char *str)
++{
++ fputs(str, stdout);
++}
++
++static void internalerror(const char *str)
++{
++ fprintf(stderr, "WARNING: %s", str);
++}
++
++static void (*outputf)(const char *str) = internaloutput;
++static void (*errorf)(const char *str) = internalerror;
++
++char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid)
++{
++ int x;
++ char *os = s;
++ if (maxlen < 18) {
++ if (s && (maxlen > 0))
++ *s = '\0';
++ } else {
++ for (x=0;x<5;x++) {
++ sprintf(s, "%02x:", eid->eid[x]);
++ s += 3;
++ }
++ sprintf(s, "%02x", eid->eid[5]);
++ }
++ return os;
++}
++
++char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
++{
++ int x;
++ char *os = s;
++ if (maxlen < 13) {
++ if (s && (maxlen > 0))
++ *s = '\0';
++ } else {
++ for (x=0;x<6;x++) {
++ sprintf(s, "%02X", eid->eid[x]);
++ s += 2;
++ }
++ }
++ return os;
++}
++
++int dundi_str_to_eid(dundi_eid *eid, char *s)
++{
++ unsigned int eid_int[6];
++ int x;
++ if (sscanf(s, "%x:%x:%x:%x:%x:%x", &eid_int[0], &eid_int[1], &eid_int[2],
++ &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
++ return -1;
++ for (x=0;x<6;x++)
++ eid->eid[x] = eid_int[x];
++ return 0;
++}
++
++int dundi_str_short_to_eid(dundi_eid *eid, char *s)
++{
++ unsigned int eid_int[6];
++ int x;
++ if (sscanf(s, "%2x%2x%2x%2x%2x%2x", &eid_int[0], &eid_int[1], &eid_int[2],
++ &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
++ return -1;
++ for (x=0;x<6;x++)
++ eid->eid[x] = eid_int[x];
++ return 0;
++}
++
++int dundi_eid_zero(dundi_eid *eid)
++{
++ int x;
++ for (x=0;x<sizeof(eid->eid) / sizeof(eid->eid[0]);x++)
++ if (eid->eid[x]) return 0;
++ return 1;
++}
++
++int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2)
++{
++ return memcmp(eid1, eid2, sizeof(dundi_eid));
++}
++
++static void dump_string(char *output, int maxlen, void *value, int len)
++{
++ maxlen--;
++ if (maxlen > len)
++ maxlen = len;
++ strncpy(output,value, maxlen);
++ output[maxlen] = '\0';
++}
++
++static void dump_cbypass(char *output, int maxlen, void *value, int len)
++{
++ strncpy(output, "Bypass Caches", maxlen);
++ output[maxlen] = '\0';
++}
++
++static void dump_eid(char *output, int maxlen, void *value, int len)
++{
++ if (len == 6)
++ dundi_eid_to_str(output, maxlen, (dundi_eid *)value);
++ else
++ snprintf(output, maxlen, "Invalid EID len %d", len);
++}
++
++char *dundi_hint2str(char *buf, int bufsiz, int flags)
++{
++ strcpy(buf, "");
++ buf[bufsiz-1] = '\0';
++ if (flags & DUNDI_HINT_TTL_EXPIRED) {
++ strncat(buf, "TTLEXPIRED|", bufsiz - strlen(buf) - 1);
++ }
++ if (flags & DUNDI_HINT_DONT_ASK) {
++ strncat(buf, "DONTASK|", bufsiz - strlen(buf) - 1);
++ }
++ if (flags & DUNDI_HINT_UNAFFECTED) {
++ strncat(buf, "UNAFFECTED|", bufsiz - strlen(buf) - 1);
++ }
++ /* Get rid of trailing | */
++ if (ast_strlen_zero(buf))
++ strcpy(buf, "NONE|");
++ buf[strlen(buf)-1] = '\0';
++ return buf;
++}
++
++static void dump_hint(char *output, int maxlen, void *value, int len)
++{
++ unsigned short flags;
++ char tmp[512];
++ char tmp2[256];
++ if (len < 2) {
++ strncpy(output, "<invalid contents>", maxlen);
++ return;
++ }
++ memcpy(&flags, value, sizeof(flags));
++ flags = ntohs(flags);
++ memset(tmp, 0, sizeof(tmp));
++ dundi_hint2str(tmp2, sizeof(tmp2), flags);
++ snprintf(tmp, sizeof(tmp), "[%s] ", tmp2);
++ memcpy(tmp + strlen(tmp), value + 2, len - 2);
++ strncpy(output, tmp, maxlen - 1);
++}
++
++static void dump_cause(char *output, int maxlen, void *value, int len)
++{
++ static char *causes[] = {
++ "SUCCESS",
++ "GENERAL",
++ "DYNAMIC",
++ "NOAUTH" ,
++ };
++ char tmp[256];
++ char tmp2[256];
++ int mlen;
++ unsigned char cause;
++ if (len < 1) {
++ strncpy(output, "<invalid contents>", maxlen);
++ return;
++ }
++ cause = *((unsigned char *)value);
++ memset(tmp2, 0, sizeof(tmp2));
++ mlen = len - 1;
++ if (mlen > 255)
++ mlen = 255;
++ memcpy(tmp2, value + 1, mlen);
++ if (cause < sizeof(causes) / sizeof(causes[0])) {
++ if (len > 1)
++ snprintf(tmp, sizeof(tmp), "%s: %s", causes[cause], tmp2);
++ else
++ snprintf(tmp, sizeof(tmp), "%s", causes[cause]);
++ } else {
++ if (len > 1)
++ snprintf(tmp, sizeof(tmp), "%d: %s", cause, tmp2);
++ else
++ snprintf(tmp, sizeof(tmp), "%d", cause);
++ }
++
++ strncpy(output,tmp, maxlen);
++ output[maxlen] = '\0';
++}
++
++static void dump_int(char *output, int maxlen, void *value, int len)
++{
++ if (len == (int)sizeof(unsigned int))
++ snprintf(output, maxlen, "%lu", (unsigned long)ntohl(*((unsigned int *)value)));
++ else
++ snprintf(output, maxlen, "Invalid INT");
++}
++
++static void dump_short(char *output, int maxlen, void *value, int len)
++{
++ if (len == (int)sizeof(unsigned short))
++ snprintf(output, maxlen, "%d", ntohs(*((unsigned short *)value)));
++ else
++ snprintf(output, maxlen, "Invalid SHORT");
++}
++
++static void dump_byte(char *output, int maxlen, void *value, int len)
++{
++ if (len == (int)sizeof(unsigned char))
++ snprintf(output, maxlen, "%d", *((unsigned char *)value));
++ else
++ snprintf(output, maxlen, "Invalid BYTE");
++}
++
++static char *proto2str(int proto, char *buf, int bufsiz)
++{
++ switch(proto) {
++ case DUNDI_PROTO_NONE:
++ strncpy(buf, "None", bufsiz - 1);
++ break;
++ case DUNDI_PROTO_IAX:
++ strncpy(buf, "IAX", bufsiz - 1);
++ break;
++ case DUNDI_PROTO_SIP:
++ strncpy(buf, "SIP", bufsiz - 1);
++ break;
++ case DUNDI_PROTO_H323:
++ strncpy(buf, "H.323", bufsiz - 1);
++ break;
++ default:
++ snprintf(buf, bufsiz, "Unknown Proto(%d)", proto);
++ }
++ buf[bufsiz-1] = '\0';
++ return buf;
++}
++
++char *dundi_flags2str(char *buf, int bufsiz, int flags)
++{
++ strcpy(buf, "");
++ buf[bufsiz-1] = '\0';
++ if (flags & DUNDI_FLAG_EXISTS) {
++ strncat(buf, "EXISTS|", bufsiz - strlen(buf) - 1);
++ }
++ if (flags & DUNDI_FLAG_MATCHMORE) {
++ strncat(buf, "MATCHMORE|", bufsiz - strlen(buf) - 1);
++ }
++ if (flags & DUNDI_FLAG_CANMATCH) {
++ strncat(buf, "CANMATCH|", bufsiz - strlen(buf) - 1);
++ }
++ if (flags & DUNDI_FLAG_IGNOREPAT) {
++ strncat(buf, "IGNOREPAT|", bufsiz - strlen(buf) - 1);
++ }
++ if (flags & DUNDI_FLAG_RESIDENTIAL) {
++ strncat(buf, "RESIDENCE|", bufsiz - strlen(buf) - 1);
++ }
++ if (flags & DUNDI_FLAG_COMMERCIAL) {
++ strncat(buf, "COMMERCIAL|", bufsiz - strlen(buf) - 1);
++ }
++ if (flags & DUNDI_FLAG_MOBILE) {
++ strncat(buf, "MOBILE", bufsiz - strlen(buf) - 1);
++ }
++ if (flags & DUNDI_FLAG_NOUNSOLICITED) {
++ strncat(buf, "NOUNSLCTD|", bufsiz - strlen(buf) - 1);
++ }
++ if (flags & DUNDI_FLAG_NOCOMUNSOLICIT) {
++ strncat(buf, "NOCOMUNSLTD|", bufsiz - strlen(buf) - 1);
++ }
++ /* Get rid of trailing | */
++ if (ast_strlen_zero(buf))
++ strcpy(buf, "NONE|");
++ buf[strlen(buf)-1] = '\0';
++ return buf;
++}
++
++static void dump_answer(char *output, int maxlen, void *value, int len)
++{
++ struct dundi_answer *answer;
++ char proto[40];
++ char flags[40];
++ char eid_str[40];
++ char tmp[512]="";
++ if (len >= 10) {
++ answer = (struct dundi_answer *)(value);
++ memcpy(tmp, answer->data, (len >= 500) ? 500 : len - 10);
++ dundi_eid_to_str(eid_str, sizeof(eid_str), &answer->eid);
++ snprintf(output, maxlen, "[%s] %d <%s/%s> from [%s]",
++ dundi_flags2str(flags, sizeof(flags), ntohs(answer->flags)),
++ ntohs(answer->weight),
++ proto2str(answer->protocol, proto, sizeof(proto)),
++ tmp, eid_str);
++ } else
++ strncpy(output, "Invalid Answer", maxlen - 1);
++}
++
++static void dump_encrypted(char *output, int maxlen, void *value, int len)
++{
++ char iv[33];
++ int x;
++ if ((len > 16) && !(len % 16)) {
++ /* Build up IV */
++ for (x=0;x<16;x++) {
++ snprintf(iv + (x << 1), 3, "%02x", ((unsigned char *)value)[x]);
++ }
++ snprintf(output, maxlen, "[IV %s] %d encrypted blocks\n", iv, len / 16);
++ } else
++ snprintf(output, maxlen, "Invalid Encrypted Datalen %d", len);
++}
++
++static void dump_raw(char *output, int maxlen, void *value, int len)
++{
++ int x;
++ unsigned char *u = value;
++ output[maxlen - 1] = '\0';
++ strcpy(output, "[ ");
++ for (x=0;x<len;x++) {
++ snprintf(output + strlen(output), maxlen - strlen(output) - 1, "%02x ", u[x]);
++ }
++ strncat(output + strlen(output), "]", maxlen - strlen(output) - 1);
++}
++
++static struct dundi_ie {
++ int ie;
++ char *name;
++ void (*dump)(char *output, int maxlen, void *value, int len);
++} ies[] = {
++ { DUNDI_IE_EID, "ENTITY IDENT", dump_eid },
++ { DUNDI_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string },
++ { DUNDI_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string },
++ { DUNDI_IE_EID_DIRECT, "DIRECT EID", dump_eid },
++ { DUNDI_IE_ANSWER, "ANSWER", dump_answer },
++ { DUNDI_IE_TTL, "TTL", dump_short },
++ { DUNDI_IE_VERSION, "VERSION", dump_short },
++ { DUNDI_IE_EXPIRATION, "EXPIRATION", dump_short },
++ { DUNDI_IE_UNKNOWN, "UKWN DUNDI CMD", dump_byte },
++ { DUNDI_IE_CAUSE, "CAUSE", dump_cause },
++ { DUNDI_IE_REQEID, "REQUEST EID", dump_eid },
++ { DUNDI_IE_ENCDATA, "ENCDATA", dump_encrypted },
++ { DUNDI_IE_SHAREDKEY, "SHAREDKEY", dump_raw },
++ { DUNDI_IE_SIGNATURE, "SIGNATURE", dump_raw },
++ { DUNDI_IE_KEYCRC32, "KEYCRC32", dump_int },
++ { DUNDI_IE_HINT, "HINT", dump_hint },
++ { DUNDI_IE_DEPARTMENT, "DEPARTMENT", dump_string },
++ { DUNDI_IE_ORGANIZATION, "ORGANIZTN", dump_string },
++ { DUNDI_IE_LOCALITY, "LOCALITY", dump_string },
++ { DUNDI_IE_STATE_PROV, "STATEPROV", dump_string },
++ { DUNDI_IE_COUNTRY, "COUNTRY", dump_string },
++ { DUNDI_IE_EMAIL, "EMAIL", dump_string },
++ { DUNDI_IE_PHONE, "PHONE", dump_string },
++ { DUNDI_IE_IPADDR, "ADDRESS", dump_string },
++ { DUNDI_IE_CACHEBYPASS, "CBYPASS", dump_cbypass },
++};
++
++const char *dundi_ie2str(int ie)
++{
++ int x;
++ for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
++ if (ies[x].ie == ie)
++ return ies[x].name;
++ }
++ return "Unknown IE";
++}
++
++static void dump_ies(unsigned char *iedata, int spaces, int len)
++{
++ int ielen;
++ int ie;
++ int x;
++ int found;
++ char interp[1024];
++ char tmp[1024];
++ if (len < 2)
++ return;
++ while(len >= 2) {
++ ie = iedata[0];
++ ielen = iedata[1];
++ /* Encrypted data is the remainder */
++ if (ie == DUNDI_IE_ENCDATA)
++ ielen = len - 2;
++ if (ielen + 2> len) {
++ snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len);
++ outputf(tmp);
++ return;
++ }
++ found = 0;
++ for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
++ if (ies[x].ie == ie) {
++ if (ies[x].dump) {
++ ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
++ snprintf(tmp, (int)sizeof(tmp), " %s%-15.15s : %s\n", (spaces ? " " : "" ), ies[x].name, interp);
++ outputf(tmp);
++ } else {
++ if (ielen)
++ snprintf(interp, (int)sizeof(interp), "%d bytes", ielen);
++ else
++ strcpy(interp, "Present");
++ snprintf(tmp, (int)sizeof(tmp), " %s%-15.15s : %s\n", (spaces ? " " : "" ), ies[x].name, interp);
++ outputf(tmp);
++ }
++ found++;
++ }
++ }
++ if (!found) {
++ snprintf(tmp, (int)sizeof(tmp), " %sUnknown IE %03d : Present\n", (spaces ? " " : "" ), ie);
++ outputf(tmp);
++ }
++ iedata += (2 + ielen);
++ len -= (2 + ielen);
++ }
++ outputf("\n");
++}
++
++void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen)
++{
++ char *pref[] = {
++ "Tx",
++ "Rx",
++ " ETx",
++ " Erx" };
++ char *commands[] = {
++ "ACK ",
++ "DPDISCOVER ",
++ "DPRESPONSE ",
++ "EIDQUERY ",
++ "EIDRESPONSE ",
++ "PRECACHERQ ",
++ "PRECACHERP ",
++ "INVALID ",
++ "UNKNOWN CMD ",
++ "NULL ",
++ "REQREQ ",
++ "REGRESPONSE ",
++ "CANCEL ",
++ "ENCRYPT ",
++ "ENCREJ " };
++ char class2[20];
++ char *class;
++ char subclass2[20];
++ char *subclass;
++ char tmp[256];
++ char retries[20];
++ char iabuf[INET_ADDRSTRLEN];
++ if (ntohs(fhi->dtrans) & DUNDI_FLAG_RETRANS)
++ strcpy(retries, "Yes");
++ else
++ strcpy(retries, "No");
++ if ((ntohs(fhi->strans) & DUNDI_FLAG_RESERVED)) {
++ /* Ignore frames with high bit set to 1 */
++ return;
++ }
++ if ((fhi->cmdresp & 0x3f) > (int)sizeof(commands)/(int)sizeof(char *)) {
++ snprintf(class2, (int)sizeof(class2), "(%d?)", fhi->cmdresp);
++ class = class2;
++ } else {
++ class = commands[(int)(fhi->cmdresp & 0x3f)];
++ }
++ snprintf(subclass2, (int)sizeof(subclass2), "%02x", fhi->cmdflags);
++ subclass = subclass2;
++ snprintf(tmp, (int)sizeof(tmp),
++ "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s (%s)\n",
++ pref[rx],
++ retries, fhi->oseqno, fhi->iseqno, class, fhi->cmdresp & 0x40 ? "Response" : "Command");
++ outputf(tmp);
++ snprintf(tmp, (int)sizeof(tmp),
++ "%s Flags: %s STrans: %5.5d DTrans: %5.5d [%s:%d]%s\n", (rx > 1) ? " " : "",
++ subclass, ntohs(fhi->strans) & ~DUNDI_FLAG_RESERVED, ntohs(fhi->dtrans) & ~DUNDI_FLAG_RETRANS,
++ ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port),
++ fhi->cmdresp & 0x80 ? " (Final)" : "");
++ outputf(tmp);
++ dump_ies(fhi->ies, rx > 1, datalen);
++}
++
++int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen)
++{
++ char tmp[256];
++ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
++ 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);
++ errorf(tmp);
++ return -1;
++ }
++ ied->buf[ied->pos++] = ie;
++ ied->buf[ied->pos++] = datalen;
++ memcpy(ied->buf + ied->pos, data, datalen);
++ ied->pos += datalen;
++ return 0;
++}
++
++int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, unsigned char *data)
++{
++ char tmp[256];
++ int datalen = data ? strlen(data) + 1 : 1;
++ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
++ 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);
++ errorf(tmp);
++ return -1;
++ }
++ ied->buf[ied->pos++] = ie;
++ ied->buf[ied->pos++] = datalen;
++ ied->buf[ied->pos++] = cause;
++ memcpy(ied->buf + ied->pos, data, datalen-1);
++ ied->pos += datalen-1;
++ return 0;
++}
++
++int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, unsigned char *data)
++{
++ char tmp[256];
++ int datalen = data ? strlen(data) + 2 : 2;
++ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
++ 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);
++ errorf(tmp);
++ return -1;
++ }
++ ied->buf[ied->pos++] = ie;
++ ied->buf[ied->pos++] = datalen;
++ flags = htons(flags);
++ memcpy(ied->buf + ied->pos, &flags, sizeof(flags));
++ ied->pos += 2;
++ memcpy(ied->buf + ied->pos, data, datalen-1);
++ ied->pos += datalen-2;
++ return 0;
++}
++
++int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen)
++{
++ char tmp[256];
++ datalen += 16;
++ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
++ 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);
++ errorf(tmp);
++ return -1;
++ }
++ ied->buf[ied->pos++] = ie;
++ ied->buf[ied->pos++] = datalen;
++ memcpy(ied->buf + ied->pos, iv, 16);
++ ied->pos += 16;
++ if (data) {
++ memcpy(ied->buf + ied->pos, data, datalen-16);
++ ied->pos += datalen-16;
++ }
++ return 0;
++}
++
++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)
++{
++ char tmp[256];
++ int datalen = data ? strlen(data) + 11 : 11;
++ int x;
++ unsigned short myw;
++ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
++ 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);
++ errorf(tmp);
++ return -1;
++ }
++ ied->buf[ied->pos++] = ie;
++ ied->buf[ied->pos++] = datalen;
++ for (x=0;x<6;x++)
++ ied->buf[ied->pos++] = eid->eid[x];
++ ied->buf[ied->pos++] = protocol;
++ myw = htons(flags);
++ memcpy(ied->buf + ied->pos, &myw, 2);
++ ied->pos += 2;
++ myw = htons(weight);
++ memcpy(ied->buf + ied->pos, &myw, 2);
++ ied->pos += 2;
++ memcpy(ied->buf + ied->pos, data, datalen-11);
++ ied->pos += datalen-11;
++ return 0;
++}
++
++int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin)
++{
++ return dundi_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in));
++}
++
++int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value)
++{
++ unsigned int newval;
++ newval = htonl(value);
++ return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
++}
++
++int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value)
++{
++ unsigned short newval;
++ newval = htons(value);
++ return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
++}
++
++int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, unsigned char *str)
++{
++ return dundi_ie_append_raw(ied, ie, str, strlen(str));
++}
++
++int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid)
++{
++ return dundi_ie_append_raw(ied, ie, (unsigned char *)eid, sizeof(dundi_eid));
++}
++
++int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat)
++{
++ return dundi_ie_append_raw(ied, ie, &dat, 1);
++}
++
++int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie)
++{
++ return dundi_ie_append_raw(ied, ie, NULL, 0);
++}
++
++void dundi_set_output(void (*func)(const char *))
++{
++ outputf = func;
++}
++
++void dundi_set_error(void (*func)(const char *))
++{
++ errorf = func;
++}
++
++int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen)
++{
++ /* Parse data into information elements */
++ int len;
++ int ie;
++ char tmp[256];
++ memset(ies, 0, (int)sizeof(struct dundi_ies));
++ ies->ttl = -1;
++ ies->expiration = -1;
++ ies->unknowncmd = -1;
++ ies->cause = -1;
++ while(datalen >= 2) {
++ ie = data[0];
++ len = data[1];
++ if (len > datalen - 2) {
++ errorf("Information element length exceeds message size\n");
++ return -1;
++ }
++ switch(ie) {
++ case DUNDI_IE_EID:
++ case DUNDI_IE_EID_DIRECT:
++ if (len != (int)sizeof(dundi_eid)) {
++ errorf("Improper entity identifer, expecting 6 bytes!\n");
++ } else if (ies->eidcount < DUNDI_MAX_STACK) {
++ ies->eids[ies->eidcount] = (dundi_eid *)(data + 2);
++ ies->eid_direct[ies->eidcount] = (ie == DUNDI_IE_EID_DIRECT);
++ ies->eidcount++;
++ } else
++ errorf("Too many entities in stack!\n");
++ break;
++ case DUNDI_IE_REQEID:
++ if (len != (int)sizeof(dundi_eid)) {
++ errorf("Improper requested entity identifer, expecting 6 bytes!\n");
++ } else
++ ies->reqeid = (dundi_eid *)(data + 2);
++ break;
++ case DUNDI_IE_CALLED_CONTEXT:
++ ies->called_context = data + 2;
++ break;
++ case DUNDI_IE_CALLED_NUMBER:
++ ies->called_number = data + 2;
++ break;
++ case DUNDI_IE_ANSWER:
++ if (len < sizeof(struct dundi_answer)) {
++ snprintf(tmp, (int)sizeof(tmp), "Answer expected to be >=%d bytes long but was %d\n", (int)sizeof(struct dundi_answer), len);
++ errorf(tmp);
++ } else {
++ if (ies->anscount < DUNDI_MAX_ANSWERS)
++ ies->answers[ies->anscount++]= (struct dundi_answer *)(data + 2);
++ else
++ errorf("Ignoring extra answers!\n");
++ }
++ break;
++ case DUNDI_IE_TTL:
++ if (len != (int)sizeof(unsigned short)) {
++ snprintf(tmp, (int)sizeof(tmp), "Expecting ttl to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
++ errorf(tmp);
++ } else
++ ies->ttl = ntohs(*((unsigned short *)(data + 2)));
++ break;
++ case DUNDI_IE_VERSION:
++ if (len != (int)sizeof(unsigned short)) {
++ snprintf(tmp, (int)sizeof(tmp), "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
++ errorf(tmp);
++ } else
++ ies->version = ntohs(*((unsigned short *)(data + 2)));
++ break;
++ case DUNDI_IE_EXPIRATION:
++ if (len != (int)sizeof(unsigned short)) {
++ snprintf(tmp, (int)sizeof(tmp), "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
++ errorf(tmp);
++ } else
++ ies->expiration = ntohs(*((unsigned short *)(data + 2)));
++ break;
++ case DUNDI_IE_KEYCRC32:
++ if (len != (int)sizeof(unsigned int)) {
++ snprintf(tmp, (int)sizeof(tmp), "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
++ errorf(tmp);
++ } else
++ ies->keycrc32 = ntohl(*((unsigned int *)(data + 2)));
++ break;
++ case DUNDI_IE_UNKNOWN:
++ if (len == 1)
++ ies->unknowncmd = data[2];
++ else {
++ snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len);
++ errorf(tmp);
++ }
++ break;
++ case DUNDI_IE_CAUSE:
++ if (len >= 1) {
++ ies->cause = data[2];
++ ies->causestr = data + 3;
++ } else {
++ snprintf(tmp, (int)sizeof(tmp), "Expected at least one byte cause, but was %d long\n", len);
++ errorf(tmp);
++ }
++ break;
++ case DUNDI_IE_HINT:
++ if (len >= 2) {
++ ies->hint = (struct dundi_hint *)(data + 2);
++ } else {
++ snprintf(tmp, (int)sizeof(tmp), "Expected at least two byte hint, but was %d long\n", len);
++ errorf(tmp);
++ }
++ break;
++ case DUNDI_IE_DEPARTMENT:
++ ies->q_dept = data + 2;
++ break;
++ case DUNDI_IE_ORGANIZATION:
++ ies->q_org = data + 2;
++ break;
++ case DUNDI_IE_LOCALITY:
++ ies->q_locality = data + 2;
++ break;
++ case DUNDI_IE_STATE_PROV:
++ ies->q_stateprov = data + 2;
++ break;
++ case DUNDI_IE_COUNTRY:
++ ies->q_country = data + 2;
++ break;
++ case DUNDI_IE_EMAIL:
++ ies->q_email = data + 2;
++ break;
++ case DUNDI_IE_PHONE:
++ ies->q_phone = data + 2;
++ break;
++ case DUNDI_IE_IPADDR:
++ ies->q_ipaddr = data + 2;
++ break;
++ case DUNDI_IE_ENCDATA:
++ /* Recalculate len as the remainder of the message, regardless of
++ theoretical length */
++ len = datalen - 2;
++ if ((len > 16) && !(len % 16)) {
++ ies->encblock = (struct dundi_encblock *)(data + 2);
++ ies->enclen = len - 16;
++ } else {
++ snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted data length %d\n", len);
++ errorf(tmp);
++ }
++ break;
++ case DUNDI_IE_SHAREDKEY:
++ if (len == 128) {
++ ies->encsharedkey = (unsigned char *)(data + 2);
++ } else {
++ snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted shared key length %d\n", len);
++ errorf(tmp);
++ }
++ break;
++ case DUNDI_IE_SIGNATURE:
++ if (len == 128) {
++ ies->encsig = (unsigned char *)(data + 2);
++ } else {
++ snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted signature length %d\n", len);
++ errorf(tmp);
++ }
++ break;
++ case DUNDI_IE_CACHEBYPASS:
++ ies->cbypass = 1;
++ break;
++ default:
++ snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", dundi_ie2str(ie), ie, len);
++ outputf(tmp);
++ }
++ /* Overwrite information element with 0, to null terminate previous portion */
++ data[0] = 0;
++ datalen -= (len + 2);
++ data += (len + 2);
++ }
++ /* Null-terminate last field */
++ *data = '\0';
++ if (datalen) {
++ errorf("Invalid information element contents, strange boundary\n");
++ return -1;
++ }
++ return 0;
++}
+diff -ruN asterisk-1.0.7-orig/pbx/dundi-parser.h asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.h
+--- asterisk-1.0.7-orig/pbx/dundi-parser.h 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.h 2005-06-02 20:21:37.000000000 +0200
+@@ -0,0 +1,88 @@
++/*
++ * Distributed Universal Number Discovery (DUNDi)
++ *
++ * Copyright (C) 2004, Digium Inc.
++ *
++ * Written by Mark Spencer <markster@digium.com>
++ *
++ * This program is Free Software distributed under the terms of
++ * of the GNU General Public License.
++ */
++
++#ifndef _DUNDI_PARSER_H
++#define _DUNDI_PARSER_H
++
++#include <asterisk/dundi.h>
++#include <asterisk/aes.h>
++
++#define DUNDI_MAX_STACK 512
++#define DUNDI_MAX_ANSWERS 100
++
++struct dundi_ies {
++ dundi_eid *eids[DUNDI_MAX_STACK + 1];
++ int eid_direct[DUNDI_MAX_STACK + 1];
++ dundi_eid *reqeid;
++ int eidcount;
++ char *called_context;
++ char *called_number;
++ struct dundi_answer *answers[DUNDI_MAX_ANSWERS + 1];
++ struct dundi_hint *hint;
++ int anscount;
++ int ttl;
++ int version;
++ int expiration;
++ int unknowncmd;
++ unsigned char *pubkey;
++ int cause;
++ unsigned char *q_dept;
++ unsigned char *q_org;
++ unsigned char *q_locality;
++ unsigned char *q_stateprov;
++ unsigned char *q_country;
++ unsigned char *q_email;
++ unsigned char *q_phone;
++ unsigned char *q_ipaddr;
++ unsigned char *causestr;
++ unsigned char *encsharedkey;
++ unsigned char *encsig;
++ unsigned long keycrc32;
++ struct dundi_encblock *encblock;
++ int enclen;
++ int cbypass;
++};
++
++struct dundi_ie_data {
++ int pos;
++ unsigned char buf[8192];
++};
++
++/* Choose a different function for output */
++extern void dundi_set_output(void (*output)(const char *data));
++/* Choose a different function for errors */
++extern void dundi_set_error(void (*output)(const char *data));
++extern void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen);
++
++extern const char *dundi_ie2str(int ie);
++
++extern int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen);
++extern int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin);
++extern int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value);
++extern int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value);
++extern int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, unsigned char *str);
++extern int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid);
++extern int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, unsigned char *desc);
++extern int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, unsigned char *data);
++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);
++extern int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen);
++extern int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat);
++extern int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie);
++extern int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen);
++extern char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid);
++extern char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid);
++extern int dundi_str_to_eid(dundi_eid *eid, char *s);
++extern int dundi_str_short_to_eid(dundi_eid *eid, char *s);
++extern int dundi_eid_zero(dundi_eid *eid);
++extern int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2);
++extern char *dundi_flags2str(char *s, int maxlen, int flags);
++extern char *dundi_hint2str(char *s, int maxlen, int flags);
++#endif
+diff -ruN asterisk-1.0.7-orig/pbx/pbx_dundi.c asterisk-1.0.7-pbx_dundi/pbx/pbx_dundi.c
+--- asterisk-1.0.7-orig/pbx/pbx_dundi.c 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.0.7-pbx_dundi/pbx/pbx_dundi.c 2005-06-02 20:21:37.000000000 +0200
+@@ -0,0 +1,4881 @@
++/*
++ * Distributed Universal Number Discovery (DUNDi)
++ *
++ * Copyright (C) 2004, Digium Inc.
++ *
++ * Written by Mark Spencer <markster@digium.com>
++ *
++ * This program is Free Software distributed under the terms of
++ * of the GNU General Public License.
++ */
++
++#include <asterisk/file.h>
++#include <asterisk/logger.h>
++#include <asterisk/channel.h>
++#include <asterisk/config.h>
++#include <asterisk/options.h>
++#include <asterisk/pbx.h>
++#include <asterisk/module.h>
++#include <asterisk/frame.h>
++#include <asterisk/file.h>
++#include <asterisk/channel_pvt.h>
++#include <asterisk/cli.h>
++#include <asterisk/lock.h>
++#include <asterisk/md5.h>
++#include <asterisk/dundi.h>
++#include <asterisk/sched.h>
++#include <asterisk/io.h>
++#include <asterisk/utils.h>
++#include <asterisk/crypto.h>
++#include <asterisk/astdb.h>
++#include <asterisk/acl.h>
++#include <asterisk/aes.h>
++
++#include "dundi-parser.h"
++#include <stdlib.h>
++#include <unistd.h>
++#include <arpa/inet.h>
++#include <netinet/in.h>
++#include <sys/socket.h>
++#include <string.h>
++#include <errno.h>
++#if defined(__FreeBSD__) || defined(__NetBSD__)
++#include <sys/types.h>
++#include <netinet/in_systm.h>
++#endif
++#include <netinet/ip.h>
++#include <sys/ioctl.h>
++#include <netinet/in.h>
++#include <net/if.h>
++#if defined(__FreeBSD__) || defined(__NetBSD__)
++#include <net/if_dl.h>
++#include <ifaddrs.h>
++#endif
++#include <zlib.h>
++
++#define MAX_RESULTS 64
++
++#define MAX_PACKET_SIZE 8192
++
++extern char ast_config_AST_KEY_DIR[];
++
++static char *tdesc = "Distributed Universal Number Discovery (DUNDi)";
++
++static char *app = "DUNDiLookup";
++static char *synopsis = "Look up a number with DUNDi";
++static char *descrip =
++"DUNDiLookup(number[|context[|options]])\n"
++" Looks up a given number in the global context specified or in\n"
++"the reserved 'e164' context if not specified. Returns -1 if the channel\n"
++"is hungup during the lookup or 0 otherwise. On completion, the variable\n"
++"${DUNDTECH} and ${DUNDDEST} will contain the technology and destination\n"
++"of the appropriate technology and destination to access the number. If no\n"
++"answer was found, and the priority n + 101 exists, execution will continue\n"
++"at that location.\n";
++
++#define DUNDI_MODEL_INBOUND (1 << 0)
++#define DUNDI_MODEL_OUTBOUND (1 << 1)
++#define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
++
++/* Keep times of last 10 lookups */
++#define DUNDI_TIMING_HISTORY 10
++
++#define FLAG_ISREG (1 << 0) /* Transaction is register request */
++#define FLAG_DEAD (1 << 1) /* Transaction is dead */
++#define FLAG_FINAL (1 << 2) /* Transaction has final message sent */
++#define FLAG_ISQUAL (1 << 3) /* Transaction is a qualification */
++#define FLAG_ENCRYPT (1 << 4) /* Transaction is encrypted wiht ECX/DCX */
++#define FLAG_SENDFULLKEY (1 << 5) /* Send full key on transaction */
++#define FLAG_STOREHIST (1 << 6) /* Record historic performance */
++
++#define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
++
++#if 0
++#define DUNDI_SECRET_TIME 15 /* Testing only */
++#else
++#define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
++#endif
++
++#define KEY_OUT 0
++#define KEY_IN 1
++
++static struct io_context *io;
++static struct sched_context *sched;
++static int netsocket = -1;
++static pthread_t netthreadid = AST_PTHREADT_NULL;
++static pthread_t precachethreadid = AST_PTHREADT_NULL;
++static int tos = 0;
++static int dundidebug = 0;
++static int authdebug = 0;
++static int dundi_ttl = DUNDI_DEFAULT_TTL;
++static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
++static int global_autokilltimeout = 0;
++static dundi_eid global_eid;
++static int default_expiration = 60;
++static int global_storehistory = 0;
++static int map_update_interval = 0;
++static int map_updates_per_pkt = 45;
++static int map_peering_sid = -1;
++static int map_contact_sid = -1;
++static char map_context[80];
++static struct sockaddr_in map_addr;
++static char dept[80];
++static char org[80];
++static char locality[80];
++static char stateprov[80];
++static char country[80];
++static char email[80];
++static char phone[80];
++static char secretpath[80];
++static char cursecret[80];
++static char ipaddr[80];
++static time_t rotatetime;
++static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
++struct permission {
++ struct permission *next;
++ int allow;
++ char name[0];
++};
++
++struct dundi_packet {
++ struct dundi_hdr *h;
++ struct dundi_packet *next;
++ int datalen;
++ struct dundi_transaction *parent;
++ int retransid;
++ int retrans;
++ unsigned char data[0];
++};
++
++struct dundi_hint_metadata {
++ unsigned short flags;
++ char exten[AST_MAX_EXTENSION];
++};
++
++struct dundi_precache_queue {
++ struct dundi_precache_queue *next;
++ char *context;
++ time_t expiration;
++ char number[0];
++};
++
++struct dundi_request;
++
++struct dundi_transaction {
++ struct sockaddr_in addr; /* Other end of transaction */
++ struct timeval start; /* When this transaction was created */
++ dundi_eid eids[DUNDI_MAX_STACK + 1];
++ int eidcount; /* Number of eids in eids */
++ dundi_eid us_eid; /* Our EID, to them */
++ dundi_eid them_eid; /* Their EID, to us */
++ aes_encrypt_ctx ecx; /* AES 128 Encryption context */
++ aes_decrypt_ctx dcx; /* AES 128 Decryption context */
++ int flags; /* Has final packet been sent */
++ int ttl; /* Remaining TTL for queries on this one */
++ int thread; /* We have a calling thread */
++ int retranstimer; /* How long to wait before retransmissions */
++ int autokillid; /* ID to kill connection if answer doesn't come back fast enough */
++ int autokilltimeout; /* Recommended timeout for autokill */
++ unsigned short strans; /* Our transaction identifier */
++ unsigned short dtrans; /* Their transaction identifer */
++ unsigned char iseqno; /* Next expected received seqno */
++ unsigned char oiseqno; /* Last received incoming seqno */
++ unsigned char oseqno; /* Next transmitted seqno */
++ unsigned char aseqno; /* Last acknowledge seqno */
++ struct dundi_packet *packets; /* Packets to be retransmitted */
++ struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
++ struct dundi_transaction *next; /* Next with respect to the parent */
++ struct dundi_request *parent; /* Parent request (if there is one) */
++ struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
++} *alltrans;
++
++struct dundi_request {
++ char dcontext[AST_MAX_EXTENSION];
++ char number[AST_MAX_EXTENSION];
++ dundi_eid query_eid;
++ dundi_eid root_eid;
++ struct dundi_result *dr;
++ struct dundi_entity_info *dei;
++ struct dundi_hint_metadata *hmd;
++ int maxcount;
++ int respcount;
++ int expiration;
++ int cbypass;
++ int pfds[2];
++ unsigned long crc32; /* CRC-32 of all but root EID's in avoid list */
++ struct dundi_transaction *trans; /* Transactions */
++ struct dundi_request *next;
++} *requests;
++
++static struct dundi_mapping {
++ char dcontext[AST_MAX_EXTENSION];
++ char lcontext[AST_MAX_EXTENSION];
++ int weight;
++ int options;
++ int tech;
++ int dead;
++ char dest[AST_MAX_EXTENSION];
++ struct dundi_mapping *next;
++} *mappings = NULL;
++
++static struct dundi_peer {
++ dundi_eid eid;
++ struct sockaddr_in addr; /* Address of DUNDi peer */
++ struct permission *permit;
++ struct permission *include;
++ struct permission *precachesend;
++ struct permission *precachereceive;
++ dundi_eid us_eid;
++ char inkey[80];
++ char outkey[80];
++ int dead;
++ int registerid;
++ int qualifyid;
++ int sentfullkey;
++ int order;
++ unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
++ unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
++ unsigned long us_keycrc32; /* CRC-32 of our key */
++ aes_encrypt_ctx us_ecx; /* Cached AES 128 Encryption context */
++ aes_decrypt_ctx us_dcx; /* Cached AES 128 Decryption context */
++ unsigned long them_keycrc32;/* CRC-32 of our key */
++ aes_encrypt_ctx them_ecx; /* Cached AES 128 Encryption context */
++ aes_decrypt_ctx them_dcx; /* Cached AES 128 Decryption context */
++ time_t keyexpire; /* When to expire/recreate key */
++ int registerexpire;
++ int lookuptimes[DUNDI_TIMING_HISTORY];
++ char *lookups[DUNDI_TIMING_HISTORY];
++ int avgms;
++ struct dundi_transaction *regtrans; /* Registration transaction */
++ struct dundi_transaction *qualtrans; /* Qualify transaction */
++ struct dundi_transaction *keypending;
++ int model; /* Pull model */
++ int pcmodel; /* Push/precache model */
++ int dynamic; /* Are we dynamic? */
++ int lastms; /* Last measured latency */
++ int maxms; /* Max permissible latency */
++ struct timeval qualtx; /* Time of transmit */
++ struct dundi_peer *next;
++} *peers;
++
++static struct dundi_precache_queue *pcq;
++
++AST_MUTEX_DEFINE_STATIC(peerlock);
++AST_MUTEX_DEFINE_STATIC(pclock);
++
++static int dundi_xmit(struct dundi_packet *pack);
++
++static void dundi_debug_output(const char *data)
++{
++ if (dundidebug)
++ ast_verbose("%s", data);
++}
++
++static void dundi_error_output(const char *data)
++{
++ ast_log(LOG_WARNING, "%s", data);
++}
++
++static int has_permission(struct permission *ps, char *cont)
++{
++ int res=0;
++ while(ps) {
++ if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
++ res = ps->allow;
++ ps = ps->next;
++ }
++ return res;
++}
++
++static char *tech2str(int tech)
++{
++ switch(tech) {
++ case DUNDI_PROTO_NONE:
++ return "None";
++ case DUNDI_PROTO_IAX:
++ return "IAX2";
++ case DUNDI_PROTO_SIP:
++ return "SIP";
++ case DUNDI_PROTO_H323:
++ return "H323";
++ default:
++ return "Unknown";
++ }
++}
++
++static int str2tech(char *str)
++{
++ if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
++ return DUNDI_PROTO_IAX;
++ else if (!strcasecmp(str, "SIP"))
++ return DUNDI_PROTO_SIP;
++ else if (!strcasecmp(str, "H323"))
++ return DUNDI_PROTO_H323;
++ else
++ return -1;
++}
++
++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[]);
++static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
++static struct dundi_transaction *create_transaction(struct dundi_peer *p);
++static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
++{
++ /* Look for an exact match first */
++ struct dundi_transaction *trans;
++ trans = alltrans;
++ while(trans) {
++ if (!inaddrcmp(&trans->addr, sin) &&
++ ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
++ ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
++ if (hdr->strans)
++ trans->dtrans = ntohs(hdr->strans) & 32767;
++ break;
++ }
++ trans = trans->allnext;
++ }
++ if (!trans) {
++ switch(hdr->cmdresp & 0x7f) {
++ case DUNDI_COMMAND_DPDISCOVER:
++ case DUNDI_COMMAND_EIDQUERY:
++ case DUNDI_COMMAND_PRECACHERQ:
++ case DUNDI_COMMAND_REGREQ:
++ case DUNDI_COMMAND_NULL:
++ case DUNDI_COMMAND_ENCRYPT:
++ if (hdr->strans) {
++ /* Create new transaction */
++ trans = create_transaction(NULL);
++ if (trans) {
++ memcpy(&trans->addr, sin, sizeof(trans->addr));
++ trans->dtrans = ntohs(hdr->strans) & 32767;
++ } else
++ ast_log(LOG_WARNING, "Out of memory!\n");
++ }
++ break;
++ default:
++ break;
++ }
++ }
++ return trans;
++}
++
++static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
++
++static int dundi_ack(struct dundi_transaction *trans, int final)
++{
++ return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
++}
++static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
++{
++ struct {
++ struct dundi_packet pack;
++ struct dundi_hdr hdr;
++ } tmp;
++ struct dundi_transaction trans;
++ /* Never respond to an INVALID with another INVALID */
++ if (h->cmdresp == DUNDI_COMMAND_INVALID)
++ return;
++ memset(&tmp, 0, sizeof(tmp));
++ memset(&trans, 0, sizeof(trans));
++ memcpy(&trans.addr, sin, sizeof(trans.addr));
++ tmp.hdr.strans = h->dtrans;
++ tmp.hdr.dtrans = h->strans;
++ tmp.hdr.iseqno = h->oseqno;
++ tmp.hdr.oseqno = h->iseqno;
++ tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
++ tmp.hdr.cmdflags = 0;
++ tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
++ tmp.pack.datalen = sizeof(struct dundi_hdr);
++ tmp.pack.parent = &trans;
++ dundi_xmit(&tmp.pack);
++}
++
++static void reset_global_eid(void)
++{
++#if defined(SIOCGIFHWADDR)
++ int x,s;
++ char eid_str[20];
++ struct ifreq ifr;
++
++ s = socket(AF_INET, SOCK_STREAM, 0);
++ if (s > 0) {
++ x = 0;
++ for(x=0;x<10;x++) {
++ memset(&ifr, 0, sizeof(ifr));
++ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
++ if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
++ memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
++ 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);
++ return;
++ }
++ }
++ }
++#else
++#if defined(ifa_broadaddr)
++ char eid_str[20];
++ struct ifaddrs *ifap;
++
++ if (getifaddrs(&ifap) == 0) {
++ struct ifaddrs *p;
++ for (p = ifap; p; p = p->ifa_next) {
++ if (p->ifa_addr->sa_family == AF_LINK) {
++ struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
++ memcpy(
++ &(global_eid.eid),
++ sdp->sdl_data + sdp->sdl_nlen, 6);
++ 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);
++ freeifaddrs(ifap);
++ return;
++ }
++ }
++ freeifaddrs(ifap);
++ }
++#endif
++#endif
++ ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
++}
++
++static int get_trans_id(void)
++{
++ struct dundi_transaction *t;
++ int stid = (rand() % 32766) + 1;
++ int tid = stid;
++ do {
++ t = alltrans;
++ while(t) {
++ if (t->strans == tid)
++ break;
++ t = t->allnext;
++ }
++ if (!t)
++ return tid;
++ tid = (tid % 32766) + 1;
++ } while (tid != stid);
++ return 0;
++}
++
++static int reset_transaction(struct dundi_transaction *trans)
++{
++ int tid;
++ tid = get_trans_id();
++ if (tid < 1)
++ return -1;
++ trans->strans = tid;
++ trans->dtrans = 0;
++ trans->iseqno = 0;
++ trans->oiseqno = 0;
++ trans->oseqno = 0;
++ trans->aseqno = 0;
++ trans->flags &= ~FLAG_FINAL;
++ return 0;
++}
++
++static struct dundi_peer *find_peer(dundi_eid *eid)
++{
++ struct dundi_peer *cur;
++ if (!eid)
++ eid = &empty_eid;
++ cur = peers;
++ while(cur) {
++ if (!dundi_eid_cmp(&cur->eid,eid))
++ return cur;
++ cur = cur->next;
++ }
++ return NULL;
++}
++
++static void build_iv(unsigned char *iv)
++{
++ /* XXX Would be nice to be more random XXX */
++ unsigned int *fluffy;
++ int x;
++ fluffy = (unsigned int *)(iv);
++ for (x=0;x<4;x++)
++ fluffy[x] = rand();
++}
++
++struct dundi_query_state {
++ dundi_eid *eids[DUNDI_MAX_STACK + 1];
++ int directs[DUNDI_MAX_STACK + 1];
++ dundi_eid reqeid;
++ char called_context[AST_MAX_EXTENSION];
++ char called_number[AST_MAX_EXTENSION];
++ struct dundi_mapping *maps;
++ int nummaps;
++ int nocache;
++ struct dundi_transaction *trans;
++ void *chal;
++ int challen;
++ int ttl;
++ char fluffy[0];
++};
++
++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)
++{
++ int flags;
++ int x;
++ if (!ast_strlen_zero(map->lcontext)) {
++ flags = 0;
++ if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
++ flags |= DUNDI_FLAG_EXISTS;
++ if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
++ flags |= DUNDI_FLAG_CANMATCH;
++ if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
++ flags |= DUNDI_FLAG_MATCHMORE;
++ if (ast_ignore_pattern(map->lcontext, called_number))
++ flags |= DUNDI_FLAG_IGNOREPAT;
++
++ /* Clearly we can't say 'don't ask' anymore if we found anything... */
++ if (flags)
++ hmd->flags &= ~DUNDI_HINT_DONT_ASK;
++
++ if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
++ /* Skip partial answers */
++ flags &= ~(DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
++ }
++ if (flags) {
++ struct varshead headp;
++ struct ast_var_t *newvariable;
++ flags |= map->options & 0xffff;
++ dr[anscnt].flags = flags;
++ dr[anscnt].techint = map->tech;
++ dr[anscnt].weight = map->weight;
++ dr[anscnt].expiration = DUNDI_DEFAULT_CACHE_TIME;
++ strncpy(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
++ dr[anscnt].eid = *us_eid;
++ dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
++ if (flags & DUNDI_FLAG_EXISTS) {
++ AST_LIST_HEAD_INIT(&headp);
++ newvariable = ast_var_assign("NUMBER", called_number);
++ AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
++ newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
++ AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
++ newvariable = ast_var_assign("SECRET", cursecret);
++ AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
++ newvariable = ast_var_assign("IPADDR", ipaddr);
++ AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
++ pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
++ while (!AST_LIST_EMPTY(&headp)) { /* List Deletion. */
++ newvariable = AST_LIST_FIRST(&headp);
++ AST_LIST_REMOVE_HEAD(&headp, entries);
++ ast_var_delete(newvariable);
++ }
++ } else
++ dr[anscnt].dest[0] = '\0';
++ anscnt++;
++ } else {
++ /* No answers... Find the fewest number of digits from the
++ number for which we have no answer. */
++ char tmp[AST_MAX_EXTENSION]="";
++ for (x=0;x<AST_MAX_EXTENSION;x++) {
++ tmp[x] = called_number[x];
++ if (!tmp[x])
++ break;
++ if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
++ /* Oops found something we can't match. If this is longer
++ than the running hint, we have to consider it */
++ if (strlen(tmp) > strlen(hmd->exten)) {
++ strncpy(hmd->exten, tmp, sizeof(hmd->exten) - 1);
++ }
++ break;
++ }
++ }
++ }
++ }
++ return anscnt;
++}
++
++static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
++
++static void *dundi_lookup_thread(void *data)
++{
++ struct dundi_query_state *st = data;
++ struct dundi_result dr[MAX_RESULTS];
++ struct dundi_ie_data ied;
++ struct dundi_hint_metadata hmd;
++ char eid_str[20];
++ int res, x;
++ int ouranswers=0;
++ int max = 999999;
++ int expiration = DUNDI_DEFAULT_CACHE_TIME;
++
++ ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
++ st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
++ memset(&ied, 0, sizeof(ied));
++ memset(&dr, 0, sizeof(dr));
++ memset(&hmd, 0, sizeof(hmd));
++ /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
++ hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
++ for (x=0;x<st->nummaps;x++)
++ ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
++ if (ouranswers < 0)
++ ouranswers = 0;
++ for (x=0;x<ouranswers;x++) {
++ if (dr[x].weight < max)
++ max = dr[x].weight;
++ }
++
++ if (max) {
++ /* If we do not have a canonical result, keep looking */
++ 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);
++ if (res > 0) {
++ /* Append answer in result */
++ ouranswers += res;
++ } else {
++ if ((res < -1) && (!ouranswers))
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
++ }
++ }
++ ast_mutex_lock(&peerlock);
++ /* Truncate if "don't ask" isn't present */
++ if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
++ hmd.exten[0] = '\0';
++ if (st->trans->flags & FLAG_DEAD) {
++ ast_log(LOG_DEBUG, "Our transaction went away!\n");
++ st->trans->thread = 0;
++ destroy_trans(st->trans, 0);
++ } else {
++ for (x=0;x<ouranswers;x++) {
++ /* Add answers */
++ if (dr[x].expiration && (expiration > dr[x].expiration))
++ expiration = dr[x].expiration;
++ dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
++ }
++ dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
++ dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
++ dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
++ st->trans->thread = 0;
++ }
++ ast_mutex_unlock(&peerlock);
++ free(st);
++ return NULL;
++}
++
++static void *dundi_precache_thread(void *data)
++{
++ struct dundi_query_state *st = data;
++ struct dundi_ie_data ied;
++ struct dundi_hint_metadata hmd;
++ char eid_str[20];
++
++ ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
++ st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
++ memset(&ied, 0, sizeof(ied));
++
++ /* Now produce precache */
++ dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
++
++ ast_mutex_lock(&peerlock);
++ /* Truncate if "don't ask" isn't present */
++ if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
++ hmd.exten[0] = '\0';
++ if (st->trans->flags & FLAG_DEAD) {
++ ast_log(LOG_DEBUG, "Our transaction went away!\n");
++ st->trans->thread = 0;
++ destroy_trans(st->trans, 0);
++ } else {
++ dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
++ st->trans->thread = 0;
++ }
++ ast_mutex_unlock(&peerlock);
++ free(st);
++ return NULL;
++}
++
++static inline int calc_ms(struct timeval *start)
++{
++ struct timeval tv;
++ gettimeofday(&tv, NULL);
++ return ((tv.tv_sec - start->tv_sec) * 1000 + (tv.tv_usec - start->tv_usec) / 1000);
++}
++
++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[]);
++
++static void *dundi_query_thread(void *data)
++{
++ struct dundi_query_state *st = data;
++ struct dundi_entity_info dei;
++ struct dundi_ie_data ied;
++ struct dundi_hint_metadata hmd;
++ char eid_str[20];
++ int res;
++ ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
++ st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
++ memset(&ied, 0, sizeof(ied));
++ memset(&dei, 0, sizeof(dei));
++ memset(&hmd, 0, sizeof(hmd));
++ if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
++ /* Ooh, it's us! */
++ ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
++ strncpy(dei.orgunit, dept, sizeof(dei.orgunit));
++ strncpy(dei.org, org, sizeof(dei.org));
++ strncpy(dei.locality, locality, sizeof(dei.locality));
++ strncpy(dei.stateprov, stateprov, sizeof(dei.stateprov));
++ strncpy(dei.country, country, sizeof(dei.country));
++ strncpy(dei.email, email, sizeof(dei.email));
++ strncpy(dei.phone, phone, sizeof(dei.phone));
++ res = 1;
++ } else {
++ /* If we do not have a canonical result, keep looking */
++ res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
++ }
++ ast_mutex_lock(&peerlock);
++ if (st->trans->flags & FLAG_DEAD) {
++ ast_log(LOG_DEBUG, "Our transaction went away!\n");
++ st->trans->thread = 0;
++ destroy_trans(st->trans, 0);
++ } else {
++ if (res) {
++ dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
++ dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
++ dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
++ dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
++ dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
++ dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
++ dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
++ if (!ast_strlen_zero(dei.ipaddr))
++ dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
++ }
++ dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
++ dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
++ st->trans->thread = 0;
++ }
++ ast_mutex_unlock(&peerlock);
++ free(st);
++ return NULL;
++}
++
++static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
++{
++ struct dundi_query_state *st;
++ int totallen;
++ int x;
++ int skipfirst=0;
++ struct dundi_ie_data ied;
++ char eid_str[20];
++ char *s;
++ pthread_t lookupthread;
++ pthread_attr_t attr;
++ if (ies->eidcount > 1) {
++ /* Since it is a requirement that the first EID is the authenticating host
++ and the last EID is the root, it is permissible that the first and last EID
++ could be the same. In that case, we should go ahead copy only the "root" section
++ since we will not need it for authentication. */
++ if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
++ skipfirst = 1;
++ }
++ totallen = sizeof(struct dundi_query_state);
++ totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
++ st = malloc(totallen);
++ if (st) {
++ memset(st, 0, totallen);
++ strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
++ memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
++ st->trans = trans;
++ st->ttl = ies->ttl - 1;
++ if (st->ttl < 0)
++ st->ttl = 0;
++ s = st->fluffy;
++ for (x=skipfirst;ies->eids[x];x++) {
++ st->eids[x-skipfirst] = (dundi_eid *)s;
++ *st->eids[x-skipfirst] = *ies->eids[x];
++ s += sizeof(dundi_eid);
++ }
++ 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);
++ pthread_attr_init(&attr);
++ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
++ trans->thread = 1;
++ if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
++ trans->thread = 0;
++ ast_log(LOG_WARNING, "Unable to create thread!\n");
++ free(st);
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
++ dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
++ return -1;
++ }
++ } else {
++ ast_log(LOG_WARNING, "Out of memory!\n");
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
++ dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
++ return -1;
++ }
++ return 0;
++}
++
++static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
++{
++ int unaffected;
++ char key1[256];
++ char key2[256];
++ char eidpeer_str[20];
++ char eidroot_str[20];
++ char data[80]="";
++ time_t timeout;
++
++ if (expiration < 0)
++ expiration = DUNDI_DEFAULT_CACHE_TIME;
++
++ /* Only cache hint if "don't ask" is there... */
++ if (!(ntohs(hint->flags)& DUNDI_HINT_DONT_ASK))
++ return 0;
++
++ unaffected = ntohs(hint->flags) & DUNDI_HINT_UNAFFECTED;
++
++ dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
++ dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
++ snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
++ snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
++
++ time(&timeout);
++ timeout += expiration;
++ snprintf(data, sizeof(data), "%ld|", (long)(timeout));
++
++ ast_db_put("dundi/cache", key1, data);
++ ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
++ ast_db_put("dundi/cache", key2, data);
++ ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
++ return 0;
++}
++
++static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
++{
++ int x;
++ char key1[256];
++ char key2[256];
++ char data[1024]="";
++ char eidpeer_str[20];
++ char eidroot_str[20];
++ time_t timeout;
++
++ if (expiration < 1)
++ expiration = DUNDI_DEFAULT_CACHE_TIME;
++
++ /* Keep pushes a little longer, cut pulls a little short */
++ if (push)
++ expiration += 10;
++ else
++ expiration -= 10;
++ if (expiration < 1)
++ expiration = 1;
++ dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
++ dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
++ snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
++ snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
++ /* Build request string */
++ time(&timeout);
++ timeout += expiration;
++ snprintf(data, sizeof(data), "%ld|", (long)(timeout));
++ for (x=start;x<req->respcount;x++) {
++ /* Skip anything with an illegal pipe in it */
++ if (strchr(req->dr[x].dest, '|'))
++ continue;
++ snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
++ req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
++ dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
++ }
++ ast_db_put("dundi/cache", key1, data);
++ ast_db_put("dundi/cache", key2, data);
++ return 0;
++}
++
++static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
++{
++ struct dundi_query_state *st;
++ int totallen;
++ int x,z;
++ struct dundi_ie_data ied;
++ char *s;
++ struct dundi_result dr2[MAX_RESULTS];
++ struct dundi_request dr;
++ struct dundi_hint_metadata hmd;
++
++ struct dundi_mapping *cur;
++ int mapcount;
++ int skipfirst = 0;
++
++ pthread_t lookupthread;
++ pthread_attr_t attr;
++
++ memset(&dr2, 0, sizeof(dr2));
++ memset(&dr, 0, sizeof(dr));
++ memset(&hmd, 0, sizeof(hmd));
++
++ /* Forge request structure to hold answers for cache */
++ hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
++ dr.dr = dr2;
++ dr.maxcount = MAX_RESULTS;
++ dr.expiration = DUNDI_DEFAULT_CACHE_TIME;
++ dr.hmd = &hmd;
++ dr.pfds[0] = dr.pfds[1] = -1;
++ trans->parent = &dr;
++ strncpy(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
++ strncpy(dr.number, ies->called_number, sizeof(dr.number) - 1);
++
++ for (x=0;x<ies->anscount;x++) {
++ if (trans->parent->respcount < trans->parent->maxcount) {
++ /* Make sure it's not already there */
++ for (z=0;z<trans->parent->respcount;z++) {
++ if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
++ !strcmp(trans->parent->dr[z].dest, ies->answers[x]->data))
++ break;
++ }
++ if (z == trans->parent->respcount) {
++ /* Copy into parent responses */
++ trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
++ trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
++ trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
++ trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
++ if (ies->expiration > 0)
++ trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
++ else
++ trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
++ dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
++ sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
++ &ies->answers[x]->eid);
++ strncpy(trans->parent->dr[trans->parent->respcount].dest, ies->answers[x]->data,
++ sizeof(trans->parent->dr[trans->parent->respcount].dest));
++ strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
++ sizeof(trans->parent->dr[trans->parent->respcount].tech));
++ trans->parent->respcount++;
++ trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
++ } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
++ /* Update weight if appropriate */
++ trans->parent->dr[z].weight = ies->answers[x]->weight;
++ }
++ } else
++ ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
++ trans->parent->number, trans->parent->dcontext);
++
++ }
++ /* Save all the results (if any) we had. Even if no results, still cache lookup. */
++ cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
++ if (ies->hint)
++ cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
++
++ totallen = sizeof(struct dundi_query_state);
++ /* Count matching map entries */
++ mapcount = 0;
++ cur = mappings;
++ while(cur) {
++ if (!strcasecmp(cur->dcontext, ccontext))
++ mapcount++;
++ cur = cur->next;
++ }
++
++ /* If no maps, return -1 immediately */
++ if (!mapcount)
++ return -1;
++
++ if (ies->eidcount > 1) {
++ /* Since it is a requirement that the first EID is the authenticating host
++ and the last EID is the root, it is permissible that the first and last EID
++ could be the same. In that case, we should go ahead copy only the "root" section
++ since we will not need it for authentication. */
++ if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
++ skipfirst = 1;
++ }
++
++ /* Prepare to run a query and then propagate that as necessary */
++ totallen += mapcount * sizeof(struct dundi_mapping);
++ totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
++ st = malloc(totallen);
++ if (st) {
++ memset(st, 0, totallen);
++ strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
++ strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
++ st->trans = trans;
++ st->ttl = ies->ttl - 1;
++ st->nocache = ies->cbypass;
++ if (st->ttl < 0)
++ st->ttl = 0;
++ s = st->fluffy;
++ for (x=skipfirst;ies->eids[x];x++) {
++ st->eids[x-skipfirst] = (dundi_eid *)s;
++ *st->eids[x-skipfirst] = *ies->eids[x];
++ st->directs[x-skipfirst] = ies->eid_direct[x];
++ s += sizeof(dundi_eid);
++ }
++ /* Append mappings */
++ x = 0;
++ st->maps = (struct dundi_mapping *)s;
++ cur = mappings;
++ while(cur) {
++ if (!strcasecmp(cur->dcontext, ccontext)) {
++ if (x < mapcount) {
++ st->maps[x] = *cur;
++ st->maps[x].next = NULL;
++ x++;
++ }
++ }
++ cur = cur->next;
++ }
++ st->nummaps = mapcount;
++ ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
++ pthread_attr_init(&attr);
++ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
++ trans->thread = 1;
++ if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
++ trans->thread = 0;
++ ast_log(LOG_WARNING, "Unable to create thread!\n");
++ free(st);
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
++ dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
++ return -1;
++ }
++ } else {
++ ast_log(LOG_WARNING, "Out of memory!\n");
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
++ dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
++ return -1;
++ }
++ return 0;
++}
++
++static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
++{
++ struct dundi_query_state *st;
++ int totallen;
++ int x;
++ struct dundi_ie_data ied;
++ char *s;
++ struct dundi_mapping *cur;
++ int mapcount;
++ int skipfirst = 0;
++
++ pthread_t lookupthread;
++ pthread_attr_t attr;
++ totallen = sizeof(struct dundi_query_state);
++ /* Count matching map entries */
++ mapcount = 0;
++ cur = mappings;
++ while(cur) {
++ if (!strcasecmp(cur->dcontext, ccontext))
++ mapcount++;
++ cur = cur->next;
++ }
++ /* If no maps, return -1 immediately */
++ if (!mapcount)
++ return -1;
++
++ if (ies->eidcount > 1) {
++ /* Since it is a requirement that the first EID is the authenticating host
++ and the last EID is the root, it is permissible that the first and last EID
++ could be the same. In that case, we should go ahead copy only the "root" section
++ since we will not need it for authentication. */
++ if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
++ skipfirst = 1;
++ }
++
++ totallen += mapcount * sizeof(struct dundi_mapping);
++ totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
++ st = malloc(totallen);
++ if (st) {
++ memset(st, 0, totallen);
++ strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
++ strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
++ st->trans = trans;
++ st->ttl = ies->ttl - 1;
++ st->nocache = ies->cbypass;
++ if (st->ttl < 0)
++ st->ttl = 0;
++ s = st->fluffy;
++ for (x=skipfirst;ies->eids[x];x++) {
++ st->eids[x-skipfirst] = (dundi_eid *)s;
++ *st->eids[x-skipfirst] = *ies->eids[x];
++ st->directs[x-skipfirst] = ies->eid_direct[x];
++ s += sizeof(dundi_eid);
++ }
++ /* Append mappings */
++ x = 0;
++ st->maps = (struct dundi_mapping *)s;
++ cur = mappings;
++ while(cur) {
++ if (!strcasecmp(cur->dcontext, ccontext)) {
++ if (x < mapcount) {
++ st->maps[x] = *cur;
++ st->maps[x].next = NULL;
++ x++;
++ }
++ }
++ cur = cur->next;
++ }
++ st->nummaps = mapcount;
++ ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
++ pthread_attr_init(&attr);
++ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
++ trans->thread = 1;
++ if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
++ trans->thread = 0;
++ ast_log(LOG_WARNING, "Unable to create thread!\n");
++ free(st);
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
++ dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
++ return -1;
++ }
++ } else {
++ ast_log(LOG_WARNING, "Out of memory!\n");
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
++ dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
++ return -1;
++ }
++ return 0;
++}
++
++static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
++{
++ char data[1024]="";
++ char *ptr, *term, *src;
++ int tech;
++ int flags;
++ int weight;
++ int length;
++ int z;
++ int expiration;
++ char fs[256];
++ time_t timeout;
++ /* Build request string */
++ if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
++ ptr = data;
++ if (sscanf(ptr, "%ld|%n", &timeout, &length) == 1) {
++ expiration = timeout - now;
++ if (expiration > 0) {
++ ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
++ ptr += length;
++ while((sscanf(ptr, "%d/%d/%d/%n", &flags, &weight, &tech, &length) == 3)) {
++ ptr += length;
++ term = strchr(ptr, '|');
++ if (term) {
++ *term = '\0';
++ src = strrchr(ptr, '/');
++ if (src) {
++ *src = '\0';
++ src++;
++ } else
++ src = "";
++ ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
++ tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags), eid_str_full);
++ /* Make sure it's not already there */
++ for (z=0;z<req->respcount;z++) {
++ if ((req->dr[z].techint == tech) &&
++ !strcmp(req->dr[z].dest, ptr))
++ break;
++ }
++ if (z == req->respcount) {
++ /* Copy into parent responses */
++ req->dr[req->respcount].flags = flags;
++ req->dr[req->respcount].weight = weight;
++ req->dr[req->respcount].techint = tech;
++ req->dr[req->respcount].expiration = expiration;
++ dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
++ dundi_eid_to_str(req->dr[req->respcount].eid_str,
++ sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
++ strncpy(req->dr[req->respcount].dest, ptr,
++ sizeof(req->dr[req->respcount].dest));
++ strncpy(req->dr[req->respcount].tech, tech2str(tech),
++ sizeof(req->dr[req->respcount].tech));
++ req->respcount++;
++ req->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
++ } else if (req->dr[z].weight > weight)
++ req->dr[z].weight = weight;
++ ptr = term + 1;
++ }
++ }
++ /* We found *something* cached */
++ if (expiration < *lowexpiration)
++ *lowexpiration = expiration;
++ return 1;
++ } else
++ ast_db_del("dundi/cache", key);
++ } else
++ ast_db_del("dundi/cache", key);
++ }
++
++ return 0;
++}
++
++static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
++{
++ char key[256];
++ char eid_str[20];
++ char eidroot_str[20];
++ time_t now;
++ int res=0;
++ int res2=0;
++ char eid_str_full[20];
++ char tmp[256]="";
++ int x;
++
++ time(&now);
++ dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
++ dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
++ dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
++ snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
++ res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
++ snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
++ res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
++ snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
++ res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
++ x = 0;
++ if (!req->respcount) {
++ while(!res2) {
++ /* Look and see if we have a hint that would preclude us from looking at this
++ peer for this number. */
++ if (!(tmp[x] = req->number[x]))
++ break;
++ x++;
++ /* Check for hints */
++ snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
++ res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
++ snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
++ res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
++ snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
++ res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
++ if (res2) {
++ if (strlen(tmp) > strlen(req->hmd->exten)) {
++ /* Update meta data if appropriate */
++ strncpy(req->hmd->exten, tmp, sizeof(req->hmd->exten) - 1);
++ }
++ }
++ }
++ res |= res2;
++ }
++
++ return res;
++}
++
++static void qualify_peer(struct dundi_peer *peer, int schedonly);
++
++static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
++{
++ if (!trans->addr.sin_addr.s_addr)
++ memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
++ trans->us_eid = p->us_eid;
++ trans->them_eid = p->eid;
++ /* Enable encryption if appropriate */
++ if (!ast_strlen_zero(p->inkey))
++ trans->flags |= FLAG_ENCRYPT;
++ if (p->maxms) {
++ trans->autokilltimeout = p->maxms;
++ trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
++ if (p->lastms > 1) {
++ trans->retranstimer = p->lastms * 2;
++ /* Keep it from being silly */
++ if (trans->retranstimer < 150)
++ trans->retranstimer = 150;
++ }
++ if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
++ trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
++ } else
++ trans->autokilltimeout = global_autokilltimeout;
++}
++
++static int do_register_expire(void *data)
++{
++ struct dundi_peer *peer = data;
++ char eid_str[20];
++ /* Called with peerlock already held */
++ ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ peer->registerexpire = -1;
++ peer->lastms = 0;
++ memset(&peer->addr, 0, sizeof(peer->addr));
++ return 0;
++}
++
++static int update_key(struct dundi_peer *peer)
++{
++ unsigned char key[16];
++ struct ast_key *ekey, *skey;
++ char eid_str[20];
++ int res;
++ if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
++ build_iv(key);
++ aes_encrypt_key128(key, &peer->us_ecx);
++ aes_decrypt_key128(key, &peer->us_dcx);
++ ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
++ if (!ekey) {
++ ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
++ peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ return -1;
++ }
++ skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
++ if (!skey) {
++ ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
++ peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ return -1;
++ }
++ if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
++ ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
++ return -1;
++ }
++ if ((res = ast_sign_bin(skey, peer->txenckey, 128, peer->txenckey + 128))) {
++ ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
++ return -1;
++ }
++ peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
++ peer->sentfullkey = 0;
++ /* Looks good */
++ time(&peer->keyexpire);
++ peer->keyexpire += dundi_key_ttl;
++ }
++ return 0;
++}
++
++static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
++{
++ unsigned char curblock[16];
++ int x;
++ memcpy(curblock, iv, sizeof(curblock));
++ while(len > 0) {
++ for (x=0;x<16;x++)
++ curblock[x] ^= src[x];
++ aes_encrypt(curblock, dst, ecx);
++ memcpy(curblock, dst, sizeof(curblock));
++ dst += 16;
++ src += 16;
++ len -= 16;
++ }
++ return 0;
++}
++static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
++{
++ unsigned char lastblock[16];
++ int x;
++ memcpy(lastblock, iv, sizeof(lastblock));
++ while(len > 0) {
++ aes_decrypt(src, dst, dcx);
++ for (x=0;x<16;x++)
++ dst[x] ^= lastblock[x];
++ memcpy(lastblock, src, sizeof(lastblock));
++ dst += 16;
++ src += 16;
++ len -= 16;
++ }
++ return 0;
++}
++
++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)
++{
++ int space = *dstlen;
++ unsigned long bytes;
++ struct dundi_hdr *h;
++ unsigned char *decrypt_space;
++ decrypt_space = alloca(srclen);
++ if (!decrypt_space)
++ return NULL;
++ decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
++ /* Setup header */
++ h = (struct dundi_hdr *)dst;
++ *h = *ohdr;
++ bytes = space - 6;
++ if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
++ ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
++ return NULL;
++ }
++ /* Update length */
++ *dstlen = bytes + 6;
++ /* Return new header */
++ return h;
++}
++
++static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
++{
++ unsigned char *compress_space;
++ int len;
++ int res;
++ unsigned long bytes;
++ struct dundi_ie_data ied;
++ struct dundi_peer *peer;
++ unsigned char iv[16];
++ len = pack->datalen + pack->datalen / 100 + 42;
++ compress_space = alloca(len);
++ if (compress_space) {
++ memset(compress_space, 0, len);
++ /* We care about everthing save the first 6 bytes of header */
++ bytes = len;
++ res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
++ if (res != Z_OK) {
++ ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
++ return -1;
++ }
++ memset(&ied, 0, sizeof(ied));
++ /* Say who we are */
++ if (!pack->h->iseqno && !pack->h->oseqno) {
++ /* Need the key in the first copy */
++ if (!(peer = find_peer(&trans->them_eid)))
++ return -1;
++ if (update_key(peer))
++ return -1;
++ if (!peer->sentfullkey)
++ trans->flags |= FLAG_SENDFULLKEY;
++ /* Append key data */
++ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
++ if (trans->flags & FLAG_SENDFULLKEY) {
++ dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
++ dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
++ } else {
++ dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
++ }
++ /* Setup contexts */
++ trans->ecx = peer->us_ecx;
++ trans->dcx = peer->us_dcx;
++
++ /* We've sent the full key */
++ peer->sentfullkey = 1;
++ }
++ /* Build initialization vector */
++ build_iv(iv);
++ /* Add the field, rounded up to 16 bytes */
++ dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
++ /* Copy the data */
++ if ((ied.pos + bytes) >= sizeof(ied.buf)) {
++ ast_log(LOG_NOTICE, "Final packet too large!\n");
++ return -1;
++ }
++ encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
++ ied.pos += ((bytes + 15) / 16) * 16;
++ /* Reconstruct header */
++ pack->datalen = sizeof(struct dundi_hdr);
++ pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
++ pack->h->cmdflags = 0;
++ memcpy(pack->h->ies, ied.buf, ied.pos);
++ pack->datalen += ied.pos;
++ return 0;
++ }
++ return -1;
++}
++
++static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
++{
++ unsigned char dst[128];
++ int res;
++ struct ast_key *key, *skey;
++ char eid_str[20];
++ if (option_debug)
++ ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
++ if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
++ /* A match */
++ return 1;
++ } else if (!newkey || !newsig)
++ return 0;
++ if (!memcmp(peer->rxenckey, newkey, 128) &&
++ !memcmp(peer->rxenckey + 128, newsig, 128)) {
++ /* By definition, a match */
++ return 1;
++ }
++ /* Decrypt key */
++ key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
++ if (!key) {
++ ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
++ peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ return -1;
++ }
++
++ skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
++ if (!skey) {
++ ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
++ peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ return -1;
++ }
++
++ /* First check signature */
++ res = ast_check_signature_bin(skey, newkey, 128, newsig);
++ if (res)
++ return 0;
++
++ res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
++ if (res != 16) {
++ if (res >= 0)
++ ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
++ return 0;
++ }
++ /* Decrypted, passes signature */
++ ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
++ memcpy(peer->rxenckey, newkey, 128);
++ memcpy(peer->rxenckey + 128, newsig, 128);
++ peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
++ aes_decrypt_key128(dst, &peer->them_dcx);
++ aes_encrypt_key128(dst, &peer->them_ecx);
++ return 1;
++}
++
++static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
++{
++ /* Handle canonical command / response */
++ int final = hdr->cmdresp & 0x80;
++ int cmd = hdr->cmdresp & 0x7f;
++ int x,y,z;
++ int resp;
++ int res;
++ int authpass=0;
++ unsigned char *bufcpy;
++ struct dundi_ie_data ied;
++ struct dundi_ies ies;
++ struct dundi_peer *peer;
++ char eid_str[20];
++ char eid_str2[20];
++ memset(&ied, 0, sizeof(ied));
++ memset(&ies, 0, sizeof(ies));
++ if (datalen) {
++ bufcpy = alloca(datalen);
++ if (!bufcpy)
++ return -1;
++ /* Make a copy for parsing */
++ memcpy(bufcpy, hdr->ies, datalen);
++ ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
++ if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
++ ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
++ return -1;
++ }
++ }
++ switch(cmd) {
++ case DUNDI_COMMAND_DPDISCOVER:
++ case DUNDI_COMMAND_EIDQUERY:
++ case DUNDI_COMMAND_PRECACHERQ:
++ if (cmd == DUNDI_COMMAND_EIDQUERY)
++ resp = DUNDI_COMMAND_EIDRESPONSE;
++ else if (cmd == DUNDI_COMMAND_PRECACHERQ)
++ resp = DUNDI_COMMAND_PRECACHERP;
++ else
++ resp = DUNDI_COMMAND_DPRESPONSE;
++ /* A dialplan or entity discover -- qualify by highest level entity */
++ peer = find_peer(ies.eids[0]);
++ if (!peer) {
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
++ dundi_send(trans, resp, 0, 1, &ied);
++ } else {
++ int hasauth = 0;
++ trans->us_eid = peer->us_eid;
++ if (strlen(peer->inkey)) {
++ hasauth = encrypted;
++ } else
++ hasauth = 1;
++ if (hasauth) {
++ /* Okay we're authentiated and all, now we check if they're authorized */
++ if (!ies.called_context)
++ ies.called_context = "e164";
++ if (cmd == DUNDI_COMMAND_EIDQUERY) {
++ res = dundi_answer_entity(trans, &ies, ies.called_context);
++ } else {
++ if (!ies.called_number || ast_strlen_zero(ies.called_number)) {
++ /* They're not permitted to access that context */
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
++ dundi_send(trans, resp, 0, 1, &ied);
++ } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
++ (peer->model & DUNDI_MODEL_INBOUND) &&
++ has_permission(peer->permit, ies.called_context)) {
++ res = dundi_answer_query(trans, &ies, ies.called_context);
++ if (res < 0) {
++ /* There is no such dundi context */
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
++ dundi_send(trans, resp, 0, 1, &ied);
++ }
++ } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
++ (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
++ has_permission(peer->include, ies.called_context)) {
++ res = dundi_prop_precache(trans, &ies, ies.called_context);
++ if (res < 0) {
++ /* There is no such dundi context */
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
++ dundi_send(trans, resp, 0, 1, &ied);
++ }
++ } else {
++ /* They're not permitted to access that context */
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
++ dundi_send(trans, resp, 0, 1, &ied);
++ }
++ }
++ } else {
++ /* They're not permitted to access that context */
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
++ dundi_send(trans, resp, 0, 1, &ied);
++ }
++ }
++ break;
++ case DUNDI_COMMAND_REGREQ:
++ /* A register request -- should only have one entity */
++ peer = find_peer(ies.eids[0]);
++ if (!peer || !peer->dynamic) {
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
++ dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
++ } else {
++ int hasauth = 0;
++ trans->us_eid = peer->us_eid;
++ if (!ast_strlen_zero(peer->inkey)) {
++ hasauth = encrypted;
++ } else
++ hasauth = 1;
++ if (hasauth) {
++ int expire = default_expiration;
++ char iabuf[INET_ADDRSTRLEN];
++ char data[256];
++ int needqual = 0;
++ if (peer->registerexpire > -1)
++ ast_sched_del(sched, peer->registerexpire);
++ peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
++ ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
++ snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
++ ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
++ if (inaddrcmp(&peer->addr, &trans->addr)) {
++ if (option_verbose > 2)
++ 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));
++ needqual = 1;
++ }
++
++ memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
++ dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
++ dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
++ if (needqual)
++ qualify_peer(peer, 1);
++ }
++ }
++ break;
++ case DUNDI_COMMAND_DPRESPONSE:
++ /* A dialplan response, lets see what we got... */
++ if (ies.cause < 1) {
++ /* Success of some sort */
++ ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
++ if (trans->flags & FLAG_ENCRYPT) {
++ authpass = encrypted;
++ } else
++ authpass = 1;
++ if (authpass) {
++ /* Pass back up answers */
++ if (trans->parent && trans->parent->dr) {
++ y = trans->parent->respcount;
++ for (x=0;x<ies.anscount;x++) {
++ if (trans->parent->respcount < trans->parent->maxcount) {
++ /* Make sure it's not already there */
++ for (z=0;z<trans->parent->respcount;z++) {
++ if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
++ !strcmp(trans->parent->dr[z].dest, ies.answers[x]->data))
++ break;
++ }
++ if (z == trans->parent->respcount) {
++ /* Copy into parent responses */
++ trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
++ trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
++ trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
++ trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
++ if (ies.expiration > 0)
++ trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
++ else
++ trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
++ dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
++ sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
++ &ies.answers[x]->eid);
++ strncpy(trans->parent->dr[trans->parent->respcount].dest, ies.answers[x]->data,
++ sizeof(trans->parent->dr[trans->parent->respcount].dest));
++ strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
++ sizeof(trans->parent->dr[trans->parent->respcount].tech));
++ trans->parent->respcount++;
++ trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
++ } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
++ /* Update weight if appropriate */
++ trans->parent->dr[z].weight = ies.answers[x]->weight;
++ }
++ } else
++ ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
++ trans->parent->number, trans->parent->dcontext);
++ }
++ /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
++ the cache know if this request was unaffected by our entity list. */
++ cache_save(&trans->them_eid, trans->parent, y,
++ ies.hint ? ntohs(ies.hint->flags) & DUNDI_HINT_UNAFFECTED : 0, ies.expiration, 0);
++ if (ies.hint) {
++ cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
++ if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
++ trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
++ if (ntohs(ies.hint->flags) & DUNDI_HINT_DONT_ASK) {
++ if (strlen(ies.hint->data) > strlen(trans->parent->hmd->exten)) {
++ strncpy(trans->parent->hmd->exten, ies.hint->data,
++ sizeof(trans->parent->hmd->exten) - 1);
++ }
++ } else {
++ trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
++ }
++ }
++ if (ies.expiration > 0) {
++ if (trans->parent->expiration > ies.expiration) {
++ trans->parent->expiration = ies.expiration;
++ }
++ }
++ }
++ /* Close connection if not final */
++ if (!final)
++ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
++ }
++
++ } else {
++ /* Auth failure, check for data */
++ if (!final) {
++ /* Cancel if they didn't already */
++ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
++ }
++ }
++ break;
++ case DUNDI_COMMAND_EIDRESPONSE:
++ /* A dialplan response, lets see what we got... */
++ if (ies.cause < 1) {
++ /* Success of some sort */
++ ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
++ if (trans->flags & FLAG_ENCRYPT) {
++ authpass = encrypted;
++ } else
++ authpass = 1;
++ if (authpass) {
++ /* Pass back up answers */
++ if (trans->parent && trans->parent->dei && ies.q_org) {
++ if (!trans->parent->respcount) {
++ trans->parent->respcount++;
++ if (ies.q_dept)
++ strncpy(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit) - 1);
++ if (ies.q_org)
++ strncpy(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org) - 1);
++ if (ies.q_locality)
++ strncpy(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality) - 1);
++ if (ies.q_stateprov)
++ strncpy(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov) - 1);
++ if (ies.q_country)
++ strncpy(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country) - 1);
++ if (ies.q_email)
++ strncpy(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email) - 1);
++ if (ies.q_phone)
++ strncpy(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone) - 1);
++ if (ies.q_ipaddr)
++ strncpy(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr) - 1);
++ if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
++ /* If it's them, update our address */
++ ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
++ trans->addr.sin_addr);
++ }
++ }
++ if (ies.hint) {
++ if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
++ trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
++ }
++ }
++ /* Close connection if not final */
++ if (!final)
++ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
++ }
++
++ } else {
++ /* Auth failure, check for data */
++ if (!final) {
++ /* Cancel if they didn't already */
++ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
++ }
++ }
++ break;
++ case DUNDI_COMMAND_REGRESPONSE:
++ /* A dialplan response, lets see what we got... */
++ if (ies.cause < 1) {
++ int hasauth;
++ /* Success of some sort */
++ if (trans->flags & FLAG_ENCRYPT) {
++ hasauth = encrypted;
++ } else
++ hasauth = 1;
++
++ if (!hasauth) {
++ ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
++ if (!final) {
++ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
++ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
++ }
++ } else {
++ 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),
++ dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
++ /* Close connection if not final */
++ if (!final)
++ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
++ }
++ } else {
++ /* Auth failure, cancel if they didn't for some reason */
++ if (!final) {
++ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
++ }
++ }
++ break;
++ case DUNDI_COMMAND_INVALID:
++ case DUNDI_COMMAND_NULL:
++ case DUNDI_COMMAND_PRECACHERP:
++ /* Do nothing special */
++ if (!final)
++ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
++ break;
++ case DUNDI_COMMAND_ENCREJ:
++ if ((trans->flags & FLAG_SENDFULLKEY) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
++ /* No really, it's over at this point */
++ if (!final)
++ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
++ } else {
++ /* Send with full key */
++ trans->flags |= FLAG_SENDFULLKEY;
++ if (final) {
++ /* Ooops, we got a final message, start by sending ACK... */
++ dundi_ack(trans, hdr->cmdresp & 0x80);
++ trans->aseqno = trans->iseqno;
++ /* Now, we gotta create a new transaction */
++ if (!reset_transaction(trans)) {
++ /* Make sure handle_frame doesn't destroy us */
++ hdr->cmdresp &= 0x7f;
++ /* Parse the message we transmitted */
++ memset(&ies, 0, sizeof(ies));
++ dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
++ /* Reconstruct outgoing encrypted packet */
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
++ dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
++ dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
++ if (ies.encblock)
++ dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
++ dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
++ peer->sentfullkey = 1;
++ }
++ }
++ }
++ break;
++ case DUNDI_COMMAND_ENCRYPT:
++ if (!encrypted) {
++ /* No nested encryption! */
++ if ((trans->iseqno == 1) && !trans->oseqno) {
++ if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
++ ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
++ (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
++ if (!final) {
++ dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
++ }
++ break;
++ }
++ apply_peer(trans, peer);
++ /* Key passed, use new contexts for this session */
++ trans->ecx = peer->them_ecx;
++ trans->dcx = peer->them_dcx;
++ }
++ if ((trans->flags & FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
++ struct dundi_hdr *dhdr;
++ unsigned char decoded[MAX_PACKET_SIZE];
++ int ddatalen;
++ ddatalen = sizeof(decoded);
++ dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
++ if (dhdr) {
++ /* Handle decrypted response */
++ if (dundidebug)
++ dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
++ handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
++ /* Carry back final flag */
++ hdr->cmdresp |= dhdr->cmdresp & 0x80;
++ break;
++ } else
++ ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
++ }
++ }
++ if (!final) {
++ /* Turn off encryption */
++ trans->flags &= ~FLAG_ENCRYPT;
++ dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
++ }
++ break;
++ default:
++ /* Send unknown command if we don't know it, with final flag IFF it's the
++ first command in the dialog and only if we haven't recieved final notification */
++ if (!final) {
++ dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
++ dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
++ }
++ }
++ return 0;
++}
++
++static void destroy_packet(struct dundi_packet *pack, int needfree);
++static void destroy_packets(struct dundi_packet *p)
++{
++ struct dundi_packet *prev;
++ while(p) {
++ prev = p;
++ p = p->next;
++ if (prev->retransid > -1)
++ ast_sched_del(sched, prev->retransid);
++ free(prev);
++ }
++}
++
++
++static int ack_trans(struct dundi_transaction *trans, int iseqno)
++{
++ /* Ack transmitted packet corresponding to iseqno */
++ struct dundi_packet *pack;
++ pack = trans->packets;
++ while(pack) {
++ if ((pack->h->oseqno + 1) % 255 == iseqno) {
++ destroy_packet(pack, 0);
++ if (trans->lasttrans) {
++ ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
++ destroy_packets(trans->lasttrans);
++ }
++ trans->lasttrans = pack;
++ if (trans->autokillid > -1)
++ ast_sched_del(sched, trans->autokillid);
++ trans->autokillid = -1;
++ return 1;
++ }
++ pack = pack->next;
++ }
++ return 0;
++}
++
++static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
++{
++ struct dundi_transaction *trans;
++ trans = find_transaction(h, sin);
++ if (!trans) {
++ dundi_reject(h, sin);
++ return 0;
++ }
++ /* Got a transaction, see where this header fits in */
++ if (h->oseqno == trans->iseqno) {
++ /* Just what we were looking for... Anything but ack increments iseqno */
++ if (ack_trans(trans, h->iseqno) && (trans->flags & FLAG_FINAL)) {
++ /* If final, we're done */
++ destroy_trans(trans, 0);
++ return 0;
++ }
++ if (h->cmdresp != DUNDI_COMMAND_ACK) {
++ trans->oiseqno = trans->iseqno;
++ trans->iseqno++;
++ handle_command_response(trans, h, datalen, 0);
++ }
++ if (trans->aseqno != trans->iseqno) {
++ dundi_ack(trans, h->cmdresp & 0x80);
++ trans->aseqno = trans->iseqno;
++ }
++ /* Delete any saved last transmissions */
++ destroy_packets(trans->lasttrans);
++ trans->lasttrans = NULL;
++ if (h->cmdresp & 0x80) {
++ /* Final -- destroy now */
++ destroy_trans(trans, 0);
++ }
++ } else if (h->oseqno == trans->oiseqno) {
++ /* Last incoming sequence number -- send ACK without processing */
++ dundi_ack(trans, 0);
++ } else {
++ /* Out of window -- simply drop */
++ ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
++ }
++ return 0;
++}
++
++static int socket_read(int *id, int fd, short events, void *cbdata)
++{
++ struct sockaddr_in sin;
++ int res;
++ struct dundi_hdr *h;
++ unsigned char buf[MAX_PACKET_SIZE];
++ int len;
++ len = sizeof(sin);
++ res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
++ if (res < 0) {
++ if (errno != ECONNREFUSED)
++ ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
++ return 1;
++ }
++ if (res < sizeof(struct dundi_hdr)) {
++ ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
++ return 1;
++ }
++ buf[res] = '\0';
++ h = (struct dundi_hdr *)buf;
++ if (dundidebug)
++ dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
++ ast_mutex_lock(&peerlock);
++ handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
++ ast_mutex_unlock(&peerlock);
++ return 1;
++}
++
++static void build_secret(char *secret, int seclen)
++{
++ char tmp[16];
++ char *s;
++ build_iv(tmp);
++ secret[0] = '\0';
++ ast_base64encode(secret ,tmp, sizeof(tmp), seclen);
++ /* Eliminate potential bad characters */
++ while((s = strchr(secret, ';'))) *s = '+';
++ while((s = strchr(secret, '/'))) *s = '+';
++ while((s = strchr(secret, ':'))) *s = '+';
++ while((s = strchr(secret, '@'))) *s = '+';
++}
++
++
++static void save_secret(const char *newkey, const char *oldkey)
++{
++ char tmp[256];
++ if (oldkey)
++ snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
++ else
++ snprintf(tmp, sizeof(tmp), "%s", newkey);
++ rotatetime = time(NULL) + DUNDI_SECRET_TIME;
++ ast_db_put(secretpath, "secret", tmp);
++ snprintf(tmp, sizeof(tmp), "%ld", rotatetime);
++ ast_db_put(secretpath, "secretexpiry", tmp);
++}
++
++static void load_password(void)
++{
++ char *current=NULL;
++ char *last=NULL;
++ char tmp[256];
++ time_t expired;
++
++ ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
++ if (sscanf(tmp, "%ld", &expired) == 1) {
++ ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
++ current = strchr(tmp, ';');
++ if (!current)
++ current = tmp;
++ else {
++ *current = '\0';
++ current++;
++ };
++ if ((time(NULL) - expired) < 0) {
++ if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
++ expired = time(NULL) + DUNDI_SECRET_TIME;
++ } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
++ last = current;
++ current = NULL;
++ } else {
++ last = NULL;
++ current = NULL;
++ }
++ }
++ if (current) {
++ /* Current key is still valid, just setup rotatation properly */
++ strncpy(cursecret, current, sizeof(cursecret) - 1);
++ rotatetime = expired;
++ } else {
++ /* Current key is out of date, rotate or eliminate all together */
++ build_secret(cursecret, sizeof(cursecret));
++ save_secret(cursecret, last);
++ }
++}
++
++static void check_password(void)
++{
++ char oldsecret[80];
++ time_t now;
++
++ time(&now);
++#if 0
++ printf("%ld/%ld\n", now, rotatetime);
++#endif
++ if ((now - rotatetime) >= 0) {
++ /* Time to rotate keys */
++ strncpy(oldsecret, cursecret, sizeof(oldsecret) - 1);
++ build_secret(cursecret, sizeof(cursecret));
++ save_secret(cursecret, oldsecret);
++ }
++}
++
++static void *network_thread(void *ignore)
++{
++ /* Our job is simple: Send queued messages, retrying if necessary. Read frames
++ from the network, and queue them for delivery to the channels */
++ int res;
++ /* Establish I/O callback for socket read */
++ ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
++ for(;;) {
++ res = ast_sched_wait(sched);
++ if ((res > 1000) || (res < 0))
++ res = 1000;
++ res = ast_io_wait(io, res);
++ if (res >= 0) {
++ ast_mutex_lock(&peerlock);
++ ast_sched_runq(sched);
++ ast_mutex_unlock(&peerlock);
++ }
++ check_password();
++ }
++ return NULL;
++}
++
++static void *process_precache(void *ign)
++{
++ struct dundi_precache_queue *qe;
++ time_t now;
++ char context[256]="";
++ char number[256]="";
++ int run;
++ for (;;) {
++ time(&now);
++ run = 0;
++ ast_mutex_lock(&pclock);
++ if (pcq) {
++ if (!pcq->expiration) {
++ /* Gone... Remove... */
++ qe = pcq;
++ pcq = pcq->next;
++ free(qe);
++ } else if (pcq->expiration < now) {
++ /* Process this entry */
++ pcq->expiration = 0;
++ strncpy(context, pcq->context, sizeof(context) - 1);
++ strncpy(number, pcq->number, sizeof(number) - 1);
++ run = 1;
++ }
++ }
++ ast_mutex_unlock(&pclock);
++ if (run) {
++ dundi_precache(context, number);
++ } else
++ sleep(1);
++ }
++ return NULL;
++}
++
++static int start_network_thread(void)
++{
++ ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
++ ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
++ return 0;
++}
++
++static int dundi_do_debug(int fd, int argc, char *argv[])
++{
++ if (argc != 2)
++ return RESULT_SHOWUSAGE;
++ dundidebug = 1;
++ ast_cli(fd, "DUNDi Debugging Enabled\n");
++ return RESULT_SUCCESS;
++}
++
++static int dundi_do_store_history(int fd, int argc, char *argv[])
++{
++ if (argc != 3)
++ return RESULT_SHOWUSAGE;
++ global_storehistory = 1;
++ ast_cli(fd, "DUNDi History Storage Enabled\n");
++ return RESULT_SUCCESS;
++}
++
++static int dundi_flush(int fd, int argc, char *argv[])
++{
++ int stats=0;
++ if ((argc < 2) || (argc > 3))
++ return RESULT_SHOWUSAGE;
++ if (argc > 2) {
++ if (!strcasecmp(argv[2], "stats"))
++ stats = 1;
++ else
++ return RESULT_SHOWUSAGE;
++ }
++ if (stats) {
++ /* Flush statistics */
++ struct dundi_peer *p;
++ int x;
++ ast_mutex_lock(&peerlock);
++ p = peers;
++ while(p) {
++ for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
++ if (p->lookups[x])
++ free(p->lookups[x]);
++ p->lookups[x] = NULL;
++ p->lookuptimes[x] = 0;
++ }
++ p->avgms = 0;
++ p = p->next;
++ }
++ ast_mutex_unlock(&peerlock);
++ } else {
++ ast_db_deltree("dundi/cache", NULL);
++ ast_cli(fd, "DUNDi Cache Flushed\n");
++ }
++ return RESULT_SUCCESS;
++}
++
++static int dundi_no_debug(int fd, int argc, char *argv[])
++{
++ if (argc != 3)
++ return RESULT_SHOWUSAGE;
++ dundidebug = 0;
++ ast_cli(fd, "DUNDi Debugging Disabled\n");
++ return RESULT_SUCCESS;
++}
++
++static int dundi_no_store_history(int fd, int argc, char *argv[])
++{
++ if (argc != 4)
++ return RESULT_SHOWUSAGE;
++ global_storehistory = 0;
++ ast_cli(fd, "DUNDi History Storage Disabled\n");
++ return RESULT_SUCCESS;
++}
++
++static char *model2str(int model)
++{
++ switch(model) {
++ case DUNDI_MODEL_INBOUND:
++ return "Inbound";
++ case DUNDI_MODEL_OUTBOUND:
++ return "Outbound";
++ case DUNDI_MODEL_SYMMETRIC:
++ return "Symmetric";
++ default:
++ return "Unknown";
++ }
++}
++
++static char *complete_peer_helper(char *line, char *word, int pos, int state, int rpos)
++{
++ int which=0;
++ char *ret;
++ struct dundi_peer *p;
++ char eid_str[20];
++ if (pos != rpos)
++ return NULL;
++ ast_mutex_lock(&peerlock);
++ p = peers;
++ while(p) {
++ if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
++ if (++which > state)
++ break;
++ }
++ p = p->next;
++ }
++ if (p) {
++ ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
++ } else
++ ret = NULL;
++ ast_mutex_unlock(&peerlock);
++ return ret;
++}
++
++static char *complete_peer_4(char *line, char *word, int pos, int state)
++{
++ return complete_peer_helper(line, word, pos, state, 3);
++}
++
++static int rescomp(const void *a, const void *b)
++{
++ const struct dundi_result *resa, *resb;
++ resa = a;
++ resb = b;
++ if (resa->weight < resb->weight)
++ return -1;
++ if (resa->weight > resb->weight)
++ return 1;
++ return 0;
++}
++
++static void sort_results(struct dundi_result *results, int count)
++{
++ qsort(results, count, sizeof(results[0]), rescomp);
++}
++
++static int dundi_do_lookup(int fd, int argc, char *argv[])
++{
++ int res;
++ char tmp[256] = "";
++ char fs[80] = "";
++ char *context;
++ int x;
++ int bypass = 0;
++ struct dundi_result dr[MAX_RESULTS];
++ struct timeval start;
++ if ((argc < 3) || (argc > 4))
++ return RESULT_SHOWUSAGE;
++ if (argc > 3) {
++ if (!strcasecmp(argv[3], "bypass"))
++ bypass=1;
++ else
++ return RESULT_SHOWUSAGE;
++ }
++ strncpy(tmp, argv[2], sizeof(tmp) - 1);
++ context = strchr(tmp, '@');
++ if (context) {
++ *context = '\0';
++ context++;
++ }
++ gettimeofday(&start, NULL);
++ res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
++
++ if (res < 0)
++ ast_cli(fd, "DUNDi lookup returned error.\n");
++ else if (!res)
++ ast_cli(fd, "DUNDi lookup returned no results.\n");
++ else
++ sort_results(dr, res);
++ for (x=0;x<res;x++) {
++ 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));
++ ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
++ }
++ ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
++ return RESULT_SUCCESS;
++}
++
++static int dundi_do_precache(int fd, int argc, char *argv[])
++{
++ int res;
++ char tmp[256] = "";
++ char *context;
++ struct timeval start;
++ if ((argc < 3) || (argc > 3))
++ return RESULT_SHOWUSAGE;
++ strncpy(tmp, argv[2], sizeof(tmp) - 1);
++ context = strchr(tmp, '@');
++ if (context) {
++ *context = '\0';
++ context++;
++ }
++ gettimeofday(&start, NULL);
++ res = dundi_precache(context, tmp);
++
++ if (res < 0)
++ ast_cli(fd, "DUNDi precache returned error.\n");
++ else if (!res)
++ ast_cli(fd, "DUNDi precache returned no error.\n");
++ ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
++ return RESULT_SUCCESS;
++}
++
++static int dundi_do_query(int fd, int argc, char *argv[])
++{
++ int res;
++ char tmp[256] = "";
++ char *context;
++ dundi_eid eid;
++ struct dundi_entity_info dei;
++ if ((argc < 3) || (argc > 3))
++ return RESULT_SHOWUSAGE;
++ if (dundi_str_to_eid(&eid, argv[2])) {
++ ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
++ return RESULT_SHOWUSAGE;
++ }
++ strncpy(tmp, argv[2], sizeof(tmp) - 1);
++ context = strchr(tmp, '@');
++ if (context) {
++ *context = '\0';
++ context++;
++ }
++ res = dundi_query_eid(&dei, context, eid);
++ if (res < 0)
++ ast_cli(fd, "DUNDi Query EID returned error.\n");
++ else if (!res)
++ ast_cli(fd, "DUNDi Query EID returned no results.\n");
++ else {
++ ast_cli(fd, "DUNDi Query EID succeeded:\n");
++ ast_cli(fd, "Department: %s\n", dei.orgunit);
++ ast_cli(fd, "Organization: %s\n", dei.org);
++ ast_cli(fd, "City/Locality: %s\n", dei.locality);
++ ast_cli(fd, "State/Province: %s\n", dei.stateprov);
++ ast_cli(fd, "Country: %s\n", dei.country);
++ ast_cli(fd, "E-mail: %s\n", dei.email);
++ ast_cli(fd, "Phone: %s\n", dei.phone);
++ ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
++ }
++ return RESULT_SUCCESS;
++}
++
++static int dundi_show_peer(int fd, int argc, char *argv[])
++{
++ struct dundi_peer *peer;
++ struct permission *p;
++ char *order;
++ char iabuf[INET_ADDRSTRLEN];
++ char eid_str[20];
++ int x, cnt;
++
++ if (argc != 4)
++ return RESULT_SHOWUSAGE;
++ ast_mutex_lock(&peerlock);
++ peer = peers;
++ while(peer) {
++ if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
++ break;
++ peer = peer->next;
++ }
++ if (peer) {
++ switch(peer->order) {
++ case 0:
++ order = "Primary";
++ break;
++ case 1:
++ order = "Secondary";
++ break;
++ case 2:
++ order = "Tertiary";
++ break;
++ case 3:
++ order = "Quartiary";
++ break;
++ default:
++ order = "Unknown";
++ }
++ ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ ast_cli(fd, "Model: %s\n", model2str(peer->model));
++ ast_cli(fd, "Order: %s\n", order);
++ ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
++ ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
++ ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
++ ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
++ ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
++ ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
++ if (peer->include) {
++ ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
++ }
++ p = peer->include;
++ while(p) {
++ ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
++ p = p->next;
++ }
++ if (peer->permit) {
++ ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
++ }
++ p = peer->permit;
++ while(p) {
++ ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
++ p = p->next;
++ }
++ cnt = 0;
++ for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
++ if (peer->lookups[x]) {
++ if (!cnt)
++ ast_cli(fd, "Last few query times:\n");
++ ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
++ cnt++;
++ }
++ }
++ if (cnt)
++ ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
++ } else
++ ast_cli(fd, "No such peer '%s'\n", argv[3]);
++ ast_mutex_unlock(&peerlock);
++ return RESULT_SUCCESS;
++}
++
++static int dundi_show_peers(int fd, int argc, char *argv[])
++{
++#define FORMAT2 "%-20.20s %-15.15s %-10.10s %-10.10s %-8.8s %-15.15s\n"
++#define FORMAT "%-20.20s %-15.15s %s %-10.10s %-10.10s %-8.8s %-15.15s\n"
++ struct dundi_peer *peer;
++ char iabuf[INET_ADDRSTRLEN];
++ int registeredonly=0;
++ char avgms[20];
++ char eid_str[20];
++ char *order = NULL;
++
++ if ((argc != 3) && (argc != 4) && (argc != 5))
++ return RESULT_SHOWUSAGE;
++ if ((argc == 4)) {
++ if (!strcasecmp(argv[3], "registered")) {
++ registeredonly = 1;
++ } else
++ return RESULT_SHOWUSAGE;
++ }
++ ast_mutex_lock(&peerlock);
++ ast_cli(fd, FORMAT2, "EID", "Host", "Model", "Order", "AvgTime", "Status");
++ for (peer = peers;peer;peer = peer->next) {
++ char status[20] = "";
++ int print_line = -1;
++ char srch[2000] = "";
++ if (registeredonly && !peer->addr.sin_addr.s_addr)
++ continue;
++ if (peer->maxms) {
++ if (peer->lastms < 0)
++ strncpy(status, "UNREACHABLE", sizeof(status) - 1);
++ else if (peer->lastms > peer->maxms)
++ snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
++ else if (peer->lastms)
++ snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
++ else
++ strncpy(status, "UNKNOWN", sizeof(status) - 1);
++ } else
++ strncpy(status, "Unmonitored", sizeof(status) - 1);
++ if (peer->avgms)
++ snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
++ else
++ strcpy(avgms, "Unavail");
++ switch(peer->order) {
++ case 0:
++ order = "Primary";
++ break;
++ case 1:
++ order = "Secondary";
++ break;
++ case 2:
++ order = "Tertiary";
++ break;
++ case 3:
++ order = "Quartiary";
++ break;
++ default:
++ order = "Unknown";
++ }
++ snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
++ peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
++ peer->dynamic ? "(D)" : "(S)", model2str(peer->model), order, avgms, status);
++
++ if (argc == 5) {
++ if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
++ print_line = -1;
++ } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
++ print_line = 1;
++ } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
++ print_line = -1;
++ } else {
++ print_line = 0;
++ }
++ }
++
++ if (print_line) {
++ ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
++ peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
++ peer->dynamic ? "(D)" : "(S)", model2str(peer->model), order, avgms, status);
++ }
++ }
++ ast_mutex_unlock(&peerlock);
++ return RESULT_SUCCESS;
++#undef FORMAT
++#undef FORMAT2
++}
++
++static int dundi_show_trans(int fd, int argc, char *argv[])
++{
++#define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
++#define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
++ struct dundi_transaction *trans;
++ char iabuf[INET_ADDRSTRLEN];
++ if (argc != 3)
++ return RESULT_SHOWUSAGE;
++ ast_mutex_lock(&peerlock);
++ ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
++ for (trans = alltrans;trans;trans = trans->allnext) {
++ ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr),
++ ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
++ }
++ ast_mutex_unlock(&peerlock);
++ return RESULT_SUCCESS;
++#undef FORMAT
++#undef FORMAT2
++}
++
++static int dundi_show_entityid(int fd, int argc, char *argv[])
++{
++ char eid_str[20];
++ if (argc != 3)
++ return RESULT_SHOWUSAGE;
++ ast_mutex_lock(&peerlock);
++ dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
++ ast_mutex_unlock(&peerlock);
++ ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
++ return RESULT_SUCCESS;
++}
++
++static int dundi_show_requests(int fd, int argc, char *argv[])
++{
++#define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
++#define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
++ struct dundi_request *req;
++ char eidstr[20];
++ if (argc != 3)
++ return RESULT_SHOWUSAGE;
++ ast_mutex_lock(&peerlock);
++ ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
++ for (req = requests;req;req = req->next) {
++ ast_cli(fd, FORMAT, req->number, req->dcontext,
++ dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
++ }
++ ast_mutex_unlock(&peerlock);
++ return RESULT_SUCCESS;
++#undef FORMAT
++#undef FORMAT2
++}
++
++/* Grok-a-dial DUNDi */
++
++static int dundi_show_mappings(int fd, int argc, char *argv[])
++{
++#define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
++#define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
++ struct dundi_mapping *map;
++ char fs[256];
++ if (argc != 3)
++ return RESULT_SHOWUSAGE;
++ ast_mutex_lock(&peerlock);
++ ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
++ for (map = mappings;map;map = map->next) {
++ ast_cli(fd, FORMAT, map->dcontext, map->weight,
++ ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
++ dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
++ }
++ ast_mutex_unlock(&peerlock);
++ return RESULT_SUCCESS;
++#undef FORMAT
++#undef FORMAT2
++}
++
++static int dundi_show_precache(int fd, int argc, char *argv[])
++{
++#define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
++#define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
++ struct dundi_precache_queue *qe;
++ int h,m,s;
++ time_t now;
++
++ if (argc != 3)
++ return RESULT_SHOWUSAGE;
++ time(&now);
++ ast_mutex_lock(&pclock);
++ ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
++ for (qe = pcq;qe;qe = qe->next) {
++ s = qe->expiration - now;
++ h = s / 3600;
++ s = s % 3600;
++ m = s / 60;
++ s = s % 60;
++ ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
++ }
++ ast_mutex_unlock(&pclock);
++ return RESULT_SUCCESS;
++#undef FORMAT
++#undef FORMAT2
++}
++
++static char debug_usage[] =
++"Usage: dundi debug\n"
++" Enables dumping of DUNDi packets for debugging purposes\n";
++
++static char no_debug_usage[] =
++"Usage: dundi no debug\n"
++" Disables dumping of DUNDi packets for debugging purposes\n";
++
++static char store_history_usage[] =
++"Usage: dundi store history\n"
++" Enables storing of DUNDi requests and times for debugging\n"
++"purposes\n";
++
++static char no_store_history_usage[] =
++"Usage: dundi no store history\n"
++" Disables storing of DUNDi requests and times for debugging\n"
++"purposes\n";
++
++static char show_peers_usage[] =
++"Usage: dundi show peers\n"
++" Lists all known DUNDi peers.\n";
++
++static char show_trans_usage[] =
++"Usage: dundi show trans\n"
++" Lists all known DUNDi transactions.\n";
++
++static char show_mappings_usage[] =
++"Usage: dundi show mappings\n"
++" Lists all known DUNDi mappings.\n";
++
++static char show_precache_usage[] =
++"Usage: dundi show precache\n"
++" Lists all known DUNDi scheduled precache updates.\n";
++
++static char show_entityid_usage[] =
++"Usage: dundi show entityid\n"
++" Displays the global entityid for this host.\n";
++
++static char show_peer_usage[] =
++"Usage: dundi show peer [peer]\n"
++" Provide a detailed description of a specifid DUNDi peer.\n";
++
++static char show_requests_usage[] =
++"Usage: dundi show requests\n"
++" Lists all known pending DUNDi requests.\n";
++
++static char lookup_usage[] =
++"Usage: dundi lookup <number>[@context] [bypass]\n"
++" Lookup the given number within the given DUNDi context\n"
++"(or e164 if none is specified). Bypasses cache if 'bypass'\n"
++"keyword is specified.\n";
++
++static char precache_usage[] =
++"Usage: dundi precache <number>[@context]\n"
++" Lookup the given number within the given DUNDi context\n"
++"(or e164 if none is specified) and precaches the results to any\n"
++"upstream DUNDi push servers.\n";
++
++static char query_usage[] =
++"Usage: dundi query <entity>[@context]\n"
++" Attempts to retrieve contact information for a specific\n"
++"DUNDi entity identifier (EID) within a given DUNDi context (or\n"
++"e164 if none is specified).\n";
++
++static char flush_usage[] =
++"Usage: dundi flush [stats]\n"
++" Flushes DUNDi answer cache, used primarily for debug. If\n"
++"'stats' is present, clears timer statistics instead of normal\n"
++"operation.\n";
++
++static struct ast_cli_entry cli_debug =
++ { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
++
++static struct ast_cli_entry cli_store_history =
++ { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
++
++static struct ast_cli_entry cli_no_store_history =
++ { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
++
++static struct ast_cli_entry cli_flush =
++ { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
++
++static struct ast_cli_entry cli_no_debug =
++ { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
++
++static struct ast_cli_entry cli_show_peers =
++ { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
++
++static struct ast_cli_entry cli_show_trans =
++ { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
++
++static struct ast_cli_entry cli_show_entityid =
++ { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
++
++static struct ast_cli_entry cli_show_mappings =
++ { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
++
++static struct ast_cli_entry cli_show_precache =
++ { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
++
++static struct ast_cli_entry cli_show_requests =
++ { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
++
++static struct ast_cli_entry cli_show_peer =
++ { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
++
++static struct ast_cli_entry cli_lookup =
++ { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
++
++static struct ast_cli_entry cli_precache =
++ { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
++
++static struct ast_cli_entry cli_queryeid =
++ { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
++
++STANDARD_LOCAL_USER;
++
++LOCAL_USER_DECL;
++
++static struct dundi_transaction *create_transaction(struct dundi_peer *p)
++{
++ struct dundi_transaction *trans;
++ int tid;
++
++ /* Don't allow creation of transactions to non-registered peers */
++ if (p && !p->addr.sin_addr.s_addr)
++ return NULL;
++ tid = get_trans_id();
++ if (tid < 1)
++ return NULL;
++ trans = malloc(sizeof(struct dundi_transaction));
++ if (trans) {
++ memset(trans, 0, sizeof(struct dundi_transaction));
++ if (global_storehistory) {
++ gettimeofday(&trans->start, NULL);
++ trans->flags |= FLAG_STOREHIST;
++ }
++ trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
++ trans->autokillid = -1;
++ if (p) {
++ apply_peer(trans, p);
++ if (!p->sentfullkey)
++ trans->flags |= FLAG_SENDFULLKEY;
++ }
++ trans->strans = tid;
++ trans->allnext = alltrans;
++ alltrans = trans;
++ }
++ return trans;
++}
++
++static int dundi_xmit(struct dundi_packet *pack)
++{
++ int res;
++ char iabuf[INET_ADDRSTRLEN];
++ if (dundidebug)
++ dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
++ res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
++ if (res < 0) {
++ ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
++ ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
++ ntohs(pack->parent->addr.sin_port), strerror(errno));
++ }
++ if (res > 0)
++ res = 0;
++ return res;
++}
++
++static inline char *dstatus_append_long(char *buf, long val) {
++ val = htonl(val);
++ memcpy(buf, &val, sizeof(long));
++ return (buf + sizeof(long));
++}
++
++static inline char *dstatus_append_short(char *buf, short val) {
++ val = htons(val);
++ memcpy(buf, &val, sizeof(short));
++ return (buf + sizeof(short));
++}
++
++static inline char *dstatus_append_string(char *buf, char *str) {
++ unsigned short len = (unsigned short)strlen(str);
++ buf = dstatus_append_short(buf, len);
++ memcpy(buf, str, len);
++ return (buf + len);
++}
++
++/* Create a packet for the DUNDi Status Updates */
++static char *dstatus_v2_pkt_header(char *buf, char pkt_type, time_t ts, short seq, short totseq) {
++ *buf++ = '\003';
++ *buf++ = pkt_type;
++
++ /* Timestamp this sequence */
++ buf = dstatus_append_long(buf, ts);
++
++ /* Setup sequence headers */
++ buf = dstatus_append_short(buf, seq);
++ buf = dstatus_append_short(buf, totseq);
++
++ /* Append our global EID */
++ memcpy(buf, &global_eid, sizeof(global_eid));
++ buf += sizeof(global_eid);
++
++ buf = dstatus_append_string(buf, map_context);
++
++ return buf;
++}
++
++static int dundi_xmit_peering(void *data)
++{
++ char buf[1500];
++ char *ptr = buf;
++ short seq = 1, totseq = 0;
++ int need_send = 0, peer_count = 0;
++ struct dundi_peer *peer;
++ time_t send_t = time(NULL);
++
++ /* If we have no where to send, don't bother */
++ if(!map_addr.sin_addr.s_addr) {
++ if(option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "No server to send mapping update to...\n");
++ map_peering_sid = ast_sched_add(sched, map_update_interval, dundi_xmit_peering, 0);
++ return 0;
++ }
++
++ /* Provide a sequence number for the packet and totals */
++ ast_mutex_lock(&peerlock);
++ for (peer = peers;peer;peer = peer->next) {
++ if(has_permission(peer->include, map_context) || has_permission(peer->permit, map_context)) {
++ peer_count++;
++ need_send = 1;
++ if(peer_count == map_updates_per_pkt) {
++ peer_count = 0;
++ need_send = 0;
++ totseq++;
++ }
++ }
++ }
++ totseq += need_send;
++
++ /* Initialize the packet header */
++ ptr = dstatus_v2_pkt_header(buf, 1, send_t, seq, totseq);
++
++ /* Include our update interval, in seconds */
++ ptr = dstatus_append_short(ptr, map_update_interval/1000);
++
++ /* Include peers/packet configured */
++ *ptr++ = (unsigned char)map_updates_per_pkt;
++
++ peer_count = 0;
++ need_send = 0;
++ for (peer = peers;peer;peer = peer->next) {
++ if(!has_permission(peer->include, map_context) && !has_permission(peer->permit, map_context))
++ continue;
++
++ peer_count++;
++ need_send = 1;
++
++ /* Copy the peers EID */
++ memcpy(ptr, &peer->eid, sizeof(peer->eid));
++ ptr += sizeof(peer->eid);
++
++ /* This is the remote peer */
++ *ptr++ = (peer->dynamic?0:1);
++ memcpy(ptr, &peer->addr.sin_addr.s_addr, sizeof(peer->addr.sin_addr.s_addr));
++ ptr += sizeof(peer->addr.sin_addr.s_addr);
++
++ /* Append the model and order */
++ *ptr++ = peer->model;
++ *ptr++ = peer->order;
++
++ /* Do some long encoding of the timings */
++ ptr = dstatus_append_long(ptr, peer->maxms);
++ ptr = dstatus_append_long(ptr, peer->lastms);
++ ptr = dstatus_append_long(ptr, peer->avgms);
++
++ if(peer_count == map_updates_per_pkt) { /* Okay, it's actually arbitrary */
++ peer_count = 0;
++ need_send = 0;
++ sendto(netsocket, buf, ptr - buf, 0, (struct sockaddr *)&map_addr, sizeof(map_addr));
++ seq++; /* We sent one, so move on */
++
++ ptr = dstatus_v2_pkt_header(buf, 1, send_t, seq, totseq);
++ /* Include our update interval, in seconds */
++ ptr = dstatus_append_short(ptr, map_update_interval/1000);
++
++ /* Include peers/packet configured */
++ *ptr++ = (unsigned char)map_updates_per_pkt;
++ }
++ }
++
++ /* If we get here, and haven't sent the packet, send it now */
++ if(need_send)
++ sendto(netsocket, buf, ptr - buf, 0, (struct sockaddr *)&map_addr, sizeof(map_addr));
++
++ /* Unlock */
++ ast_mutex_unlock(&peerlock);
++
++ /* Reschedule yourselves */
++ map_peering_sid = ast_sched_add(sched, map_update_interval, dundi_xmit_peering, 0);
++ return RESULT_SUCCESS;
++}
++
++static int dundi_xmit_contact(void *data) {
++ char buf[1500];
++ char *ptr = buf;
++
++ /* Packet type 2, sent now, 1/1 packets */
++ ptr = dstatus_v2_pkt_header(buf, 2, time(NULL), 1, 1);
++
++ ptr = dstatus_append_string(ptr, dept);
++ ptr = dstatus_append_string(ptr, org);
++ ptr = dstatus_append_string(ptr, locality);
++ ptr = dstatus_append_string(ptr, stateprov);
++ ptr = dstatus_append_string(ptr, country);
++ ptr = dstatus_append_string(ptr, email);
++ ptr = dstatus_append_string(ptr, phone);
++
++ sendto(netsocket, buf, ptr - buf, 0, (struct sockaddr *)&map_addr, sizeof(map_addr));
++
++ map_contact_sid = ast_sched_add(sched, map_update_interval * 5, dundi_xmit_contact, 0);
++ return RESULT_SUCCESS;
++}
++
++static void destroy_packet(struct dundi_packet *pack, int needfree)
++{
++ struct dundi_packet *prev, *cur;
++ if (pack->parent) {
++ prev = NULL;
++ cur = pack->parent->packets;
++ while(cur) {
++ if (cur == pack) {
++ if (prev)
++ prev->next = cur->next;
++ else
++ pack->parent->packets = cur->next;
++ break;
++ }
++ prev = cur;
++ cur = cur->next;
++ }
++ }
++ if (pack->retransid > -1)
++ ast_sched_del(sched, pack->retransid);
++ if (needfree)
++ free(pack);
++ else {
++ pack->retransid = -1;
++ pack->next = NULL;
++ }
++}
++
++static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
++{
++ struct dundi_transaction *cur, *prev;
++ struct dundi_peer *peer;
++ struct timeval tv;
++ int ms;
++ int x;
++ int cnt;
++ char eid_str[20];
++ if (trans->flags & (FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
++ peer = peers;
++ while (peer) {
++ if (peer->regtrans == trans)
++ peer->regtrans = NULL;
++ if (peer->keypending == trans)
++ peer->keypending = NULL;
++ if (peer->qualtrans == trans) {
++ if (fromtimeout) {
++ if (peer->lastms > -1)
++ ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ peer->lastms = -1;
++ } else {
++ gettimeofday(&tv, NULL);
++ ms = (tv.tv_sec - peer->qualtx.tv_sec) * 1000 +
++ (tv.tv_usec - peer->qualtx.tv_usec) / 1000;
++ if (ms < 1)
++ ms = 1;
++ if (ms < peer->maxms) {
++ if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
++ ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ } else if (peer->lastms < peer->maxms) {
++ 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);
++ }
++ peer->lastms = ms;
++ }
++ peer->qualtrans = NULL;
++ }
++ if (trans->flags & FLAG_STOREHIST) {
++ if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
++ if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
++ peer->avgms = 0;
++ cnt = 0;
++ if (peer->lookups[DUNDI_TIMING_HISTORY-1])
++ free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
++ for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
++ peer->lookuptimes[x] = peer->lookuptimes[x-1];
++ peer->lookups[x] = peer->lookups[x-1];
++ if (peer->lookups[x]) {
++ peer->avgms += peer->lookuptimes[x];
++ cnt++;
++ }
++ }
++ peer->lookuptimes[0] = calc_ms(&trans->start);
++ peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
++ if (peer->lookups[0]) {
++ sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
++ peer->avgms += peer->lookuptimes[0];
++ cnt++;
++ }
++ if (cnt)
++ peer->avgms /= cnt;
++ }
++ }
++ }
++ peer = peer->next;
++ }
++ }
++ if (trans->parent) {
++ /* Unlink from parent if appropriate */
++ prev = NULL;
++ cur = trans->parent->trans;
++ while(cur) {
++ if (cur == trans) {
++ if (prev)
++ prev->next = trans->next;
++ else
++ trans->parent->trans = trans->next;
++ break;
++ }
++ prev = cur;
++ cur = cur->next;
++ }
++ if (!trans->parent->trans) {
++ /* Wake up sleeper */
++ if (trans->parent->pfds[1] > -1) {
++ write(trans->parent->pfds[1], "killa!", 6);
++ }
++ }
++ }
++ /* Unlink from all trans */
++ prev = NULL;
++ cur = alltrans;
++ while(cur) {
++ if (cur == trans) {
++ if (prev)
++ prev->allnext = trans->allnext;
++ else
++ alltrans = trans->allnext;
++ break;
++ }
++ prev = cur;
++ cur = cur->allnext;
++ }
++ destroy_packets(trans->packets);
++ destroy_packets(trans->lasttrans);
++ trans->packets = NULL;
++ if (trans->autokillid > -1)
++ ast_sched_del(sched, trans->autokillid);
++ trans->autokillid = -1;
++ if (trans->thread) {
++ /* If used by a thread, mark as dead and be done */
++ trans->flags |= FLAG_DEAD;
++ } else
++ free(trans);
++}
++
++static int dundi_rexmit(void *data)
++{
++ struct dundi_packet *pack;
++ char iabuf[INET_ADDRSTRLEN];
++ int res;
++ ast_mutex_lock(&peerlock);
++ pack = data;
++ if (pack->retrans < 1) {
++ pack->retransid = -1;
++ if (!(pack->parent->flags & FLAG_ISQUAL))
++ ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
++ ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
++ ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
++ destroy_trans(pack->parent, 1);
++ res = 0;
++ } else {
++ /* Decrement retransmission, try again */
++ pack->retrans--;
++ dundi_xmit(pack);
++ res = 1;
++ }
++ ast_mutex_unlock(&peerlock);
++ return res;
++}
++
++static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
++{
++ struct dundi_packet *pack;
++ int res;
++ int len;
++ char eid_str[20];
++ len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
++ /* Reserve enough space for encryption */
++ if (trans->flags & FLAG_ENCRYPT)
++ len += 384;
++ pack = malloc(len);
++ if (pack) {
++ memset(pack, 0, len);
++ pack->h = (struct dundi_hdr *)(pack->data);
++ if (cmdresp != DUNDI_COMMAND_ACK) {
++ pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
++ pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
++ pack->next = trans->packets;
++ trans->packets = pack;
++ }
++ pack->parent = trans;
++ pack->h->strans = htons(trans->strans);
++ pack->h->dtrans = htons(trans->dtrans);
++ pack->h->iseqno = trans->iseqno;
++ pack->h->oseqno = trans->oseqno;
++ pack->h->cmdresp = cmdresp;
++ pack->datalen = sizeof(struct dundi_hdr);
++ if (ied) {
++ memcpy(pack->h->ies, ied->buf, ied->pos);
++ pack->datalen += ied->pos;
++ }
++ if (final) {
++ pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
++ trans->flags |= FLAG_FINAL;
++ }
++ pack->h->cmdflags = flags;
++ if (cmdresp != DUNDI_COMMAND_ACK) {
++ trans->oseqno++;
++ trans->oseqno = trans->oseqno % 256;
++ }
++ trans->aseqno = trans->iseqno;
++ /* If we have their public key, encrypt */
++ if (trans->flags & FLAG_ENCRYPT) {
++ switch(cmdresp) {
++ case DUNDI_COMMAND_REGREQ:
++ case DUNDI_COMMAND_REGRESPONSE:
++ case DUNDI_COMMAND_DPDISCOVER:
++ case DUNDI_COMMAND_DPRESPONSE:
++ case DUNDI_COMMAND_EIDQUERY:
++ case DUNDI_COMMAND_EIDRESPONSE:
++ case DUNDI_COMMAND_PRECACHERQ:
++ case DUNDI_COMMAND_PRECACHERP:
++ if (dundidebug)
++ dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
++ res = dundi_encrypt(trans, pack);
++ break;
++ default:
++ res = 0;
++ }
++ } else
++ res = 0;
++ if (!res)
++ res = dundi_xmit(pack);
++ if (res)
++ ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
++
++ if (cmdresp == DUNDI_COMMAND_ACK)
++ free(pack);
++ return res;
++ }
++ return -1;
++}
++
++static int do_autokill(void *data)
++{
++ struct dundi_transaction *trans = data;
++ char eid_str[20];
++ ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
++ dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
++ trans->autokillid = -1;
++ destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
++ return 0;
++}
++
++static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
++{
++ struct dundi_peer *p;
++ if (!dundi_eid_cmp(eid, us)) {
++ dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
++ return;
++ }
++ ast_mutex_lock(&peerlock);
++ p = peers;
++ while(p) {
++ if (!dundi_eid_cmp(&p->eid, eid)) {
++ if (has_permission(p->include, context))
++ dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
++ else
++ dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
++ break;
++ }
++ p = p->next;
++ }
++ if (!p)
++ dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
++ ast_mutex_unlock(&peerlock);
++}
++
++static int dundi_discover(struct dundi_transaction *trans)
++{
++ struct dundi_ie_data ied;
++ int x;
++ if (!trans->parent) {
++ ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
++ return -1;
++ }
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
++ if (!dundi_eid_zero(&trans->us_eid))
++ dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
++ for (x=0;x<trans->eidcount;x++)
++ dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
++ dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
++ dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
++ dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
++ if (trans->parent->cbypass)
++ dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
++ if (trans->autokilltimeout)
++ trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
++ return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
++}
++
++static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
++{
++ struct dundi_ie_data ied;
++ int x, res;
++ int max = 999999;
++ int expiration = DUNDI_DEFAULT_CACHE_TIME;
++ int ouranswers=0;
++ dundi_eid *avoid[1] = { NULL, };
++ int direct[1] = { 0, };
++ struct dundi_result dr[MAX_RESULTS];
++ struct dundi_hint_metadata hmd;
++ if (!trans->parent) {
++ ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
++ return -1;
++ }
++ memset(&hmd, 0, sizeof(hmd));
++ memset(&dr, 0, sizeof(dr));
++ /* Look up the answers we're going to include */
++ for (x=0;x<mapcount;x++)
++ ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
++ if (ouranswers < 0)
++ ouranswers = 0;
++ for (x=0;x<ouranswers;x++) {
++ if (dr[x].weight < max)
++ max = dr[x].weight;
++ }
++ if (max) {
++ /* If we do not have a canonical result, keep looking */
++ 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);
++ if (res > 0) {
++ /* Append answer in result */
++ ouranswers += res;
++ }
++ }
++
++ if (ouranswers > 0) {
++ *foundanswers += ouranswers;
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
++ if (!dundi_eid_zero(&trans->us_eid))
++ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
++ for (x=0;x<trans->eidcount;x++)
++ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
++ dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
++ dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
++ dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
++ for (x=0;x<ouranswers;x++) {
++ /* Add answers */
++ if (dr[x].expiration && (expiration > dr[x].expiration))
++ expiration = dr[x].expiration;
++ dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
++ }
++ dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
++ dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
++ if (trans->autokilltimeout)
++ trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
++ if (expiration < *minexp)
++ *minexp = expiration;
++ return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
++ } else {
++ /* Oops, nothing to send... */
++ destroy_trans(trans, 0);
++ return 0;
++ }
++}
++
++static int dundi_query(struct dundi_transaction *trans)
++{
++ struct dundi_ie_data ied;
++ int x;
++ if (!trans->parent) {
++ ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
++ return -1;
++ }
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
++ if (!dundi_eid_zero(&trans->us_eid))
++ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
++ for (x=0;x<trans->eidcount;x++)
++ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
++ dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
++ dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
++ dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
++ if (trans->autokilltimeout)
++ trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
++ return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
++}
++
++static int discover_transactions(struct dundi_request *dr)
++{
++ struct dundi_transaction *trans;
++ trans = dr->trans;
++ while(trans) {
++ dundi_discover(trans);
++ trans = trans->next;
++ }
++ return 0;
++}
++
++static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
++{
++ struct dundi_transaction *trans;
++ trans = dr->trans;
++ while(trans) {
++ precache_trans(trans, maps, mapcount, expiration, foundanswers);
++ trans = trans->next;
++ }
++ return 0;
++}
++
++static int query_transactions(struct dundi_request *dr)
++{
++ struct dundi_transaction *trans;
++ ast_mutex_lock(&peerlock);
++ trans = dr->trans;
++ while(trans) {
++ dundi_query(trans);
++ trans = trans->next;
++ }
++ ast_mutex_unlock(&peerlock);
++ return 0;
++}
++
++static int optimize_transactions(struct dundi_request *dr, int order)
++{
++ /* Minimize the message propagation through DUNDi by
++ alerting the network to hops which should be not be considered */
++ struct dundi_transaction *trans;
++ struct dundi_peer *peer;
++ dundi_eid tmp;
++ int x;
++ int needpush;
++ ast_mutex_lock(&peerlock);
++ trans = dr->trans;
++ while(trans) {
++ /* Pop off the true root */
++ if (trans->eidcount) {
++ tmp = trans->eids[--trans->eidcount];
++ needpush = 1;
++ } else {
++ tmp = trans->us_eid;
++ needpush = 0;
++ }
++
++ peer = peers;
++ while(peer) {
++ if (has_permission(peer->include, dr->dcontext) &&
++ dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
++ (peer->order <= order)) {
++ /* For each other transaction, make sure we don't
++ ask this EID about the others if they're not
++ already in the list */
++ if (!dundi_eid_cmp(&tmp, &peer->eid))
++ x = -1;
++ else {
++ for (x=0;x<trans->eidcount;x++) {
++ if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
++ break;
++ }
++ }
++ if (x == trans->eidcount) {
++ /* Nope not in the list, if needed, add us at the end since we're the source */
++ if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
++ trans->eids[trans->eidcount++] = peer->eid;
++ /* Need to insert the real root (or us) at the bottom now as
++ a requirement now. */
++ needpush = 1;
++ }
++ }
++ }
++ peer = peer->next;
++ }
++ /* If necessary, push the true root back on the end */
++ if (needpush)
++ trans->eids[trans->eidcount++] = tmp;
++ trans = trans->next;
++ }
++ ast_mutex_unlock(&peerlock);
++ return 0;
++}
++
++static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
++{
++ struct dundi_transaction *trans;
++ int x;
++ char eid_str[20];
++ char eid_str2[20];
++ /* Ignore if not registered */
++ if (!p->addr.sin_addr.s_addr)
++ return 0;
++ if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
++ return 0;
++ if (ast_strlen_zero(dr->number))
++ 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);
++ else
++ 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);
++ trans = create_transaction(p);
++ if (!trans)
++ return -1;
++ trans->next = dr->trans;
++ trans->parent = dr;
++ trans->ttl = ttl;
++ for (x=0;avoid[x] && (x <DUNDI_MAX_STACK);x++)
++ trans->eids[x] = *avoid[x];
++ trans->eidcount = x;
++ dr->trans = trans;
++ return 0;
++}
++
++static void cancel_request(struct dundi_request *dr)
++{
++ struct dundi_transaction *trans, *next;
++
++ ast_mutex_lock(&peerlock);
++ trans = dr->trans;
++
++ while(trans) {
++ next = trans->next;
++ /* Orphan transaction from request */
++ trans->parent = NULL;
++ trans->next = NULL;
++ /* Send final cancel */
++ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
++ trans = next;
++ }
++ ast_mutex_unlock(&peerlock);
++}
++
++static void abort_request(struct dundi_request *dr)
++{
++ ast_mutex_lock(&peerlock);
++ while(dr->trans)
++ destroy_trans(dr->trans, 0);
++ ast_mutex_unlock(&peerlock);
++}
++
++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[])
++{
++ struct dundi_peer *p;
++ int x;
++ int res;
++ int pass;
++ int allowconnect;
++ char eid_str[20];
++ ast_mutex_lock(&peerlock);
++ p = peers;
++ while(p) {
++ if (modeselect == 1) {
++ /* Send the precache to push upstreams only! */
++ pass = has_permission(p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
++ allowconnect = 1;
++ } else {
++ /* Normal lookup / EID query */
++ pass = has_permission(p->include, dr->dcontext);
++ allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
++ }
++ if (skip) {
++ if (!dundi_eid_cmp(skip, &p->eid))
++ pass = 0;
++ }
++ if (pass) {
++ if (p->order <= order) {
++ /* Check order first, then check cache, regardless of
++ omissions, this gets us more likely to not have an
++ affected answer. */
++ if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
++ res = 0;
++ /* Make sure we haven't already seen it and that it won't
++ affect our answer */
++ for (x=0;avoid[x];x++) {
++ if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
++ /* If not a direct connection, it affects our answer */
++ if (directs && !directs[x])
++ dr->hmd->flags &= ~DUNDI_HINT_UNAFFECTED;
++ break;
++ }
++ }
++ /* Make sure we can ask */
++ if (allowconnect) {
++ if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
++ /* Check for a matching or 0 cache entry */
++ append_transaction(dr, p, ttl, avoid);
++ } else
++ ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
++ }
++ }
++ *foundcache |= res;
++ } else if (!*skipped || (p->order < *skipped))
++ *skipped = p->order;
++ }
++ p = p->next;
++ }
++ ast_mutex_unlock(&peerlock);
++}
++
++static int register_request(struct dundi_request *dr, struct dundi_request **pending)
++{
++ struct dundi_request *cur;
++ int res=0;
++ char eid_str[20];
++ ast_mutex_lock(&peerlock);
++ cur = requests;
++ while(cur) {
++ if (option_debug)
++ ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
++ dr->dcontext, dr->number);
++ if (!strcasecmp(cur->dcontext, dr->dcontext) &&
++ !strcasecmp(cur->number, dr->number) &&
++ (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
++ ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n",
++ cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
++ *pending = cur;
++ res = 1;
++ break;
++ }
++ cur = cur->next;
++ }
++ if (!res) {
++ ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n",
++ dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
++ /* Go ahead and link us in since nobody else is searching for this */
++ dr->next = requests;
++ requests = dr;
++ *pending = NULL;
++ }
++ ast_mutex_unlock(&peerlock);
++ return res;
++}
++
++static void unregister_request(struct dundi_request *dr)
++{
++ struct dundi_request *cur, *prev;
++ ast_mutex_lock(&peerlock);
++ prev = NULL;
++ cur = requests;
++ while(cur) {
++ if (cur == dr) {
++ if (prev)
++ prev->next = cur->next;
++ else
++ requests = cur->next;
++ break;
++ }
++ prev = cur;
++ cur = cur->next;
++ }
++ ast_mutex_unlock(&peerlock);
++}
++
++static int check_request(struct dundi_request *dr)
++{
++ struct dundi_request *cur;
++ int res = 0;
++ ast_mutex_lock(&peerlock);
++ cur = requests;
++ while(cur) {
++ if (cur == dr) {
++ res = 1;
++ break;
++ }
++ cur = cur->next;
++ }
++ ast_mutex_unlock(&peerlock);
++ return res;
++}
++
++static unsigned long avoid_crc32(dundi_eid *avoid[])
++{
++ /* Idea is that we're calculating a checksum which is independent of
++ the order that the EID's are listed in */
++ unsigned long acrc32 = 0;
++ int x;
++ for (x=0;avoid[x];x++) {
++ /* Order doesn't matter */
++ if (avoid[x+1]) {
++ acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
++ }
++ }
++ return acrc32;
++}
++
++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[])
++{
++ int res;
++ struct dundi_request dr, *pending;
++ dundi_eid *rooteid=NULL;
++ int x;
++ int ttlms;
++ int ms;
++ int foundcache;
++ int skipped=0;
++ int order=0;
++ char eid_str[20];
++ struct timeval start;
++
++ /* Don't do anthing for a hungup channel */
++ if (chan && chan->_softhangup)
++ return 0;
++
++ ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
++
++ for (x=0;avoid[x];x++)
++ rooteid = avoid[x];
++ /* Now perform real check */
++ memset(&dr, 0, sizeof(dr));
++ if (pipe(dr.pfds)) {
++ ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
++ return -1;
++ }
++ dr.dr = result;
++ dr.hmd = hmd;
++ dr.maxcount = maxret;
++ dr.expiration = *expiration;
++ dr.cbypass = cbypass;
++ dr.crc32 = avoid_crc32(avoid);
++ strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
++ strncpy(dr.number, number, sizeof(dr.number) - 1);
++ if (rooteid)
++ dr.root_eid = *rooteid;
++ res = register_request(&dr, &pending);
++ if (res) {
++ /* Already a request */
++ if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
++ /* This is on behalf of someone else. Go ahead and close this out since
++ they'll get their answer anyway. */
++ ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
++ dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
++ close(dr.pfds[0]);
++ close(dr.pfds[1]);
++ return -2;
++ } else {
++ /* Wait for the cache to populate */
++ ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
++ dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
++ gettimeofday(&start, NULL);
++ while(check_request(pending) && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup)) {
++ /* XXX Would be nice to have a way to poll/select here XXX */
++ usleep(1);
++ }
++ /* Continue on as normal, our cache should kick in */
++ }
++ }
++ /* Create transactions */
++ do {
++ order = skipped;
++ skipped = 0;
++ foundcache = 0;
++ build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
++ } while (skipped && !foundcache && !dr.trans);
++ /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
++ do this earlier because we didn't know if we were going to have transactions
++ or not. */
++ if (!ttl) {
++ hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
++ abort_request(&dr);
++ unregister_request(&dr);
++ close(dr.pfds[0]);
++ close(dr.pfds[1]);
++ return 0;
++ }
++
++ /* Optimize transactions */
++ optimize_transactions(&dr, order);
++ /* Actually perform transactions */
++ discover_transactions(&dr);
++ /* Wait for transaction to come back */
++ gettimeofday(&start, NULL);
++ while(dr.trans && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup)) {
++ ms = 100;
++ ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
++ }
++ if (chan && chan->_softhangup)
++ ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
++ cancel_request(&dr);
++ unregister_request(&dr);
++ res = dr.respcount;
++ *expiration = dr.expiration;
++ close(dr.pfds[0]);
++ close(dr.pfds[1]);
++ return res;
++}
++
++int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
++{
++ struct dundi_hint_metadata hmd;
++ dundi_eid *avoid[1] = { NULL, };
++ int direct[1] = { 0, };
++ int expiration = DUNDI_DEFAULT_CACHE_TIME;
++ memset(&hmd, 0, sizeof(hmd));
++ hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
++ return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
++}
++
++static void reschedule_precache(const char *number, const char *context, int expiration)
++{
++ int len;
++ struct dundi_precache_queue *qe, *prev=NULL;
++ ast_mutex_lock(&pclock);
++ qe = pcq;
++ while(qe) {
++ if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
++ if (prev)
++ prev->next = qe->next;
++ else
++ pcq = qe->next;
++ qe->next = NULL;
++ break;
++ }
++ prev = qe;
++ qe = qe->next;
++ };
++ if (!qe) {
++ len = sizeof(struct dundi_precache_queue);
++ len += strlen(number) + 1;
++ len += strlen(context) + 1;
++ qe = malloc(len);
++ if (qe) {
++ memset(qe, 0, len);
++ strcpy(qe->number, number);
++ qe->context = qe->number + strlen(number) + 1;
++ strcpy(qe->context, context);
++ }
++ }
++ time(&qe->expiration);
++ qe->expiration += expiration;
++ prev = pcq;
++ if (prev) {
++ while(prev->next && (prev->next->expiration <= qe->expiration))
++ prev = prev->next;
++ qe->next = prev->next;
++ prev->next = qe;
++ } else
++ pcq = qe;
++ ast_mutex_unlock(&pclock);
++
++}
++
++static void dundi_precache_full(void)
++{
++ struct dundi_mapping *cur;
++ struct ast_context *con;
++ struct ast_exten *e;
++ cur = mappings;
++ while(cur) {
++ ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
++ ast_lock_contexts();
++ con = ast_walk_contexts(NULL);
++ while(con) {
++ if (!strcasecmp(cur->lcontext, ast_get_context_name(con))) {
++ /* Found the match, now queue them all up */
++ ast_lock_context(con);
++ e = ast_walk_context_extensions(con, NULL);
++ while(e) {
++ reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
++ e = ast_walk_context_extensions(con, e);
++ }
++ ast_unlock_context(con);
++ }
++ con = ast_walk_contexts(con);
++ }
++ ast_unlock_contexts();
++ cur = cur->next;
++ }
++}
++
++static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
++{
++ struct dundi_request dr;
++ struct dundi_hint_metadata hmd;
++ struct dundi_result dr2[MAX_RESULTS];
++ struct timeval start;
++ struct dundi_mapping *maps=NULL, *cur;
++ int nummaps;
++ int foundanswers;
++ int foundcache, skipped, ttlms, ms;
++ if (!context)
++ context = "e164";
++ ast_log(LOG_DEBUG, "Precache internal (%s@%s)!\n", number, context);
++
++ ast_mutex_lock(&peerlock);
++ nummaps = 0;
++ cur = mappings;
++ while(cur) {
++ if (!strcasecmp(cur->dcontext, context))
++ nummaps++;
++ cur = cur->next;
++ }
++ if (nummaps) {
++ maps = alloca(nummaps * sizeof(struct dundi_mapping));
++ nummaps = 0;
++ if (maps) {
++ cur = mappings;
++ while(cur) {
++ if (!strcasecmp(cur->dcontext, context))
++ maps[nummaps++] = *cur;
++ cur = cur->next;
++ }
++ }
++ }
++ ast_mutex_unlock(&peerlock);
++ if (!nummaps || !maps)
++ return -1;
++ ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
++ memset(&dr2, 0, sizeof(dr2));
++ memset(&dr, 0, sizeof(dr));
++ memset(&hmd, 0, sizeof(hmd));
++ dr.dr = dr2;
++ strncpy(dr.number, number, sizeof(dr.number) - 1);
++ strncpy(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext) - 1);
++ dr.maxcount = MAX_RESULTS;
++ dr.expiration = DUNDI_DEFAULT_CACHE_TIME;
++ dr.hmd = &hmd;
++ pipe(dr.pfds);
++ dr.pfds[0] = dr.pfds[1] = -1;
++ build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
++ optimize_transactions(&dr, 0);
++ foundanswers = 0;
++ precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
++ if (foundanswers) {
++ if (dr.expiration > 0)
++ reschedule_precache(dr.number, dr.dcontext, dr.expiration);
++ else
++ ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
++ }
++ gettimeofday(&start, NULL);
++ while(dr.trans && (calc_ms(&start) < ttlms)) {
++ if (dr.pfds[0] > -1) {
++ ms = 100;
++ ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
++ } else
++ usleep(1);
++ }
++ cancel_request(&dr);
++ if (dr.pfds[0] > -1) {
++ close(dr.pfds[0]);
++ close(dr.pfds[1]);
++ }
++ return 0;
++}
++
++int dundi_precache(const char *context, const char *number)
++{
++ dundi_eid *avoid[1] = { NULL, };
++ return dundi_precache_internal(context, number, dundi_ttl, avoid);
++}
++
++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[])
++{
++ int res;
++ struct dundi_request dr;
++ dundi_eid *rooteid=NULL;
++ int x;
++ int ttlms;
++ int skipped=0;
++ int foundcache=0;
++ struct timeval start;
++
++ ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
++
++ for (x=0;avoid[x];x++)
++ rooteid = avoid[x];
++ /* Now perform real check */
++ memset(&dr, 0, sizeof(dr));
++ dr.hmd = hmd;
++ dr.dei = dei;
++ strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
++ memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
++ if (rooteid)
++ dr.root_eid = *rooteid;
++ /* Create transactions */
++ build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
++
++ /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
++ do this earlier because we didn't know if we were going to have transactions
++ or not. */
++ if (!ttl) {
++ hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
++ return 0;
++ }
++
++ /* Optimize transactions */
++ optimize_transactions(&dr, 9999);
++ /* Actually perform transactions */
++ query_transactions(&dr);
++ /* Wait for transaction to come back */
++ gettimeofday(&start, NULL);
++ while(dr.trans && (calc_ms(&start) < ttlms))
++ usleep(1);
++ res = dr.respcount;
++ return res;
++}
++
++int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
++{
++ dundi_eid *avoid[1] = { NULL, };
++ struct dundi_hint_metadata hmd;
++ memset(&hmd, 0, sizeof(hmd));
++ return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
++}
++
++static int dundi_lookup_exec(struct ast_channel *chan, void *data)
++{
++ char *tmp;
++ char *context;
++ char *opts;
++ int res = -1;
++ struct localuser *u;
++
++ if (!data || !strlen(data)) {
++ ast_log(LOG_WARNING, "DUNDiLookup requires an argument (number)\n");
++ return 0;
++ }
++ LOCAL_USER_ADD(u);
++ tmp = ast_strdupa(data);
++ if (tmp) {
++ context = strchr(tmp, '|');
++ if (context) {
++ *context = '\0';
++ context++;
++ opts = strchr(context, '|');
++ if (opts) {
++ *opts = '\0';
++ opts++;
++ }
++ } else
++ opts = NULL;
++ if (!context || !strlen(context))
++ context = "e164";
++ if (!opts)
++ opts = "";
++
++ }
++ LOCAL_USER_REMOVE(u);
++ return res;
++}
++
++
++static void mark_peers(void)
++{
++ struct dundi_peer *peer;
++ ast_mutex_lock(&peerlock);
++ peer = peers;
++ while(peer) {
++ peer->dead = 1;
++ peer = peer->next;
++ }
++ ast_mutex_unlock(&peerlock);
++}
++
++static void mark_mappings(void)
++{
++ struct dundi_mapping *map;
++ ast_mutex_lock(&peerlock);
++ map = mappings;
++ while(map) {
++ map->dead = 1;
++ map = map->next;
++ }
++ ast_mutex_unlock(&peerlock);
++}
++
++static void destroy_permissions(struct permission *p)
++{
++ struct permission *prev;
++ while(p) {
++ prev = p;
++ p = p->next;
++ free(prev);
++ }
++}
++
++static void destroy_peer(struct dundi_peer *peer)
++{
++ if (peer->registerid > -1)
++ ast_sched_del(sched, peer->registerid);
++ if (peer->regtrans)
++ destroy_trans(peer->regtrans, 0);
++ if (peer->keypending)
++ destroy_trans(peer->keypending, 0);
++ if (peer->qualifyid > -1)
++ ast_sched_del(sched, peer->qualifyid);
++ destroy_permissions(peer->permit);
++ destroy_permissions(peer->include);
++ free(peer);
++}
++
++static void destroy_map(struct dundi_mapping *map)
++{
++ free(map);
++}
++
++static void prune_peers(void)
++{
++ struct dundi_peer *peer, *prev, *next;
++ ast_mutex_lock(&peerlock);
++ peer = peers;
++ prev = NULL;
++ while(peer) {
++ next = peer->next;
++ if (peer->dead) {
++ if (prev)
++ prev->next = peer->next;
++ else
++ peers = peer->next;
++ destroy_peer(peer);
++ } else
++ prev = peer;
++ peer = next;
++ }
++ ast_mutex_unlock(&peerlock);
++}
++
++static void prune_mappings(void)
++{
++ struct dundi_mapping *map, *prev, *next;
++ ast_mutex_lock(&peerlock);
++ map = mappings;
++ prev = NULL;
++ while(map) {
++ next = map->next;
++ if (map->dead) {
++ if (prev)
++ prev->next = map->next;
++ else
++ mappings = map->next;
++ destroy_map(map);
++ } else
++ prev = map;
++ map = next;
++ }
++ ast_mutex_unlock(&peerlock);
++}
++
++static struct permission *append_permission(struct permission *p, char *s, int allow)
++{
++ struct permission *start;
++ start = p;
++ if (p) {
++ while(p->next)
++ p = p->next;
++ }
++ if (p) {
++ p->next = malloc(sizeof(struct permission) + strlen(s) + 1);
++ p = p->next;
++ } else {
++ p = malloc(sizeof(struct permission) + strlen(s) + 1);
++ }
++ if (p) {
++ memset(p, 0, sizeof(struct permission));
++ memcpy(p->name, s, strlen(s) + 1);
++ p->allow = allow;
++ }
++ return start ? start : p;
++}
++
++#define MAX_OPTS 128
++
++static void build_mapping(char *name, char *value)
++{
++ char *t, *fields[MAX_OPTS];
++ struct dundi_mapping *map;
++ int x;
++ int y;
++ t = ast_strdupa(value);
++ if (t) {
++ map = mappings;
++ while(map) {
++ /* Find a double match */
++ if (!strcasecmp(map->dcontext, name) &&
++ (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
++ (!value[strlen(map->lcontext)] ||
++ (value[strlen(map->lcontext)] == ','))))
++ break;
++ map = map->next;
++ }
++ if (!map) {
++ map = malloc(sizeof(struct dundi_mapping));
++ if (map) {
++ memset(map, 0, sizeof(struct dundi_mapping));
++ map->next = mappings;
++ mappings = map;
++ map->dead = 1;
++ }
++ }
++ if (map) {
++ map->options = 0;
++ memset(fields, 0, sizeof(fields));
++ x = 0;
++ while(t && x < MAX_OPTS) {
++ fields[x++] = t;
++ t = strchr(t, ',');
++ if (t) {
++ *t = '\0';
++ t++;
++ }
++ } /* Russell was here, arrrr! */
++ if ((x == 1) && ast_strlen_zero(fields[0])) {
++ /* Placeholder mapping */
++ strncpy(map->dcontext, name, sizeof(map->dcontext) - 1);
++ map->dead = 0;
++ } else if (x >= 4) {
++ strncpy(map->dcontext, name, sizeof(map->dcontext) - 1);
++ strncpy(map->lcontext, fields[0], sizeof(map->lcontext) - 1);
++ if ((sscanf(fields[1], "%i", &map->weight) == 1) && (map->weight >= 0) && (map->weight < 60000)) {
++ strncpy(map->dest, fields[3], sizeof(map->dest) - 1);
++ if ((map->tech = str2tech(fields[2]))) {
++ map->dead = 0;
++ }
++ } else {
++ ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
++ }
++ for (y=4;y<x;y++) {
++ if (!strcasecmp(fields[y], "nounsolicited"))
++ map->options |= DUNDI_FLAG_NOUNSOLICITED;
++ else if (!strcasecmp(fields[y], "nocomunsolicit"))
++ map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
++ else if (!strcasecmp(fields[y], "residential"))
++ map->options |= DUNDI_FLAG_RESIDENTIAL;
++ else if (!strcasecmp(fields[y], "commercial"))
++ map->options |= DUNDI_FLAG_COMMERCIAL;
++ else if (!strcasecmp(fields[y], "mobile"))
++ map->options |= DUNDI_FLAG_MOBILE;
++ else if (!strcasecmp(fields[y], "nopartial"))
++ map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
++ else
++ ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
++ }
++ } else
++ ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
++ }
++ }
++}
++
++static int do_register(void *data)
++{
++ struct dundi_ie_data ied;
++ struct dundi_peer *peer = data;
++ char eid_str[20];
++ char eid_str2[20];
++ /* Called with peerlock already held */
++ 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));
++ peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
++ /* Destroy old transaction if there is one */
++ if (peer->regtrans)
++ destroy_trans(peer->regtrans, 0);
++ peer->regtrans = create_transaction(peer);
++ if (peer->regtrans) {
++ peer->regtrans->flags |= FLAG_ISREG;
++ memset(&ied, 0, sizeof(ied));
++ dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
++ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
++ dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
++ dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
++
++ } else
++ 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));
++
++ return 0;
++}
++
++static int do_qualify(void *data)
++{
++ struct dundi_peer *peer;
++ peer = data;
++ peer->qualifyid = -1;
++ qualify_peer(peer, 0);
++ return 0;
++}
++
++static void qualify_peer(struct dundi_peer *peer, int schedonly)
++{
++ int when;
++ if (peer->qualifyid > -1)
++ ast_sched_del(sched, peer->qualifyid);
++ peer->qualifyid = -1;
++ if (peer->qualtrans)
++ destroy_trans(peer->qualtrans, 0);
++ peer->qualtrans = NULL;
++ if (peer->maxms > 0) {
++ when = 60000;
++ if (peer->lastms < 0)
++ when = 10000;
++ if (schedonly)
++ when = 5000;
++ peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
++ if (!schedonly)
++ peer->qualtrans = create_transaction(peer);
++ if (peer->qualtrans) {
++ gettimeofday(&peer->qualtx, NULL);
++ peer->qualtrans->flags |= FLAG_ISQUAL;
++ dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
++ }
++ }
++}
++static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
++{
++ char data[256];
++ char *c;
++ int port, expire;
++ char eid_str[20];
++ dundi_eid_to_str(eid_str, sizeof(eid_str), eid);
++ if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
++ c = strchr(data, ':');
++ if (c) {
++ *c = '\0';
++ c++;
++ if (sscanf(c, "%d:%d", &port, &expire) == 2) {
++ /* Got it! */
++ inet_aton(data, &peer->addr.sin_addr);
++ peer->addr.sin_family = AF_INET;
++ peer->addr.sin_port = htons(port);
++ peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
++ }
++ }
++ }
++}
++
++
++static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
++{
++ struct dundi_peer *peer;
++ struct ast_hostent he;
++ struct hostent *hp;
++ dundi_eid testeid;
++ int needregister=0;
++ char eid_str[20];
++
++ ast_mutex_lock(&peerlock);
++ peer = peers;
++ while(peer) {
++ if (!dundi_eid_cmp(&peer->eid, eid)) {
++ break;
++ }
++ peer = peer->next;
++ }
++ if (!peer) {
++ /* Add us into the list */
++ peer = malloc(sizeof(struct dundi_peer));
++ if (peer) {
++ memset(peer, 0, sizeof(struct dundi_peer));
++ peer->registerid = -1;
++ peer->registerexpire = -1;
++ peer->qualifyid = -1;
++ peer->addr.sin_family = AF_INET;
++ peer->addr.sin_port = htons(DUNDI_PORT);
++ populate_addr(peer, eid);
++ peer->next = peers;
++ peers = peer;
++ }
++ }
++ if (peer) {
++ peer->dead = 0;
++ peer->eid = *eid;
++ peer->us_eid = global_eid;
++ destroy_permissions(peer->permit);
++ destroy_permissions(peer->include);
++ peer->permit = NULL;
++ peer->include = NULL;
++ if (peer->registerid > -1)
++ ast_sched_del(sched, peer->registerid);
++ peer->registerid = -1;
++ while(v) {
++ if (!strcasecmp(v->name, "inkey")) {
++ strncpy(peer->inkey, v->value, sizeof(peer->inkey) - 1);
++ } else if (!strcasecmp(v->name, "outkey")) {
++ strncpy(peer->outkey, v->value, sizeof(peer->outkey) - 1);
++ } else if (!strcasecmp(v->name, "host")) {
++ if (!strcasecmp(v->value, "dynamic")) {
++ peer->dynamic = 1;
++ } else {
++ hp = ast_gethostbyname(v->value, &he);
++ if (hp) {
++ memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
++ peer->dynamic = 0;
++ } else {
++ ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
++ peer->dead = 1;
++ }
++ }
++ } else if (!strcasecmp(v->name, "ustothem")) {
++ if (!dundi_str_to_eid(&testeid, v->value))
++ peer->us_eid = testeid;
++ else
++ ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
++ } else if (!strcasecmp(v->name, "include")) {
++ peer->include = append_permission(peer->include, v->value, 1);
++ } else if (!strcasecmp(v->name, "permit")) {
++ peer->permit = append_permission(peer->permit, v->value, 1);
++ } else if (!strcasecmp(v->name, "noinclude")) {
++ peer->include = append_permission(peer->include, v->value, 0);
++ } else if (!strcasecmp(v->name, "deny")) {
++ peer->permit = append_permission(peer->permit, v->value, 0);
++ } else if (!strcasecmp(v->name, "register")) {
++ needregister = ast_true(v->value);
++ } else if (!strcasecmp(v->name, "order")) {
++ if (!strcasecmp(v->value, "primary"))
++ peer->order = 0;
++ else if (!strcasecmp(v->value, "secondary"))
++ peer->order = 1;
++ else if (!strcasecmp(v->value, "tertiary"))
++ peer->order = 2;
++ else if (!strcasecmp(v->value, "quartiary"))
++ peer->order = 3;
++ else {
++ 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);
++ }
++ } else if (!strcasecmp(v->name, "qualify")) {
++ if (!strcasecmp(v->value, "no")) {
++ peer->maxms = 0;
++ } else if (!strcasecmp(v->value, "yes")) {
++ peer->maxms = DEFAULT_MAXMS;
++ } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
++ ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
++ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
++ peer->maxms = 0;
++ }
++ } else if (!strcasecmp(v->name, "model")) {
++ if (!strcasecmp(v->value, "inbound"))
++ peer->model = DUNDI_MODEL_INBOUND;
++ else if (!strcasecmp(v->value, "outbound"))
++ peer->model = DUNDI_MODEL_OUTBOUND;
++ else if (!strcasecmp(v->value, "symmetric"))
++ peer->model = DUNDI_MODEL_SYMMETRIC;
++ else if (!strcasecmp(v->value, "none"))
++ peer->model = 0;
++ else {
++ ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
++ v->value, v->lineno);
++ }
++ } else if (!strcasecmp(v->name, "precache")) {
++ if (!strcasecmp(v->value, "inbound"))
++ peer->pcmodel = DUNDI_MODEL_INBOUND;
++ else if (!strcasecmp(v->value, "outbound"))
++ peer->pcmodel = DUNDI_MODEL_OUTBOUND;
++ else if (!strcasecmp(v->value, "symmetric"))
++ peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
++ else if (!strcasecmp(v->value, "none"))
++ peer->pcmodel = 0;
++ else {
++ ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
++ v->value, v->lineno);
++ }
++ }
++ v = v->next;
++ }
++ (*globalpcmode) |= peer->pcmodel;
++ if (!peer->model && !peer->pcmodel) {
++ ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
++ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ peer->dead = 1;
++ } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
++ ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
++ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ peer->dead = 1;
++ } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
++ ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
++ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ peer->dead = 1;
++ } else if (peer->include && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
++ ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
++ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ } else if (peer->permit && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
++ 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",
++ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
++ } else {
++ if (needregister) {
++ peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
++ }
++ qualify_peer(peer, 1);
++ }
++ }
++ ast_mutex_unlock(&peerlock);
++}
++
++static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
++{
++ struct dundi_result results[MAX_RESULTS];
++ int res;
++ int x;
++ int found = 0;
++ if (!strncasecmp(context, "macro-", 6)) {
++ if (!chan) {
++ ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
++ return -1;
++ }
++ /* If done as a macro, use macro extension */
++ if (!strcasecmp(exten, "s")) {
++ exten = pbx_builtin_getvar_helper(chan, "ARG1");
++ if (!exten || ast_strlen_zero(exten))
++ exten = chan->macroexten;
++ if (!exten || ast_strlen_zero(exten))
++ exten = chan->exten;
++ if (!exten || ast_strlen_zero(exten)) {
++ ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
++ return -1;
++ }
++ }
++ if (!data || ast_strlen_zero(data))
++ data = "e164";
++ } else {
++ if (!data || ast_strlen_zero(data))
++ data = context;
++ }
++ res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
++ for (x=0;x<res;x++) {
++ if (results[x].flags & flag)
++ found++;
++ }
++ if (found >= priority)
++ return 1;
++ return 0;
++}
++
++static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
++{
++ return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
++}
++
++static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
++{
++ return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
++}
++
++static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, int newstack, const char *data)
++{
++ struct dundi_result results[MAX_RESULTS];
++ int res;
++ int x=0;
++ char req[1024];
++ struct ast_app *dial;
++
++ if (!strncasecmp(context, "macro-", 6)) {
++ if (!chan) {
++ ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
++ return -1;
++ }
++ /* If done as a macro, use macro extension */
++ if (!strcasecmp(exten, "s")) {
++ exten = pbx_builtin_getvar_helper(chan, "ARG1");
++ if (!exten || ast_strlen_zero(exten))
++ exten = chan->macroexten;
++ if (!exten || ast_strlen_zero(exten))
++ exten = chan->exten;
++ if (!exten || ast_strlen_zero(exten)) {
++ ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
++ return -1;
++ }
++ }
++ if (!data || ast_strlen_zero(data))
++ data = "e164";
++ } else {
++ if (!data || ast_strlen_zero(data))
++ data = context;
++ }
++ res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
++ if (res > 0) {
++ sort_results(results, res);
++ for (x=0;x<res;x++) {
++ if (results[x].flags & DUNDI_FLAG_EXISTS) {
++ if (!--priority)
++ break;
++ }
++ }
++ }
++ if (x < res) {
++ /* Got a hit! */
++ snprintf(req, sizeof(req), "%s/%s", results[x].tech, results[x].dest);
++ dial = pbx_findapp("Dial");
++ if (dial)
++ res = pbx_exec(chan, dial, req, newstack);
++ } else
++ res = -1;
++ return res;
++}
++
++static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
++{
++ return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
++}
++
++static struct ast_switch dundi_switch =
++{
++ name: "DUNDi",
++ description: "DUNDi Discovered Dialplan Switch",
++ exists: dundi_exists,
++ canmatch: dundi_canmatch,
++ exec: dundi_exec,
++ matchmore: dundi_matchmore,
++};
++
++static int set_config(char *config_file, struct sockaddr_in* sin)
++{
++ struct ast_config *cfg;
++ struct ast_variable *v;
++ char *cat;
++ int format;
++ int x;
++ char hn[256];
++ struct ast_hostent he;
++ struct hostent *hp;
++ struct sockaddr_in sin2;
++ static int last_port = 0;
++ int globalpcmodel = 0;
++ dundi_eid testeid;
++
++ dundi_ttl = DUNDI_DEFAULT_TTL;
++ cfg = ast_load(config_file);
++
++
++ if (!cfg) {
++ ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
++ return -1;
++ }
++ ipaddr[0] = '\0';
++ if (!gethostname(hn, sizeof(hn))) {
++ hp = ast_gethostbyname(hn, &he);
++ if (hp) {
++ memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
++ ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin2.sin_addr);
++ } else
++ ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
++ } else
++ ast_log(LOG_WARNING, "Unable to get host name!\n");
++ ast_mutex_lock(&peerlock);
++ reset_global_eid();
++ global_storehistory = 0;
++ strncpy(secretpath, "dundi", sizeof(secretpath) - 1);
++ v = ast_variable_browse(cfg, "general");
++ while(v) {
++ if (!strcasecmp(v->name, "port")){
++ sin->sin_port = ntohs(atoi(v->value));
++ if(last_port==0){
++ last_port=sin->sin_port;
++ } else if(sin->sin_port != last_port)
++ ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
++ } else if (!strcasecmp(v->name, "bindaddr")) {
++ struct hostent *hp;
++ struct ast_hostent he;
++ hp = ast_gethostbyname(v->value, &he);
++ if (hp) {
++ memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
++ } else
++ ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
++ } else if (!strcasecmp(v->name, "authdebug")) {
++ authdebug = ast_true(v->value);
++ } else if (!strcasecmp(v->name, "ttl")) {
++ if ((sscanf(v->value, "%i", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
++ dundi_ttl = x;
++ } else {
++ ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
++ v->value, v->lineno, DUNDI_DEFAULT_TTL);
++ }
++ } else if (!strcasecmp(v->name, "autokill")) {
++ if (sscanf(v->value, "%i", &x) == 1) {
++ if (x >= 0)
++ global_autokilltimeout = x;
++ else
++ ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
++ } else if (ast_true(v->value)) {
++ global_autokilltimeout = DEFAULT_MAXMS;
++ } else {
++ global_autokilltimeout = 0;
++ }
++ } else if (!strcasecmp(v->name, "entityid")) {
++ if (!dundi_str_to_eid(&testeid, v->value))
++ global_eid = testeid;
++ else
++ ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
++ } else if (!strcasecmp(v->name, "tos")) {
++ if (sscanf(v->value, "%i", &format) == 1)
++ tos = format & 0xff;
++ else if (!strcasecmp(v->value, "lowdelay"))
++ tos = IPTOS_LOWDELAY;
++ else if (!strcasecmp(v->value, "throughput"))
++ tos = IPTOS_THROUGHPUT;
++ else if (!strcasecmp(v->value, "reliability"))
++ tos = IPTOS_RELIABILITY;
++#if !defined(__NetBSD__)
++ else if (!strcasecmp(v->value, "mincost"))
++ tos = IPTOS_MINCOST;
++#endif
++ else if (!strcasecmp(v->value, "none"))
++ tos = 0;
++ else
++#if defined(__NetBSD__)
++ ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno);
++#else
++ ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', or 'none'\n", v->lineno);
++#endif
++ } else if (!strcasecmp(v->name, "department")) {
++ strncpy(dept, v->value, sizeof(dept) - 1);
++ } else if (!strcasecmp(v->name, "organization")) {
++ strncpy(org, v->value, sizeof(org) - 1);
++ } else if (!strcasecmp(v->name, "locality")) {
++ strncpy(locality, v->value, sizeof(locality) - 1);
++ } else if (!strcasecmp(v->name, "stateprov")) {
++ strncpy(stateprov, v->value, sizeof(stateprov) - 1);
++ } else if (!strcasecmp(v->name, "country")) {
++ strncpy(country, v->value, sizeof(country) - 1);
++ } else if (!strcasecmp(v->name, "email")) {
++ strncpy(email, v->value, sizeof(email) - 1);
++ } else if (!strcasecmp(v->name, "phone")) {
++ strncpy(phone, v->value, sizeof(phone) - 1);
++ } else if (!strcasecmp(v->name, "storehistory")) {
++ global_storehistory = ast_true(v->value);
++ } else if (!strcasecmp(v->name, "mapserver")) {
++ hp = ast_gethostbyname(v->value, &he);
++ if (hp) {
++ memcpy(&map_addr.sin_addr, hp->h_addr, sizeof(map_addr.sin_addr));
++ if (option_verbose > 1)
++ ast_verbose(VERBOSE_PREFIX_2 "Using mapping server at %s\n", v->value);
++ } else {
++ memset(&map_addr.sin_addr, 0, sizeof(map_addr.sin_addr));
++ ast_log(LOG_WARNING, "Could not find host '%s' for mapping host\n", v->value);
++ }
++ } else if (!strcasecmp(v->name, "mappeers_per_pkt")) {
++ map_updates_per_pkt = atoi(v->value);
++ if(map_updates_per_pkt < 1 || map_updates_per_pkt > 45) {
++ ast_log(LOG_WARNING, "Map updates must be between 1 and 45 inclusive. Setting to 45.\n");
++ map_updates_per_pkt = 45;
++ }
++ } else if (!strcasecmp(v->name, "mapport")) {
++ if (option_verbose > 1)
++ ast_verbose(VERBOSE_PREFIX_2 "Using mapping server at port %d\n", atoi(v->value));
++ map_addr.sin_port = htons(atoi(v->value));
++ } else if (!strcasecmp(v->name, "mapinterval")) {
++ map_update_interval = atoi(v->value) * 1000;
++ if(map_update_interval < 5000 && map_update_interval != 0)
++ map_update_interval = 5000;
++ if (option_verbose > 1)
++ ast_verbose(VERBOSE_PREFIX_2 "Using mapping update interval of %d ms\n", map_update_interval);
++ } else if(!strcasecmp(v->name, "mapcontext")) {
++ strncpy(map_context, v->value, sizeof(map_context) - 1);
++ }
++ v = v->next;
++ }
++ ast_mutex_unlock(&peerlock);
++ mark_mappings();
++ v = ast_variable_browse(cfg, "mappings");
++ while(v) {
++ build_mapping(v->name, v->value);
++ v = v->next;
++ }
++ prune_mappings();
++ mark_peers();
++ cat = ast_category_browse(cfg, NULL);
++ while(cat) {
++ if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
++ /* Entries */
++ if (!dundi_str_to_eid(&testeid, cat))
++ build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
++ else
++ ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
++ }
++ cat = ast_category_browse(cfg, cat);
++ }
++ prune_peers();
++ ast_destroy(cfg);
++ load_password();
++
++ /* Schedule updates */
++ if(map_peering_sid > -1)
++ ast_sched_del(sched, map_peering_sid);
++ if(map_contact_sid > -1)
++ ast_sched_del(sched, map_contact_sid);
++ if(map_update_interval) {
++ map_peering_sid = ast_sched_add(sched, 1000, dundi_xmit_peering, 0);
++ map_contact_sid = ast_sched_add(sched, 1000, dundi_xmit_contact, 0);
++ }
++
++ if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
++ dundi_precache_full();
++
++ return 0;
++}
++
++int unload_module(void)
++{
++ int res;
++ STANDARD_HANGUP_LOCALUSERS;
++ ast_cli_unregister(&cli_debug);
++ ast_cli_unregister(&cli_store_history);
++ ast_cli_unregister(&cli_flush);
++ ast_cli_unregister(&cli_no_debug);
++ ast_cli_unregister(&cli_no_store_history);
++ ast_cli_unregister(&cli_show_peers);
++ ast_cli_unregister(&cli_show_entityid);
++ ast_cli_unregister(&cli_show_trans);
++ ast_cli_unregister(&cli_show_requests);
++ ast_cli_unregister(&cli_show_mappings);
++ ast_cli_unregister(&cli_show_precache);
++ ast_cli_unregister(&cli_show_peer);
++ ast_cli_unregister(&cli_lookup);
++ ast_cli_unregister(&cli_precache);
++ ast_cli_unregister(&cli_queryeid);
++ ast_unregister_switch(&dundi_switch);
++ res = ast_unregister_application(app);
++ return res;
++}
++
++int reload(void)
++{
++ struct sockaddr_in sin;
++ set_config("dundi.conf",&sin);
++ return 0;
++}
++
++int load_module(void)
++{
++ int res=0;
++ struct sockaddr_in sin;
++ char iabuf[INET_ADDRSTRLEN];
++
++ dundi_set_output(dundi_debug_output);
++ dundi_set_error(dundi_error_output);
++
++ /* Seed random number generator */
++ srand(time(NULL));
++
++ sin.sin_family = AF_INET;
++ sin.sin_port = ntohs(DUNDI_PORT);
++ sin.sin_addr.s_addr = INADDR_ANY;
++
++ /* INADDR_ANY should be 0.0.0.0 */
++ map_addr.sin_family = AF_INET;
++ map_addr.sin_port = ntohs(4525);
++ map_addr.sin_addr.s_addr = INADDR_ANY;
++ strncpy(map_context, "open-e164", sizeof(map_context) - 1);
++
++ /* Make a UDP socket */
++ io = io_context_create();
++ sched = sched_context_create();
++
++ if (!io || !sched) {
++ ast_log(LOG_ERROR, "Out of memory\n");
++ return -1;
++ }
++
++ ast_cli_register(&cli_debug);
++ ast_cli_register(&cli_store_history);
++ ast_cli_register(&cli_flush);
++ ast_cli_register(&cli_no_debug);
++ ast_cli_register(&cli_no_store_history);
++ ast_cli_register(&cli_show_peers);
++ ast_cli_register(&cli_show_entityid);
++ ast_cli_register(&cli_show_trans);
++ ast_cli_register(&cli_show_requests);
++ ast_cli_register(&cli_show_mappings);
++ ast_cli_register(&cli_show_precache);
++ ast_cli_register(&cli_show_peer);
++ ast_cli_register(&cli_lookup);
++ ast_cli_register(&cli_precache);
++ ast_cli_register(&cli_queryeid);
++ if (ast_register_switch(&dundi_switch))
++ ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
++
++ set_config("dundi.conf",&sin);
++
++ netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
++
++ if (netsocket < 0) {
++ ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
++ return -1;
++ }
++ if (bind(netsocket,(struct sockaddr *)&sin, sizeof(sin))) {
++ 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));
++ return -1;
++ }
++
++ if (option_verbose > 1)
++ ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
++
++ if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)))
++ ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
++
++ if (!res) {
++ res = start_network_thread();
++ if (option_verbose > 1)
++ 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));
++ } else {
++ ast_log(LOG_ERROR, "Unable to start network thread\n");
++ close(netsocket);
++ }
++ res = ast_register_application(app, dundi_lookup_exec, synopsis, descrip);
++
++ return 0;
++}
++
++char *description(void)
++{
++ return tdesc;
++}
++
++int usecount(void)
++{
++ int res;
++ /* XXX DUNDi cannot be unloaded XXX */
++ return 1;
++ STANDARD_USECOUNT(res);
++ return res;
++}
++
++char *key()
++{
++ return ASTERISK_GPL_KEY;
++}
+diff -ruN asterisk-1.0.7-orig/pbx.c asterisk-1.0.7-pbx_dundi/pbx.c
+--- asterisk-1.0.7-orig/pbx.c 2005-02-19 01:27:52.000000000 +0100
++++ asterisk-1.0.7-pbx_dundi/pbx.c 2005-06-02 20:21:37.000000000 +0200
+@@ -820,7 +820,7 @@
+ /*--- pbx_retrieve_variable: Support for Asterisk built-in variables and
+ functions in the dialplan
+ ---*/
+-static void pbx_substitute_variables_temp(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen)
++static void pbx_substitute_variables_temp(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
+ {
+ char *first,*second;
+ char tmpvar[80] = "";
+@@ -829,7 +829,6 @@
+ int offset,offset2;
+ struct ast_var_t *variables;
+ char *name, *num; /* for callerid name + num variables */
+- struct varshead *headp=NULL;
+
+ if (c)
+ headp=&c->varshead;
+@@ -854,7 +853,7 @@
+ if (!first)
+ first = tmpvar + strlen(tmpvar);
+ *first='\0';
+- pbx_substitute_variables_temp(c,tmpvar,ret,workspace,workspacelen - 1);
++ pbx_substitute_variables_temp(c,tmpvar,ret,workspace,workspacelen - 1, headp);
+ if (!(*ret))
+ return;
+ offset=atoi(first+1); /* The number of characters,
+@@ -993,7 +992,7 @@
+ strncpy(workspace, c->language, workspacelen - 1);
+ *ret = workspace;
+ } else {
+- if (c) {
++ if (headp) {
+ AST_LIST_TRAVERSE(headp,variables,entries) {
+ #if 0
+ ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",var,ast_var_name(variables));
+@@ -1040,7 +1039,7 @@
+ }
+ }
+
+-void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
++static void pbx_substitute_variables_helper_full(struct ast_channel *c, const char *cp1, char *cp2, int count, struct varshead *headp)
+ {
+ char *cp4;
+ const char *tmp, *whereweare;
+@@ -1131,7 +1130,7 @@
+
+ /* Retrieve variable value */
+ workspace[0] = '\0';
+- pbx_substitute_variables_temp(c,vars,&cp4, workspace, sizeof(workspace));
++ pbx_substitute_variables_temp(c,vars,&cp4, workspace, sizeof(workspace), headp);
+ if (cp4) {
+ length = strlen(cp4);
+ if (length > count)
+@@ -1206,6 +1205,16 @@
+ }
+ }
+
++void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
++{
++ pbx_substitute_variables_helper_full(c, cp1, cp2, count, NULL);
++}
++
++void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
++{
++ pbx_substitute_variables_helper_full(NULL, cp1, cp2, count, headp);
++}
++
+ static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e) {
+
+ memset(passdata, 0, datalen);