diff options
author | nico <nico@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2005-06-03 06:39:21 +0000 |
---|---|---|
committer | nico <nico@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2005-06-03 06:39:21 +0000 |
commit | 31240e2fe46457b6c14bd54c9cae5066593bf35e (patch) | |
tree | 185dcd57ec87ab34a070f438fa5afcd30c0eed8f | |
parent | 665fe42cf2e0af62de1259481cbb001106cf0ac8 (diff) |
Add pbx_dundi patch (thanks Greg Boehnlein).
Move "not recommended" codecs into subpackages.
git-svn-id: svn://svn.openwrt.org/openwrt/trunk/openwrt@1138 3c298f89-4303-0410-b956-a3cf2f4a3e73
-rw-r--r-- | package/asterisk/Config.in | 63 | ||||
-rw-r--r-- | package/asterisk/Makefile | 114 | ||||
-rw-r--r-- | package/asterisk/ipkg/asterisk-codec-ilbc.control | 7 | ||||
-rw-r--r-- | package/asterisk/ipkg/asterisk-codec-lpc10.control | 7 | ||||
-rw-r--r-- | package/asterisk/ipkg/asterisk-pbx-dundi.conffiles | 1 | ||||
-rw-r--r-- | package/asterisk/ipkg/asterisk-pbx-dundi.control | 7 | ||||
-rw-r--r-- | package/asterisk/patches/asterisk-1.0.7-pbx_dundi.patch | 6493 |
7 files changed, 6634 insertions, 58 deletions
diff --git a/package/asterisk/Config.in b/package/asterisk/Config.in index 27a10b89eb..a8c5d311c8 100644 --- a/package/asterisk/Config.in +++ b/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/package/asterisk/Makefile b/package/asterisk/Makefile index 5a4603bc9d..5c5dd7bf01 100644 --- a/package/asterisk/Makefile +++ b/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/package/asterisk/ipkg/asterisk-codec-ilbc.control b/package/asterisk/ipkg/asterisk-codec-ilbc.control new file mode 100644 index 0000000000..cc0dd61910 --- /dev/null +++ b/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/package/asterisk/ipkg/asterisk-codec-lpc10.control b/package/asterisk/ipkg/asterisk-codec-lpc10.control new file mode 100644 index 0000000000..fa7146e570 --- /dev/null +++ b/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/package/asterisk/ipkg/asterisk-pbx-dundi.conffiles b/package/asterisk/ipkg/asterisk-pbx-dundi.conffiles new file mode 100644 index 0000000000..ab116b5d18 --- /dev/null +++ b/package/asterisk/ipkg/asterisk-pbx-dundi.conffiles @@ -0,0 +1 @@ +/etc/asterisk/dundi.conf diff --git a/package/asterisk/ipkg/asterisk-pbx-dundi.control b/package/asterisk/ipkg/asterisk-pbx-dundi.control new file mode 100644 index 0000000000..1575b29f66 --- /dev/null +++ b/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/package/asterisk/patches/asterisk-1.0.7-pbx_dundi.patch b/package/asterisk/patches/asterisk-1.0.7-pbx_dundi.patch new file mode 100644 index 0000000000..f8066ba40c --- /dev/null +++ b/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); |