diff options
36 files changed, 2057 insertions, 8061 deletions
diff --git a/package/asterisk/Config.in b/package/asterisk/Config.in index 127bb15be..dad8cf7fe 100644 --- a/package/asterisk/Config.in +++ b/package/asterisk/Config.in @@ -95,7 +95,16 @@ config BR2_PACKAGE_ASTERISK_PGSQL select BR2_PACKAGE_LIBPQ help PostgreSQL modules for Asterisk - + +config BR2_PACKAGE_ASTERISK_SQLITE + prompt "asterisk-sqlite................. SQLite modules" + tristate + default m if CONFIG_DEVEL + depends BR2_PACKAGE_ASTERISK + select BR2_PACKAGE_LIBSQLITE + help + SQLite modules for Asterisk + config BR2_PACKAGE_ASTERISK_SOUNDS prompt "asterisk-sounds................. Sound files" tristate @@ -111,7 +120,7 @@ config BR2_PACKAGE_ASTERISK_VOICEMAIL depends BR2_PACKAGE_ASTERISK help Voicemail related modules for Asterisk - + config BR2_PACKAGE_ASTERISK_MINI prompt "asterisk-mini..................... Minimal open source PBX" tristate @@ -137,6 +146,5 @@ config BR2_PACKAGE_ASTERISK_MINI - pbx_config - res_features - res_musiconhold - endmenu diff --git a/package/asterisk/Makefile b/package/asterisk/Makefile index e670774f1..470315481 100644 --- a/package/asterisk/Makefile +++ b/package/asterisk/Makefile @@ -3,9 +3,9 @@ include $(TOPDIR)/rules.mk PKG_NAME:=asterisk -PKG_VERSION:=1.0.10 +PKG_VERSION:=1.2.1 PKG_RELEASE:=1 -PKG_MD5SUM:=f60f4c4edc36fa61dc55a5385fd82e71 +PKG_MD5SUM:=04657086791e80f319c0d728af705001 PKG_SOURCE_URL:=http://ftp.digium.com/pub/asterisk/ ftp://ftp.digium.com/pub/asterisk/ PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz @@ -19,6 +19,7 @@ $(eval $(call PKG_template,ASTERISK,asterisk,$(PKG_VERSION)-$(PKG_RELEASE),$(ARC $(eval $(call PKG_template,ASTERISK_MINI,asterisk-mini,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH))) $(eval $(call PKG_template,ASTERISK_MYSQL,asterisk-mysql,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH))) $(eval $(call PKG_template,ASTERISK_PGSQL,asterisk-pgsql,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH))) +$(eval $(call PKG_template,ASTERISK_SQLITE,asterisk-sqlite,$(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_CHAN_BLUETOOTH,asterisk-chan-bluetooth,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH))) @@ -30,47 +31,53 @@ $(eval $(call PKG_template,ASTERISK_CODEC_SPEEX,asterisk-codec-speex,$(PKG_VERSI $(eval $(call PKG_template,ASTERISK_PBX_DUNDI,asterisk-pbx-dundi,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH))) $(eval $(call PKG_template,ASTERISK_RES_AGI,asterisk-res-agi,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH))) -ifneq ($(DEVELOPER),) -APPS:=app_sql_mysql.so app_sql_postgres.so -MODS:=cdr_mysql.so cdr_pgsql.so -SPEEX:=codec_speex.so -else -APPS:= -MODS:= +EXTRA_CFLAGS := -I$(STAGING_DIR)/usr/include -I$(STAGING_DIR)/include +EXTRA_LDFLAGS := -L$(STAGING_DIR)/usr/lib -L$(STAGING_DIR)/lib + ifneq ($(BR2_PACKAGE_ASTERISK_MYSQL),) -APPS += app_sql_mysql.so -MODS += cdr_mysql.so +EXTRA_CFLAGS += -I$(STAGING_DIR)/usr/include/mysql +EXTRA_LDFLAGS += -L$(STAGING_DIR)/usr/lib/mysql +EXTRA_APP_MODULES += app_sql_mysql.so +EXTRA_CDR_MODULES += cdr_mysql.so +EXTRA_RES_MODULES += res_config_mysql.so endif ifneq ($(BR2_PACKAGE_ASTERISK_PGSQL),) -APPS += app_sql_postgres.so -MODS += cdr_pgsql.so +EXTRA_APP_MODULES += app_sql_postgres.so +EXTRA_CDR_MODULES += cdr_pgsql.so endif -ifneq ($(BR2_PACKAGE_ASTERISK_CODEC_SPEEX),) -SPEEX:=codec_speex.so +ifneq ($(BR2_PACKAGE_ASTERISK_SQLITE),) +EXTRA_CDR_MODULES += cdr_sqlite.so endif +ifneq ($(BR2_PACKAGE_ASTERISK_CODEC_SPEEX),) +EXTRA_CFLAGS += -I$(STAGING_DIR)/usr/include/speex +EXTRA_CODEC_MODULES += codec_speex.so endif $(PKG_BUILD_DIR)/.configured: touch $@ $(PKG_BUILD_DIR)/.built: - $(MAKE) -C "$(PKG_BUILD_DIR)/channels" \ - CC="$(HOSTCC)" \ - gentone $(MAKE) -C "$(PKG_BUILD_DIR)" \ - CC_FOR_BUILD="$(HOSTCC)" \ + CROSS_ARCH="Linux" \ + CROSS_COMPILE="$(TARGET_CROSS)" \ + CROSS_COMPILE_BIN="/void/" \ + CROSS_COMPILE_TARGET="/void/" \ $(TARGET_CONFIGURE_OPTS) \ + HOST_CC="$(HOSTCC)" \ OPTIMIZE="$(TARGET_CFLAGS)" \ PROC="$(ARCH)" \ - CFLAGS_EXTRA="-I$(STAGING_DIR)/usr/include -I$(STAGING_DIR)/usr/include/speex" \ - LDFLAGS_EXTRA="-L$(STAGING_DIR)/usr/lib -L$(STAGING_DIR)/usr/lib/mysql" \ - CRYPTO_LIBS="-L$(STAGING_DIR)/usr/lib -Wl,-Bstatic -lssl -lcrypto -Wl,-Bdynamic" \ - EXTRA_APPS="$(APPS)" \ - EXTRA_MODS="$(MODS)" \ - MODSPEEX="$(SPEEX)" - $(MAKE) -C $(PKG_BUILD_DIR) \ + DEBUG="" \ + OPTIONS="-DLOW_MEMORY -Dlinux" \ + NOCRYPTO="yes" \ + EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \ + EXTRA_LDFLAGS="$(EXTRA_LDFLAGS)" \ + EXTRA_APP_MODULES="$(EXTRA_APP_MODULES)" \ + EXTRA_CDR_MODULES="$(EXTRA_CDR_MODULES)" \ + EXTRA_CODEC_MODULES="$(EXTRA_CODEC_MODULES)" \ + EXTRA_RES_MODULES="$(EXTRA_RES_MODULES)" \ DESTDIR="$(PKG_INSTALL_DIR)" \ - install samples + ASTVARLIBDIR="/usr/lib/asterisk" \ + all install samples rm -f $(PKG_INSTALL_DIR)/etc/asterisk/*.old touch $@ @@ -164,6 +171,12 @@ $(IPKG_ASTERISK_PGSQL): $(RSTRIP) $(IDIR_ASTERISK_PGSQL) $(IPKG_BUILD) $(IDIR_ASTERISK_PGSQL) $(PACKAGE_DIR) +$(IPKG_ASTERISK_SQLITE): + install -d -m0755 $(IDIR_ASTERISK_SQLITE)/usr/lib/asterisk/modules + install -m0755 $(PKG_BUILD_DIR)/cdr/cdr_sqlite.so $(IDIR_ASTERISK_SQLITE)/usr/lib/asterisk/modules/ + $(RSTRIP) $(IDIR_ASTERISK_SQLITE) + $(IPKG_BUILD) $(IDIR_ASTERISK_SQLITE) $(PACKAGE_DIR) + $(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/ diff --git a/package/asterisk/files/modules.conf b/package/asterisk/files/modules.conf index eeda73f65..1d6f35400 100644 --- a/package/asterisk/files/modules.conf +++ b/package/asterisk/files/modules.conf @@ -7,12 +7,29 @@ [modules] autoload=yes ; +; Any modules that need to be loaded before the Asterisk core has been +; initialized (just after the logger has been initialized) can be loaded +; using 'preload'. This will frequently be needed if you wish to map all +; module configuration files into Realtime storage, since the Realtime +; driver will need to be loaded before the modules using those configuration +; files are initialized. +; +; An example of loading ODBC support would be: +;preload => res_odbc.so +;preload => res_config_odbc.so +; +noload => res_config_mysql.so ; +; ; load => res_features.so ; Call Parking Resource noload => res_indications.so ; Indications Configuration noload => res_monitor.so ; Call Monitoring Resource ; load => res_musiconhold.so ; Music On Hold Resource noload => cdr_csv.so ; Comma Separated Values CDR Backend +noload => cdr_custom.so ; Customizable Comma Separated Values CDR Backend noload => cdr_manager.so ; Asterisk Call Manager CDR Backend +noload => cdr_mysql.so ; MySQL CDR Backend +noload => cdr_pgsql.so ; PostgreSQL CDR Backend +noload => cdr_sqlite.so ; SQLite CDR Backend noload => chan_agent.so ; Agent Proxy Channel ; load => chan_iax2.so ; Inter Asterisk eXchange (Ver 2) ; load => chan_local.so ; Local Proxy Channel @@ -21,8 +38,11 @@ noload => codec_a_mu.so ; A-law and Mulaw direct Coder/Decoder noload => codec_adpcm.so ; Adaptive Differential PCM Coder/Decoder noload => codec_alaw.so ; A-law Coder/Decoder noload => codec_g726.so ; ITU G.726-32kbps G726 Transcoder -; load => codec_gsm.so ; GSM/PCM16 (signed linear) Codec Translat +; load => codec_gsm.so ; GSM/PCM16 (signed linear) Codec Translation ; load => codec_ulaw.so ; Mu-law Coder/Decoder +noload => codec_speex.so ; Speex/PCM16 (signed linear) Codec Translator +noload => format_au.so ; Sun Microsystems AU format (signed linear) +noload => format_g723.so ; G.723.1 Simple Timestamp File Format noload => format_g726.so ; Raw G.726 (16/24/32/40kbps) data noload => format_g729.so ; Raw G729 data ; load => format_gsm.so ; Raw GSM data @@ -34,21 +54,26 @@ noload => format_sln.so ; Raw Signed Linear Audio support (SLN) noload => format_vox.so ; Dialogic VOX (ADPCM) File Format ; load => format_wav.so ; Microsoft WAV format (8000hz Signed Line ; load => format_wav_gsm.so ; Microsoft WAV format (Proprietary GSM) -noload => app_alarmreceiver.so ; Alarm Receiver for Asterisk +noload => app_alarmreceiver.so ; Alarm Receiver Application noload => app_authenticate.so ; Authentication Application -noload => app_cdr.so ; Make sure asterisk doesn't save CDR for +noload => app_cdr.so ; Make sure asterisk doesn't save CDR noload => app_chanisavail.so ; Check if channel is available +noload => app_chanspy.so ; Listen in on any channel noload => app_controlplayback.so ; Control Playback Application noload => app_cut.so ; Cuts up variables -noload => app_db.so ; Database access functions for Asterisk e +noload => app_db.so ; Database access functions ; load => app_dial.so ; Dialing Application +noload => app_dictate.so ; Virtual Dictation Machine Application noload => app_directory.so ; Extension Directory -noload => app_disa.so ; DISA (Direct Inward System Access) Appli +noload => app_directed_pickup.so ; Directed Call Pickup Support +noload => app_disa.so ; DISA (Direct Inward System Access) Application +noload => app_dumpchan.so ; Dump channel variables Application ; load => app_echo.so ; Simple Echo Application noload => app_enumlookup.so ; ENUM Lookup noload => app_eval.so ; Reevaluates strings noload => app_exec.so ; Executes applications -noload => app_forkcdr.so ; Fork The CDR into 2 seperate entities. +noload => app_externalivr.so ; External IVR application interface +noload => app_forkcdr.so ; Fork The CDR into 2 seperate entities noload => app_getcpeid.so ; Get ADSI CPE ID noload => app_groupcount.so ; Group Management Routines noload => app_ices.so ; Encode and Stream via icecast and ices @@ -56,14 +81,18 @@ noload => app_image.so ; Image Transmission Application noload => app_lookupblacklist.so ; Look up Caller*ID name/number from black noload => app_lookupcidname.so ; Look up CallerID Name from local databas ; load => app_macro.so ; Extension Macros -; load => app_milliwatt.so ; Digital Milliwatt (mu-law) Test Applicat +noload => app_math.so ; A simple math Application +noload => app_md5.so ; MD5 checksum Application +; load => app_milliwatt.so ; Digital Milliwatt (mu-law) Test Application +noload => app_mixmonitor.so ; Record a call and mix the audio during the recording noload => app_parkandannounce.so ; Call Parking and Announce Application ; load => app_playback.so ; Trivial Playback Application noload => app_privacy.so ; Require phone number to be entered, if n -noload => app_qcall.so ; Call from Queue noload => app_queue.so ; True Call Queueing noload => app_random.so ; Random goto noload => app_read.so ; Read Variable Application +noload => app_readfile.so ; Read in a file +noload => app_realtime.so ; Realtime Data Lookup/Rewrite noload => app_record.so ; Trivial Record Application ; load => app_sayunixtime.so ; Say time noload => app_senddtmf.so ; Send DTMF digits Application @@ -72,10 +101,13 @@ noload => app_setcallerid.so ; Set CallerID Application noload => app_setcdruserfield.so ; CDR user field apps noload => app_setcidname.so ; Set CallerID Name noload => app_setcidnum.so ; Set CallerID Number +noload => app_setrndis.so ; Set RDNIS Number +noload => app_settransfercapability.so ; Set ISDN Transfer Capability noload => app_sms.so ; SMS/PSTN handler noload => app_softhangup.so ; Hangs up the requested channel -noload => app_striplsd.so ; Strip trailing digits -noload => app_substring.so ; (Deprecated) Save substring digits in a +noload => app_sql_mysql.so ; Simple MySQL Interface +noload => app_sql_postgres.so ; Simple PostgreSQL Interface +noload => app_stack.so ; Stack Routines noload => app_system.so ; Generic System() application noload => app_talkdetect.so ; Playback with Talk Detection noload => app_test.so ; Interface Test Application @@ -85,9 +117,18 @@ noload => app_url.so ; Send URL Applications noload => app_userevent.so ; Custom User Event Application ; load => app_verbose.so ; Send verbose output noload => app_waitforring.so ; Waits until first ring after time +noload => app_waitforsilence.so ; Wait For Silence Application +noload => app_while.so ; While Loops and Conditional Execution +noload => pbx_ael.so ; Asterisk Extension Language Compiler ; load => pbx_config.so ; Text Extension Configuration +noload => pbx_functions.so ; Builtin dialplan functions +noload => pbx_loopback.so ; Loopback Switch +noload => pbx_realtime.so ; Realtime Switch noload => pbx_spool.so ; Outgoing Spool Support noload => pbx_wilcalu.so ; Wil Cal U (Auto Dialer) +noload => func_callerid.so ; Caller ID related dialplan functions +noload => func_enum.so ; ENUM Functions +noload => func_uri.so ; URI encoding / decoding functions ; ; Module names listed in "global" section will have symbols globally diff --git a/package/asterisk/ipkg/asterisk-sqlite.control b/package/asterisk/ipkg/asterisk-sqlite.control new file mode 100644 index 000000000..e43651d1f --- /dev/null +++ b/package/asterisk/ipkg/asterisk-sqlite.control @@ -0,0 +1,5 @@ +Package: asterisk-sqlite +Priority: optional +Section: net +Description: SQLite modules for Asterisk +Depends: asterisk, libsqlite2 diff --git a/package/asterisk/ipkg/asterisk.conffiles b/package/asterisk/ipkg/asterisk.conffiles index db0496121..5f805dedb 100644 --- a/package/asterisk/ipkg/asterisk.conffiles +++ b/package/asterisk/ipkg/asterisk.conffiles @@ -2,6 +2,7 @@ /etc/asterisk/agents.conf /etc/asterisk/alarmreceiver.conf /etc/asterisk/cdr_manager.conf +/etc/asterisk/codecs.conf /etc/asterisk/enum.conf /etc/asterisk/extconfig.conf /etc/asterisk/extensions.conf diff --git a/package/asterisk/patches/asterisk-1.0.7-Makefile-apps.patch b/package/asterisk/patches/asterisk-1.0.7-Makefile-apps.patch deleted file mode 100644 index 9ac842e2c..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-Makefile-apps.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff -ruN asterisk-1.0.7-old/apps/Makefile asterisk-1.0.7-new/apps/Makefile ---- asterisk-1.0.7-old/apps/Makefile 2004-09-24 23:32:56.000000000 +0200 -+++ asterisk-1.0.7-new/apps/Makefile 2005-03-19 17:38:06.000000000 +0100 -@@ -35,13 +35,14 @@ - APPS+=app_intercom.so - endif - --#APPS+=app_sql_postgres.so -+# add extra apps -+APPS+=$(EXTRA_APPS) - #APPS+=app_sql_odbc.so - #APPS+=app_rpt.so - --APPS+=$(shell if [ -f /usr/include/linux/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi) --APPS+=$(shell if [ -f /usr/local/include/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi) --APPS+=$(shell if [ -f /usr/include/osp/osp.h ]; then echo "app_osplookup.so" ; fi) -+#APPS+=$(shell if [ -f /usr/include/linux/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi) -+#APPS+=$(shell if [ -f /usr/local/include/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi) -+#APPS+=$(shell if [ -f /usr/include/osp/osp.h ]; then echo "app_osplookup.so" ; fi) - - CFLAGS+=-fPIC - diff --git a/package/asterisk/patches/asterisk-1.0.7-Makefile-cdr.patch b/package/asterisk/patches/asterisk-1.0.7-Makefile-cdr.patch deleted file mode 100644 index 3484b920a..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-Makefile-cdr.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -ruN asterisk-1.0.7-old/cdr/Makefile asterisk-1.0.7-new/cdr/Makefile ---- asterisk-1.0.7-old/cdr/Makefile 2004-08-31 18:33:00.000000000 +0200 -+++ asterisk-1.0.7-new/cdr/Makefile 2005-03-19 17:38:06.000000000 +0100 -@@ -12,7 +12,7 @@ - # - - #ADD cdr_pgsql.so to MODS= to include PostgreSQL support: REQUIRES PostgreSQL libs --MODS=cdr_csv.so cdr_manager.so -+MODS=cdr_csv.so cdr_manager.so $(EXTRA_MODS) - - - CFLAGS+=-fPIC diff --git a/package/asterisk/patches/asterisk-1.0.7-Makefile-channels-h323.patch b/package/asterisk/patches/asterisk-1.0.7-Makefile-channels-h323.patch deleted file mode 100644 index 8d8932875..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-Makefile-channels-h323.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -ruN asterisk-1.0.7-old/channels/h323/Makefile asterisk-1.0.7-new/channels/h323/Makefile ---- asterisk-1.0.7-old/channels/h323/Makefile 2004-11-15 03:35:35.000000000 +0100 -+++ asterisk-1.0.7-new/channels/h323/Makefile 2005-03-19 17:38:06.000000000 +0100 -@@ -22,9 +22,11 @@ - OSARCH=$(shell uname -s) - ifneq (${OSARCH},FreeBSD) - ifneq (${OSARCH},NetBSD) -+ifneq ($(PROC),mipsel) - CFLAGS += -march=$(shell uname -m) - endif - endif -+endif - CFLAGS += -DPBYTE_ORDER=PLITTLE_ENDIAN - - ifeq (${OSARCH},Linux) -@@ -47,7 +49,7 @@ - - # Pre Janus release directives - CFLAGS += -DNDEBUG -DDO_CRASH -DDEBUG_THREADS --CFLAGS += -pipe -Wall -fPIC -+CFLAGS += -pipe -Wall -fPIC $(OPTIMIZE) - ifeq (${OSARCH},Linux) - CFLAGS += -DP_LINUX - LIBS+=-lpthread -@@ -74,7 +76,7 @@ - - - libchanh323.a: ast_h323.o -- ar cr libchanh323.a ast_h323.o -+ $(AR) cr libchanh323.a ast_h323.o - - ast_h323.o: ast_h323.cpp - $(CXX) -g -c -fno-rtti -o $@ $(CFLAGS) $< diff --git a/package/asterisk/patches/asterisk-1.0.7-Makefile-channels.patch b/package/asterisk/patches/asterisk-1.0.7-Makefile-channels.patch deleted file mode 100644 index d1395107e..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-Makefile-channels.patch +++ /dev/null @@ -1,61 +0,0 @@ -diff -ruN asterisk-1.0.7-old/channels/Makefile asterisk-1.0.7-new/channels/Makefile ---- asterisk-1.0.7-old/channels/Makefile 2004-08-31 18:33:00.000000000 +0200 -+++ asterisk-1.0.7-new/channels/Makefile 2005-03-19 17:38:06.000000000 +0100 -@@ -71,25 +71,25 @@ - CHANNEL_LIBS+=chan_oss.so - endif - --CHANNEL_LIBS+=$(shell [ -f /usr/include/linux/ixjuser.h ] && echo chan_phone.so) --CHANNEL_LIBS+=$(shell [ -f /usr/local/include/ixjuser.h ] && echo chan_phone.so) --CHANNEL_LIBS+=$(shell [ -f h323/libchanh323.a ] && echo chan_h323.so) -- --CFLAGS+=-Wno-missing-prototypes -Wno-missing-declarations --CFLAGS+=$(shell [ ! -f /usr/include/linux/if_wanpipe.h ] && echo " -DOLD_SANGOMA_API") --CHANNEL_LIBS+=$(shell [ -f /usr/include/alsa/asoundlib.h ] && echo "chan_alsa.so") --CFLAGS+=$(shell [ -f /usr/lib/libpri.so.1 ] && echo " -DZAPATA_PRI") --CFLAGS+=$(shell [ -f /usr/lib/libmfcr2.so.1 ] && echo " -DZAPATA_R2") --CFLAGS+=$(shell [ -f alsa-monitor.h ] && echo " -DALSA_MONITOR") --ZAPPRI=$(shell [ -f /usr/lib/libpri.so.1 ] && echo "-lpri") --ZAPR2=$(shell [ -f /usr/lib/libmfcr2.so.1 ] && echo "-lmfcr2") --CFLAGS+=$(shell [ -f /usr/include/linux/zaptel.h ] && echo "-DIAX_TRUNKING") --CFLAGS+=$(shell [ -f /usr/local/include/zaptel.h ] && echo "-DIAX_TRUNKING") --CHANNEL_LIBS+=$(shell [ -f /usr/include/vpbapi.h ] && echo "chan_vpb.so" ) --CFLAGS+=$(shell [ -f /usr/include/vpbapi.h ] && echo " -DLINUX") -+#CHANNEL_LIBS+=$(shell [ -f /usr/include/linux/ixjuser.h ] && echo chan_phone.so) -+#CHANNEL_LIBS+=$(shell [ -f /usr/local/include/ixjuser.h ] && echo chan_phone.so) -+#CHANNEL_LIBS+=$(shell [ -f h323/libchanh323.a ] && echo chan_h323.so) -+ -+#CFLAGS+=-Wno-missing-prototypes -Wno-missing-declarations -+#CFLAGS+=$(shell [ ! -f /usr/include/linux/if_wanpipe.h ] && echo " -DOLD_SANGOMA_API") -+#CHANNEL_LIBS+=$(shell [ -f /usr/include/alsa/asoundlib.h ] && echo "chan_alsa.so") -+#CFLAGS+=$(shell [ -f /usr/lib/libpri.so.1 ] && echo " -DZAPATA_PRI") -+#CFLAGS+=$(shell [ -f /usr/lib/libmfcr2.so.1 ] && echo " -DZAPATA_R2") -+#CFLAGS+=$(shell [ -f alsa-monitor.h ] && echo " -DALSA_MONITOR") -+#ZAPPRI=$(shell [ -f /usr/lib/libpri.so.1 ] && echo "-lpri") -+#ZAPR2=$(shell [ -f /usr/lib/libmfcr2.so.1 ] && echo "-lmfcr2") -+#CFLAGS+=$(shell [ -f /usr/include/linux/zaptel.h ] && echo "-DIAX_TRUNKING") -+#CFLAGS+=$(shell [ -f /usr/local/include/zaptel.h ] && echo "-DIAX_TRUNKING") -+#CHANNEL_LIBS+=$(shell [ -f /usr/include/vpbapi.h ] && echo "chan_vpb.so" ) -+#CFLAGS+=$(shell [ -f /usr/include/vpbapi.h ] && echo " -DLINUX") - --ALSA_SRC=chan_alsa.c --ALSA_SRC+=$(shell [ -f alsa-monitor.h ] && echo "alsa-monitor.h") -+#ALSA_SRC=chan_alsa.c -+#ALSA_SRC+=$(shell [ -f alsa-monitor.h ] && echo "alsa-monitor.h") - - CFLAGS+=-DCRYPTO - CFLAGS+=-fPIC -@@ -106,10 +106,10 @@ - - ZAPDIR=/usr/lib - --CHANNEL_LIBS+=$(shell [ -f /usr/include/linux/zaptel.h ] && echo "chan_zap.so") --CHANNEL_LIBS+=$(shell [ -f /usr/local/include/zaptel.h ] && echo "chan_zap.so") -+#CHANNEL_LIBS+=$(shell [ -f /usr/include/linux/zaptel.h ] && echo "chan_zap.so") -+#CHANNEL_LIBS+=$(shell [ -f /usr/local/include/zaptel.h ] && echo "chan_zap.so") - --CHANNEL_LIBS+=$(shell [ -f /usr/include/nbs.h ] && echo "chan_nbs.so" ) -+#CHANNEL_LIBS+=$(shell [ -f /usr/include/nbs.h ] && echo "chan_nbs.so" ) - - ifndef OPENH323DIR - OPENH323DIR=$(HOME)/openh323 diff --git a/package/asterisk/patches/asterisk-1.0.7-Makefile-codecs-ilbc.patch b/package/asterisk/patches/asterisk-1.0.7-Makefile-codecs-ilbc.patch deleted file mode 100644 index 2d2fd3d2b..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-Makefile-codecs-ilbc.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -ruN asterisk-1.0.7-old/codecs/ilbc/Makefile asterisk-1.0.7-new/codecs/ilbc/Makefile ---- asterisk-1.0.7-old/codecs/ilbc/Makefile 2004-08-29 19:40:58.000000000 +0200 -+++ asterisk-1.0.7-new/codecs/ilbc/Makefile 2005-03-19 17:38:06.000000000 +0100 -@@ -1,5 +1,5 @@ - ARCH=$(PROC) --CFLAGS+=-Wall -fPIC -O3 -funroll-loops -fomit-frame-pointer -+CFLAGS+=-Wall -fPIC $(OPTIMIZE) - LIB=libilbc.a - - OBJS= anaFilter.o iCBSearch.o packing.o \ -@@ -12,8 +12,8 @@ - - - $(LIB): $(OBJS) -- ar cr $(LIB) $(OBJS) -- ranlib $(LIB) -+ $(AR) cr $(LIB) $(OBJS) -+ $(RANLIB) $(LIB) - - clean: - rm -f $(LIB) *.o diff --git a/package/asterisk/patches/asterisk-1.0.7-Makefile-codecs.patch b/package/asterisk/patches/asterisk-1.0.7-Makefile-codecs.patch deleted file mode 100644 index 1a854a06e..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-Makefile-codecs.patch +++ /dev/null @@ -1,69 +0,0 @@ -diff -ruN asterisk-1.0.7-old/codecs/Makefile asterisk-1.0.7-new/codecs/Makefile ---- asterisk-1.0.7-old/codecs/Makefile 2005-03-17 15:43:51.000000000 +0100 -+++ asterisk-1.0.7-new/codecs/Makefile 2005-03-22 23:26:20.000000000 +0100 -@@ -17,21 +17,21 @@ - # g723.1b) - # - #MODG723=codec_g723_1.so codec_g723_1b.so --MODG723=$(shell [ -f g723.1/coder.c ] && echo "codec_g723_1.so") --MODG723+=$(shell [ -f g723.1b/coder2.c ] && echo "codec_g723_1b.so") --MODSPEEX=$(shell [ -f /usr/include/speex.h ] || [ -f /usr/include/speex/speex.h ] || [ -f /usr/local/include/speex.h ] || [ -f /usr/local/include/speex/speex.h ] && echo "codec_speex.so") -+#MODG723=$(shell [ -f g723.1/coder.c ] && echo "codec_g723_1.so") -+#MODG723+=$(shell [ -f g723.1b/coder2.c ] && echo "codec_g723_1b.so") -+#MODSPEEX=$(shell [ -f /usr/include/speex.h ] || [ -f /usr/include/speex/speex.h ] || [ -f /usr/local/include/speex.h ] || [ -f /usr/local/include/speex/speex.h ] && echo "codec_speex.so") - MODILBC=$(shell [ -f ilbc/iLBC_decode.h ] && echo "codec_ilbc.so") - CFLAGS+=-fPIC --CFLAGS+=$(shell [ -f /usr/local/include/speex.h ] && echo "-I/usr/local/include") --CFLAGS+=$(shell [ -f /usr/local/include/speex/speex.h ] && echo "-I/usr/local/include/speex") --CFLAGS+=$(shell [ -f /usr/include/speex/speex.h ] && echo "-I/usr/include/speex") -+#CFLAGS+=$(shell [ -f /usr/local/include/speex.h ] && echo "-I/usr/local/include") -+#CFLAGS+=$(shell [ -f /usr/local/include/speex/speex.h ] && echo "-I/usr/local/include/speex") -+#CFLAGS+=$(shell [ -f /usr/include/speex/speex.h ] && echo "-I/usr/include/speex") - - LIBG723=g723.1/libg723.a - LIBG723B=g723.1b/libg723b.a - LIBGSM=gsm/lib/libgsm.a - LIBGSMT=gsm/lib/libgsm.a - LIBLPC10=lpc10/liblpc10.a --LIBSPEEX=$(shell [ -f /usr/local/lib/libspeex.a ] && echo "-L/usr/local/lib") -+LIBSPEEX=$(LDFLAGS_EXTRA) - LIBSPEEX+=-lspeex -lm - LIBILBC=ilbc/libilbc.a - -@@ -43,26 +43,26 @@ - - clean: - rm -f *.so *.o .depend -- ! [ -d g723.1 ] || $(MAKE) -C g723.1 clean -- ! [ -d g723.1b ] || $(MAKE) -C g723.1b clean -- $(MAKE) -C gsm clean -- $(MAKE) -C lpc10 clean -- $(MAKE) -C ilbc clean -+ ! [ -d g723.1 ] || $(MAKE) PROC=$(PROC) -C g723.1 clean -+ ! [ -d g723.1b ] || $(MAKE) PROC=$(PROC) -C g723.1b clean -+ $(MAKE) PROC=$(PROC) -C gsm clean -+ $(MAKE) PROC=$(PROC) -C lpc10 clean -+ $(MAKE) PROC=$(PROC) -C ilbc clean - - $(LIBG723): -- $(MAKE) -C g723.1 all -+ $(MAKE) PROC=$(PROC) -C g723.1 all - - gsm/lib/libgsm.a: -- $(MAKE) -C gsm lib/libgsm.a -+ $(MAKE) PROC=$(PROC) -C gsm lib/libgsm.a - - $(LIBG723B): -- $(MAKE) -C g723.1b all -+ $(MAKE) PROC=$(PROC) -C g723.1b all - - $(LIBLPC10): -- $(MAKE) -C lpc10 all -+ $(MAKE) PROC=$(PROC) -C lpc10 all - - $(LIBILBC): -- $(MAKE) -C ilbc all -+ $(MAKE) PROC=$(PROC) -C ilbc all - - codec_ilbc.so: codec_ilbc.o $(LIBILBC) - $(CC) $(SOLINK) -o $@ $< $(LIBILBC) diff --git a/package/asterisk/patches/asterisk-1.0.7-Makefile-res.patch b/package/asterisk/patches/asterisk-1.0.7-Makefile-res.patch deleted file mode 100644 index 0cd45801b..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-Makefile-res.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff -ruN asterisk-1.0.7-old/res/Makefile asterisk-1.0.7-new/res/Makefile ---- asterisk-1.0.7-old/res/Makefile 2004-07-17 22:58:01.000000000 +0200 -+++ asterisk-1.0.7-new/res/Makefile 2005-03-19 17:58:17.000000000 +0100 -@@ -13,9 +13,9 @@ - - MODS=res_adsi.so res_features.so res_crypto.so res_musiconhold.so res_indications.so res_monitor.so \ - res_agi.so --MODS+=$(shell if [ -f "/usr/include/odbcinst.h" ]; then echo "res_odbc.so res_config_odbc.so"; fi) --MODS+=$(shell if [ -f "/usr/local/include/odbcinst.h" ]; then echo "res_odbc.so res_config_odbc.so"; fi) --MODS+=$(shell if [ -f "/usr/include/osp/osp.h" ]; then echo "res_osp.so"; fi) -+#MODS+=$(shell if [ -f "/usr/include/odbcinst.h" ]; then echo "res_odbc.so res_config_odbc.so"; fi) -+#MODS+=$(shell if [ -f "/usr/local/include/odbcinst.h" ]; then echo "res_odbc.so res_config_odbc.so"; fi) -+#MODS+=$(shell if [ -f "/usr/include/osp/osp.h" ]; then echo "res_osp.so"; fi) - - CRYPTO_LIBS=-lssl -lcrypto - diff --git a/package/asterisk/patches/asterisk-1.0.7-Makefile.patch b/package/asterisk/patches/asterisk-1.0.7-Makefile.patch deleted file mode 100644 index 9c557f25a..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-Makefile.patch +++ /dev/null @@ -1,121 +0,0 @@ -diff -ruN asterisk-1.0.7-old/Makefile asterisk-1.0.7-new/Makefile ---- asterisk-1.0.7-old/Makefile 2005-03-10 09:15:05.000000000 +0100 -+++ asterisk-1.0.7-new/Makefile 2005-03-19 17:38:06.000000000 +0100 -@@ -63,10 +63,10 @@ - #K6OPT = -DK6OPT - - #Tell gcc to optimize the asterisk's code --OPTIMIZE+=-O6 -+#OPTIMIZE+=-O6 - - #Include debug symbols in the executables (-g) and profiling info (-pg) --DEBUG=-g #-pg -+#DEBUG=-g #-pg - - # If you are running a radio application, define RADIO_RELAX so that the DTMF - # will be received more reliably -@@ -74,7 +74,7 @@ - - # If you don't have a lot of memory (e.g. embedded Asterisk), uncomment the - # following to reduce the size of certain static buffers --#OPTIONS += -DLOW_MEMORY -+OPTIONS += -DLOW_MEMORY - - # Optional debugging parameters - DEBUG_THREADS = #-DDEBUG_THREADS #-DDO_CRASH -@@ -112,7 +112,7 @@ - BUSYDETECT+= #-DBUSYDETECT_COMPARE_TONE_AND_SILENCE - - ASTLIBDIR=$(INSTALL_PREFIX)/usr/lib/asterisk --ASTVARLIBDIR=$(INSTALL_PREFIX)/var/lib/asterisk -+ASTVARLIBDIR=$(INSTALL_PREFIX)/usr/lib/asterisk - ASTETCDIR=$(INSTALL_PREFIX)/etc/asterisk - ASTSPOOLDIR=$(INSTALL_PREFIX)/var/spool/asterisk - ASTLOGDIR=$(INSTALL_PREFIX)/var/log/asterisk -@@ -128,7 +128,7 @@ - - INCLUDE=-Iinclude -I../include - CFLAGS=-pipe -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) $(INCLUDE) -D_REENTRANT -D_GNU_SOURCE #-DMAKE_VALGRIND_HAPPY --CFLAGS+=$(OPTIMIZE) -+CFLAGS+=$(OPTIMIZE) $(CFLAGS_EXTRA) - - ifneq ($(PROC),ultrasparc) - CFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi) -@@ -187,7 +187,7 @@ - CFLAGS+=# -fomit-frame-pointer - SUBDIRS=res channels pbx apps codecs formats agi cdr astman stdtime - ifeq (${OSARCH},Linux) --LIBS=-ldl -lpthread -+LIBS=$(LDFLAGS_EXTRA) -ldl -lpthread - endif - LIBS+=-lncurses -lm - ifeq (${OSARCH},Linux) -@@ -205,7 +205,7 @@ - ifeq (${OSARCH},OpenBSD) - LIBS=-lcrypto -lpthread -lm -lncurses - endif --LIBS+=-lssl -+LIBS+=-Wl,-Bstatic -lssl -Wl,-Bdynamic - OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \ - translate.o file.o say.o pbx.o cli.o md5.o term.o \ - ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \ -@@ -240,12 +240,12 @@ - cd editline && unset CFLAGS LIBS && ./configure ; \ - - editline/libedit.a: FORCE -- cd editline && unset CFLAGS LIBS && test -f config.h || ./configure -- $(MAKE) -C editline libedit.a -+ cd editline && unset CFLAGS LIBS && test -f config.h || CFLAGS="$(OPTIMIZE) $(CFLAGS_EXTRA)" LDFLAGS="$(LDFLAGS_EXTRA)" ./configure -+ $(MAKE) PROC=$(PROC) CFLAGS="$(OPTIMIZE)" -C editline libedit.a - - db1-ast/libdb1.a: FORCE - @if [ -d db1-ast ]; then \ -- $(MAKE) -C db1-ast libdb1.a ; \ -+ $(MAKE) PROC=$(PROC) OORG="$(OPTIMIZE)" -C db1-ast libdb1.a ; \ - else \ - echo "You need to do a cvs update -d not just cvs update"; \ - exit 1; \ -@@ -289,7 +289,7 @@ - - stdtime/libtime.a: FORCE - @if [ -d stdtime ]; then \ -- $(MAKE) -C stdtime libtime.a ; \ -+ $(MAKE) PROC=$(PROC) -C stdtime libtime.a ; \ - else \ - echo "You need to do a cvs update -d not just cvs update"; \ - exit 1; \ -@@ -381,7 +381,7 @@ - if [ ! -f $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ]; then \ - install -m 755 contrib/scripts/safe_asterisk $(DESTDIR)$(ASTSBINDIR)/ ;\ - fi -- for x in $(SUBDIRS); do $(MAKE) -C $$x install || exit 1 ; done -+ for x in $(SUBDIRS); do $(MAKE) PROC=$(PROC) -C $$x install || exit 1 ; done - install -d $(DESTDIR)$(ASTHEADERDIR) - install -m 644 include/asterisk/*.h $(DESTDIR)$(ASTHEADERDIR) - rm -f $(DESTDIR)$(ASTVARLIBDIR)/sounds/vm -@@ -519,8 +519,8 @@ - __rpm: _version - rm -rf /tmp/asterisk ; \ - mkdir -p /tmp/asterisk/redhat/RPMS/i386 ; \ -- $(MAKE) DESTDIR=/tmp/asterisk install ; \ -- $(MAKE) DESTDIR=/tmp/asterisk samples ; \ -+ $(MAKE) PROC=$(PROC) DESTDIR=/tmp/asterisk install ; \ -+ $(MAKE) PROC=$(PROC) DESTDIR=/tmp/asterisk samples ; \ - mkdir -p /tmp/asterisk/etc/rc.d/init.d ; \ - cp -f redhat/asterisk /tmp/asterisk/etc/rc.d/init.d/ ; \ - sed "s/^Version:.*/Version: $(RPMVERSION)/g" redhat/asterisk.spec > asterisk.spec ; \ -@@ -544,12 +544,12 @@ - fi - - dont-optimize: -- $(MAKE) OPTIMIZE= K6OPT= install -+ $(MAKE) PROC=$(PROC) OPTIMIZE= K6OPT= install - - valgrind: dont-optimize - - depend: .depend -- for x in $(SUBDIRS); do $(MAKE) -C $$x depend || exit 1 ; done -+ for x in $(SUBDIRS); do $(MAKE) PROC=$(PROC) -C $$x depend || exit 1 ; done - - .depend: - @if ! which mpg123 &>/dev/null ; then \ diff --git a/package/asterisk/patches/asterisk-1.0.7-astdb-in-spool.patch b/package/asterisk/patches/asterisk-1.0.7-astdb-in-spool.patch deleted file mode 100644 index 13efe3bb6..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-astdb-in-spool.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -ruN asterisk-1.0.7-old/asterisk.h asterisk-1.0.7-new/asterisk.h ---- asterisk-1.0.7-old/asterisk.h 2004-09-07 17:02:53.000000000 +0200 -+++ asterisk-1.0.7-new/asterisk.h 2005-05-17 09:56:13.000000000 +0200 -@@ -27,7 +27,7 @@ - #define AST_LOG_DIR ASTLOGDIR - #define AST_AGI_DIR ASTAGIDIR - #define AST_KEY_DIR ASTVARLIBDIR "/keys" --#define AST_DB ASTVARLIBDIR "/astdb" -+#define AST_DB ASTSPOOLDIR "/astdb" - #define AST_TMP_DIR ASTSPOOLDIR "/tmp" - - #define AST_CONFIG_FILE ASTCONFPATH diff --git a/package/asterisk/patches/asterisk-1.0.7-no-crypto.diff b/package/asterisk/patches/asterisk-1.0.7-no-crypto.diff deleted file mode 100644 index 8d17f5a3d..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-no-crypto.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff -urN asterisk-1.0.7-old/res/Makefile asterisk-1.0.7-new/res/Makefile ---- asterisk-1.0.7-old/res/Makefile 2005-04-25 20:53:11.000000000 +0200 -+++ asterisk-1.0.7-new/res/Makefile 2005-04-25 20:53:26.000000000 +0200 -@@ -11,7 +11,7 @@ - # the GNU General Public License - # - --MODS=res_adsi.so res_features.so res_crypto.so res_musiconhold.so res_indications.so res_monitor.so \ -+MODS=res_adsi.so res_features.so res_musiconhold.so res_indications.so res_monitor.so \ - res_agi.so - #MODS+=$(shell if [ -f "/usr/include/odbcinst.h" ]; then echo "res_odbc.so res_config_odbc.so"; fi) - #MODS+=$(shell if [ -f "/usr/local/include/odbcinst.h" ]; then echo "res_odbc.so res_config_odbc.so"; fi) diff --git a/package/asterisk/patches/asterisk-1.0.7-no-newt.diff b/package/asterisk/patches/asterisk-1.0.7-no-newt.diff deleted file mode 100644 index b1638b3b1..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-no-newt.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff -urN asterisk-1.0.7-old/astman/Makefile asterisk-1.0.7-new/astman/Makefile ---- asterisk-1.0.7-old/astman/Makefile 2004-06-26 21:25:39.000000000 +0200 -+++ asterisk-1.0.7-new/astman/Makefile 2005-04-29 15:54:53.000000000 +0200 -@@ -8,7 +8,7 @@ - CFLAGS+=-I/usr/local/include -L/usr/local/lib - endif - --TARGET=$(shell if [ -f /usr/include/newt.h ]; then echo "astman"; else if [ -f /usr/local/include/newt.h ]; then echo "astman"; else echo "none" ; fi ; fi) -+TARGET=none - all: depend $(TARGET) - - install: diff --git a/package/asterisk/patches/asterisk-1.0.7-no_gtk.patch b/package/asterisk/patches/asterisk-1.0.7-no_gtk.patch deleted file mode 100644 index 9b99e8dd0..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-no_gtk.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff -ruN asterisk-1.0.7-old/pbx/Makefile asterisk-1.0.7-new/pbx/Makefile ---- asterisk-1.0.7-old/pbx/Makefile 2003-10-26 19:50:49.000000000 +0100 -+++ asterisk-1.0.7-new/pbx/Makefile 2005-07-04 15:42:52.000000000 +0200 -@@ -16,13 +16,13 @@ - PBX_LIBS=pbx_config.so pbx_wilcalu.so pbx_spool.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") -+#PBX_LIBS+=$(shell gtk-config --cflags >/dev/null 2>/dev/null && echo "pbx_gtkconsole.so") - # Add KDE Console if appropriate - #PBX_LIBS+=$(shell [ "$$QTDIR" != "" ] && echo "pbx_kdeconsole.so") - - --GTK_FLAGS=`gtk-config --cflags gthread` --GTK_LIBS=`gtk-config --libs gthread` -+#GTK_FLAGS=`gtk-config --cflags gthread` -+#GTK_LIBS=`gtk-config --libs gthread` - #CXX=egcs - MOC=$(QTDIR)/bin/moc - KDE_FLAGS=-I$(KDEDIR)/include -I$(KDEDIR)/include/kde -I$(QTDIR)/include diff --git a/package/asterisk/patches/asterisk-1.0.7-pbx_dundi.patch b/package/asterisk/patches/asterisk-1.0.7-pbx_dundi.patch deleted file mode 100644 index f8066ba40..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-pbx_dundi.patch +++ /dev/null @@ -1,6493 +0,0 @@ -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); diff --git a/package/asterisk/patches/asterisk-1.0.7-sipura-rtp.patch b/package/asterisk/patches/asterisk-1.0.7-sipura-rtp.patch deleted file mode 100644 index 58a2e6b12..000000000 --- a/package/asterisk/patches/asterisk-1.0.7-sipura-rtp.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -ruN asterisk-1.0.7-old/rtp.c asterisk-1.0.7-new/rtp.c ---- asterisk-1.0.7-old/rtp.c 2005-01-19 03:33:54.000000000 +0100 -+++ asterisk-1.0.7-new/rtp.c 2005-05-16 19:40:02.000000000 +0200 -@@ -1048,6 +1048,7 @@ - rtpheader[3] |= htonl((800)); - /* Set the End bit for the last 3 */ - rtpheader[3] |= htonl((1 << 23)); -+ rtpheader[1] = htonl(++(rtp->lastts)); /* added for Sipura SPA-3000 compat. */ - } else if ( x < 5) { - rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno++)); - } diff --git a/package/asterisk/patches/asterisk-1.0.9-Makefile-codecs-gsm.patch b/package/asterisk/patches/asterisk-1.0.9-Makefile-codecs-gsm.patch deleted file mode 100644 index 3030e780c..000000000 --- a/package/asterisk/patches/asterisk-1.0.9-Makefile-codecs-gsm.patch +++ /dev/null @@ -1,80 +0,0 @@ -diff -ruN asterisk-1.0.9-old/codecs/gsm/Makefile asterisk-1.0.9-new/codecs/gsm/Makefile ---- asterisk-1.0.9-old/codecs/gsm/Makefile 2005-06-21 16:27:28.000000000 +0200 -+++ asterisk-1.0.9-new/codecs/gsm/Makefile 2005-07-11 13:36:12.000000000 +0200 -@@ -40,12 +40,12 @@ - ifneq (${OSARCH},Darwin) - ifneq (${PROC},x86_64) - ifneq (${PROC},ultrasparc) --ifneq ($(shell uname -m),ppc) --ifneq ($(shell uname -m),alpha) --ifneq ($(shell uname -m),armv4l) -+ifneq (${PROC},alpha) -+ifneq (${PROC},armv4l) - ifneq (${PROC},sparc64) - ifneq (${PROC},ppc) - ifneq (${PROC},ppc64) -+ifneq (${PROC},mipsel) - OPTIMIZE+=-march=$(PROC) - endif - endif -@@ -84,7 +84,7 @@ - # CCFLAGS = -c -O - - CC ?= gcc --CCFLAGS += -c -DNeedFunctionPrototypes=1 -funroll-loops -fPIC $(OPTIMIZE) -fomit-frame-pointer -+CCFLAGS += -c -DNeedFunctionPrototypes=1 -fPIC $(OPTIMIZE) - - LD = $(CC) - -@@ -219,11 +219,12 @@ - $(SRC)/short_term.c \ - $(SRC)/table.c - ifeq (${OSARCH},Linux) --ifneq ($(shell uname -m),x86_64) --ifneq ($(shell uname -m),ppc) --ifneq ($(shell uname -m),alpha) --ifneq ($(shell uname -m),armv4l) --ifneq ($(shell uname -m),sparc64) -+ifneq (${PROC},x86_64) -+ifneq (${PROC},ppc) -+ifneq (${PROC},alpha) -+ifneq (${PROC},armv4l) -+ifneq (${PROC},sparc64) -+ifneq ($(PROC),mipsel) - GSM_SOURCES+= $(SRC)/k6opt.s - endif - endif -@@ -231,6 +232,7 @@ - endif - endif - endif -+endif - - TOAST_SOURCES = $(SRC)/toast.c \ - $(SRC)/toast_lin.c \ -@@ -277,11 +279,12 @@ - $(SRC)/table.o - - ifeq (${OSARCH},Linux) --ifneq ($(shell uname -m), x86_64) --ifneq ($(shell uname -m), ppc) --ifneq ($(shell uname -m), alpha) --ifneq ($(shell uname -m), armv4l) --ifneq ($(shell uname -m), sparc64) -+ifneq (${PROC}, x86_64) -+ifneq (${PROC}, ppc) -+ifneq (${PROC}, alpha) -+ifneq (${PROC}, armv4l) -+ifneq (${PROC}, sparc64) -+ifneq ($(PROC), mipsel) - GSM_OBJECTS+= $(SRC)/k6opt.o - endif - endif -@@ -289,6 +292,7 @@ - endif - endif - endif -+endif - - TOAST_OBJECTS = $(SRC)/toast.o \ - $(SRC)/toast_lin.o \ diff --git a/package/asterisk/patches/asterisk-1.0.9-chan_bluetooth.patch b/package/asterisk/patches/asterisk-1.0.9-chan_bluetooth.patch index f6a4cc495..7c278a71b 100644 --- a/package/asterisk/patches/asterisk-1.0.9-chan_bluetooth.patch +++ b/package/asterisk/patches/asterisk-1.0.9-chan_bluetooth.patch @@ -19,7 +19,7 @@ diff -ruN asterisk-1.0.9-old/channels/Makefile asterisk-1.0.9-new/channels/Makef $(CC) $(SOLINK) -o $@ $< h323/libchanh323.a $(CHANH323LIB) -L$(PWLIBDIR)/lib $(PTLIB) -L$(OPENH323DIR)/lib $(H323LIB) -L/usr/lib -lcrypto -lssl -lexpat +chan_bluetooth.so: chan_bluetooth.o -+ $(CC) $(SOLINK) -o $@ $< $(LDFLAGS_EXTRA) -lbluetooth ++ $(CC) $(SOLINK) -o $@ $< $(EXTRA_LDFLAGS) -lbluetooth + #chan_modem.so : chan_modem.o @@ -164,7 +164,7 @@ diff -ruN asterisk-1.0.9-old/channels/chan_bluetooth.c asterisk-1.0.9-new/channe +#define BLT_CONFIG_FILE "bluetooth.conf" +#define BLT_RDBUFF_MAX 1024 +#define BLT_DEFAULT_HCI_DEV 0 -+#define BLT_SVN_REVISION "$Rev: 38 $" ++#define BLT_SVN_REVISION "$Rev$" + +/* ---------------------------------- */ + diff --git a/package/asterisk/patches/asterisk-1.2.0-Makefile-apps.patch b/package/asterisk/patches/asterisk-1.2.0-Makefile-apps.patch new file mode 100644 index 000000000..7df38f764 --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-Makefile-apps.patch @@ -0,0 +1,34 @@ +diff -ruN asterisk-1.2.0-old/apps/Makefile asterisk-1.2.0-new/apps/Makefile +--- asterisk-1.2.0-old/apps/Makefile 2005-11-11 01:32:45.000000000 +0100 ++++ asterisk-1.2.0-new/apps/Makefile 2005-12-04 19:26:20.000000000 +0100 +@@ -83,6 +83,9 @@ + #CFLAGS+=-DEXTENDED_ODBC_STORAGE + # See doc/README.odbcstorage for more information + ++CFLAGS += $(EXTRA_CFLAGS) ++APPS += $(EXTRA_APP_MODULES) ++ + all: $(APPS) + + clean: +@@ -102,14 +105,17 @@ + app_curl.so: app_curl.o + $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(CURLLIBS) + ++app_sql_mysql.so: app_sql_mysql.o ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lmysqlclient ++ + app_sql_postgres.o: app_sql_postgres.c +- $(CC) -pipe -I/usr/local/pgsql/include $(CFLAGS) -c -o app_sql_postgres.o app_sql_postgres.c ++ $(CC) -pipe $(CFLAGS) -c -o app_sql_postgres.o app_sql_postgres.c + + app_sql_postgres.so: app_sql_postgres.o +- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -L/usr/local/pgsql/lib -lpq ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lpq + + app_sql_odbc.so: app_sql_odbc.o +- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lodbc ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lodbc + + look: look.c + $(CC) -pipe -O6 -g look.c -o look -lncurses diff --git a/package/asterisk/patches/asterisk-1.2.0-Makefile-cdr.patch b/package/asterisk/patches/asterisk-1.2.0-Makefile-cdr.patch new file mode 100644 index 000000000..7fc623ece --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-Makefile-cdr.patch @@ -0,0 +1,37 @@ +diff -ruN asterisk-1.2.0-old/cdr/Makefile asterisk-1.2.0-new/cdr/Makefile +--- asterisk-1.2.0-old/cdr/Makefile 2005-11-14 01:45:07.000000000 +0100 ++++ asterisk-1.2.0-new/cdr/Makefile 2005-12-04 22:22:43.000000000 +0100 +@@ -107,6 +107,9 @@ + MODS+=cdr_sqlite.so + endif + ++CFLAGS += $(EXTRA_CFLAGS) ++MODS += $(EXTRA_CDR_MODULES) ++ + all: depend $(MODS) + + install: all +@@ -123,16 +126,19 @@ + endif + + cdr_odbc.so: cdr_odbc.o +- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lodbc $(MLFLAGS) ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lodbc $(MLFLAGS) + + cdr_tds.so: cdr_tds.o +- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -ltds $(MLFLAGS) ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -ltds $(MLFLAGS) ++ ++cdr_mysql.so: cdr_pgsql.o ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lmysqlclient -lz $(MLFLAGS) + + cdr_pgsql.so: cdr_pgsql.o +- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lpq -lz $(MLFLAGS) ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lpq -lz $(MLFLAGS) + + cdr_sqlite.so: cdr_sqlite.o +- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lsqlite $(MLFLAGS) ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lsqlite $(MLFLAGS) + + depend: .depend + diff --git a/package/asterisk/patches/asterisk-1.2.0-Makefile-channels.patch b/package/asterisk/patches/asterisk-1.2.0-Makefile-channels.patch new file mode 100644 index 000000000..d5ac6ad7a --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-Makefile-channels.patch @@ -0,0 +1,22 @@ +diff -ruN asterisk-1.2.0-old/channels/Makefile asterisk-1.2.0-new/channels/Makefile +--- asterisk-1.2.0-old/channels/Makefile 2005-12-04 04:48:40.000000000 +0100 ++++ asterisk-1.2.0-new/channels/Makefile 2005-12-04 23:30:19.000000000 +0100 +@@ -155,6 +155,9 @@ + + #CFLAGS+=$(shell [ -f $(ZAPDIR)/libzap.a ] && echo "-I$(ZAPDIR)") + ++CFLAGS += $(EXTRA_CFLAGS) ++CHANNEL_LIBS += $(EXTRA_CHAN_MODULES) ++ + all: depend $(CHANNEL_LIBS) + + clean: +@@ -162,7 +165,7 @@ + rm -f busy.h ringtone.h gentone gentone-ulaw + + %.so : %.o +- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} ${LIBS} ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} + + ifneq ($(wildcard .depend),) + include .depend diff --git a/package/asterisk/patches/asterisk-1.2.0-Makefile-codecs-gsm.patch b/package/asterisk/patches/asterisk-1.2.0-Makefile-codecs-gsm.patch new file mode 100644 index 000000000..ec49daff2 --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-Makefile-codecs-gsm.patch @@ -0,0 +1,35 @@ +diff -ruN asterisk-1.2.0-old/codecs/gsm/Makefile asterisk-1.2.0-new/codecs/gsm/Makefile +--- asterisk-1.2.0-old/codecs/gsm/Makefile 2005-11-08 04:31:45.000000000 +0100 ++++ asterisk-1.2.0-new/codecs/gsm/Makefile 2005-12-04 13:31:50.000000000 +0100 +@@ -236,6 +236,7 @@ + ifneq ($(shell uname -m),armv4l) + ifneq ($(shell uname -m),sparc64) + ifneq (${PROC},arm) ++ifneq (${PROC},mipsel) + GSM_SOURCES+= $(SRC)/k6opt.s + endif + endif +@@ -245,6 +246,7 @@ + endif + endif + endif ++endif + + TOAST_SOURCES = $(SRC)/toast.c \ + $(SRC)/toast_lin.c \ +@@ -297,6 +299,7 @@ + ifneq ($(shell uname -m), alpha) + ifneq ($(shell uname -m), sparc64) + ifneq ($(shell uname -m), armv4l) ++ifneq (${PROC}, mipsel) + GSM_OBJECTS+= $(SRC)/k6opt.o + endif + endif +@@ -305,6 +308,7 @@ + endif + endif + endif ++endif + + TOAST_OBJECTS = $(SRC)/toast.o \ + $(SRC)/toast_lin.o \ diff --git a/package/asterisk/patches/asterisk-1.2.0-Makefile-codecs.patch b/package/asterisk/patches/asterisk-1.2.0-Makefile-codecs.patch new file mode 100644 index 000000000..f59c24c5d --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-Makefile-codecs.patch @@ -0,0 +1,13 @@ +diff -ruN asterisk-1.2.0-old/codecs/Makefile asterisk-1.2.0-new/codecs/Makefile +--- asterisk-1.2.0-old/codecs/Makefile 2005-11-08 05:13:18.000000000 +0100 ++++ asterisk-1.2.0-new/codecs/Makefile 2005-12-04 19:24:53.000000000 +0100 +@@ -72,6 +72,9 @@ + codec_adpcm.so codec_ulaw.so codec_alaw.so codec_a_mu.so \ + codec_g726.so + ++CFLAGS += $(EXTRA_CFLAGS) ++CODECS += $(EXTRA_CODEC_MODULES) ++ + all: depend $(CODECS) + + clean: diff --git a/package/asterisk/patches/asterisk-1.2.0-Makefile-pbx.patch b/package/asterisk/patches/asterisk-1.2.0-Makefile-pbx.patch new file mode 100644 index 000000000..f1d2a6331 --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-Makefile-pbx.patch @@ -0,0 +1,22 @@ +diff -ruN asterisk-1.2.0-old/pbx/Makefile asterisk-1.2.0-new/pbx/Makefile +--- asterisk-1.2.0-old/pbx/Makefile 2005-11-01 22:53:30.000000000 +0100 ++++ asterisk-1.2.0-new/pbx/Makefile 2005-12-04 19:23:48.000000000 +0100 +@@ -38,6 +38,9 @@ + + KDE_CONSOLE_OBJS=pbx_kdeconsole_main.o pbx_kdeconsole.o + ++CFLAGS += $(EXTRA_CFLAGS) ++PBX_LIBS += $(EXTRA_PBX_MODULES) ++ + all: depend $(PBX_LIBS) + + clean: +@@ -59,7 +62,7 @@ + $(CC) $(SOLINK) -o $@ $(KDE_CONSOLE_OBJS) $(KDE_LIBS) + + pbx_dundi.so: dundi-parser.o pbx_dundi.o +- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} pbx_dundi.o dundi-parser.o -lz ${CYGSOLIB} ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} pbx_dundi.o dundi-parser.o $(EXTRA_LDFLAGS) -lz ${CYGSOLIB} + + %.moc : %.h + $(MOC) $< -o $@ diff --git a/package/asterisk/patches/asterisk-1.2.0-Makefile-res.patch b/package/asterisk/patches/asterisk-1.2.0-Makefile-res.patch new file mode 100644 index 000000000..2f782cb1d --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-Makefile-res.patch @@ -0,0 +1,26 @@ +diff -ruN asterisk-1.2.0-old/res/Makefile asterisk-1.2.0-new/res/Makefile +--- asterisk-1.2.0-old/res/Makefile 2005-11-16 21:49:44.000000000 +0100 ++++ asterisk-1.2.0-new/res/Makefile 2005-12-04 19:18:15.000000000 +0100 +@@ -69,6 +69,9 @@ + CFLAGS+=-DOPENSSL_NO_KRB5 -fPIC + endif + ++CFLAGS += $(EXTRA_CFLAGS) ++MODS += $(EXTRA_RES_MODULES) ++ + all: depend $(MODS) + + install: all +@@ -112,6 +112,12 @@ + res_config_odbc.so: res_config_odbc.o + $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} ${CYG_RES_CONFIG_ODBC_LIB} + ++res_config_mysql.so: res_config_mysql.o ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lmysqlclient -lz ++ ++res_sqlite.so: res_sqlite.o ++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lsqlite ++ + ifneq ($(wildcard .depend),) + include .depend + endif diff --git a/package/asterisk/patches/asterisk-1.2.0-Makefile.patch b/package/asterisk/patches/asterisk-1.2.0-Makefile.patch new file mode 100644 index 000000000..2b4afa26e --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-Makefile.patch @@ -0,0 +1,54 @@ +diff -ruN asterisk-1.2.0-old/Makefile asterisk-1.2.0-new/Makefile +--- asterisk-1.2.0-old/Makefile 2005-11-16 21:23:53.000000000 +0100 ++++ asterisk-1.2.0-new/Makefile 2005-12-04 23:01:16.000000000 +0100 +@@ -341,16 +339,6 @@ + netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \ + cryptostub.o + +-ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/sys/poll.h),) +- OBJS+= poll.o +- ASTCFLAGS+=-DPOLLCOMPAT +-endif +- +-ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/dlfcn.h),) +- OBJS+= dlfcn.o +- ASTCFLAGS+=-DDLFCNCOMPAT +-endif +- + ifeq ($(OSARCH),Linux) + LIBS+=-ldl -lpthread -lncurses -lm -lresolv #-lnjamd + else +@@ -401,7 +389,9 @@ + HAVEDOT=no + endif + ++ifneq ($(NOCRYPTO),yes) + LIBS+=-lssl ++endif + + INSTALL=install + +@@ -430,12 +420,12 @@ + cd editline && unset CFLAGS LIBS && ./configure ; \ + + editline/libedit.a: FORCE +- cd editline && unset CFLAGS LIBS && test -f config.h || ./configure ++ cd editline && unset CFLAGS LIBS && test -f config.h || CFLAGS="$(OPTIMIZE) $(EXTRA_CFLAGS)" LDFLAGS="$(EXTRA_LDFLAGS)" ./configure + $(MAKE) -C editline libedit.a + + db1-ast/libdb1.a: FORCE + @if [ -d db1-ast ]; then \ +- $(MAKE) -C db1-ast libdb1.a ; \ ++ $(MAKE) OORG="$(OPTIMIZE)" -C db1-ast libdb1.a ; \ + else \ + echo "You need to do a cvs update -d not just cvs update"; \ + exit 1; \ +@@ -513,7 +503,7 @@ + fi + rm -f include/asterisk/build.h.tmp + $(CC) -c -o buildinfo.o $(CFLAGS) buildinfo.c +- $(CC) $(DEBUG) $(ASTOBJ) $(ASTLINK) $(OBJS) buildinfo.o $(LIBEDIT) db1-ast/libdb1.a stdtime/libtime.a $(LIBS) ++ $(CC) $(DEBUG) $(ASTOBJ) $(ASTLINK) $(OBJS) buildinfo.o $(LIBEDIT) db1-ast/libdb1.a stdtime/libtime.a $(EXTRA_LDFLAGS) $(LIBS) + + muted: muted.o + $(CC) $(AUDIO_LIBS) -o muted muted.o diff --git a/package/asterisk/patches/asterisk-1.2.0-app_mysql.patch b/package/asterisk/patches/asterisk-1.2.0-app_mysql.patch new file mode 100644 index 000000000..6f02e75a5 --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-app_mysql.patch @@ -0,0 +1,449 @@ +diff -ruN asterisk-1.2.0-old/apps/app_sql_mysql.c asterisk-1.2.0-new/apps/app_sql_mysql.c +--- asterisk-1.2.0-old/apps/app_sql_mysql.c 1970-01-01 01:00:00.000000000 +0100 ++++ asterisk-1.2.0-new/apps/app_sql_mysql.c 2005-06-07 18:36:28.000000000 +0200 +@@ -0,0 +1,445 @@ ++/* ++ * Asterisk -- A telephony toolkit for Linux. ++ * ++ * Connect to MySQL ++ * ++ * Copyright (C) 2004, Constantine Filin and Christos Ricudis ++ * ++ * Christos Ricudis <ricudis@itc.auth.gr> ++ * Constantine Filin <cf@intermedia.net> ++ * ++ * This program is free software, distributed under the terms of ++ * the GNU General Public License ++ */ ++ ++#include <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <stdlib.h> ++#include <sys/types.h> ++#include <stdio.h> ++#include <unistd.h> ++ ++#include <mysql.h> ++ ++#include <asterisk/file.h> ++#include <asterisk/logger.h> ++#include <asterisk/channel.h> ++#include <asterisk/pbx.h> ++#include <asterisk/module.h> ++#include <asterisk/linkedlists.h> ++#include <asterisk/chanvars.h> ++#include <asterisk/lock.h> ++ ++#define EXTRA_LOG 0 ++ ++static char *tdesc = "Simple Mysql Interface"; ++ ++static char *app = "MYSQL"; ++ ++static char *synopsis = "Do several mySQLy things"; ++ ++static char *descrip = ++"MYSQL(): Do several mySQLy things\n" ++"Syntax:\n" ++" MYSQL(Connect connid dhhost dbuser dbpass dbname)\n" ++" Connects to a database. Arguments contain standard MySQL parameters\n" ++" passed to function mysql_real_connect. Connection identifer returned\n" ++" in ${var}\n" ++" MYSQL(Query resultid ${connid} query-string)\n" ++" Executes standard MySQL query contained in query-string using established\n" ++" connection identified by ${connection_identifier}. Result of query is\n" ++" is stored in ${var}.\n" ++" MYSQL(Fetch fetchid ${resultid} var1 var2 ... varN)\n" ++" Fetches a single row from a result set contained in ${result_identifier}.\n" ++" Assigns returned fields to ${var1} ... ${varn}. ${fetchid} is set TRUE\n" ++" if additional rows exist in result set.\n" ++" MYSQL(Clear ${resultid})\n" ++" Frees memory and datastructures associated with result set.\n" ++" MYSQL(Disconnect ${connid})\n" ++" Disconnects from named connection to MySQL.\n" ++" On exit, always returns 0. Sets MYSQL_STATUS to 0 on success and -1 on error.\n"; ++ ++/* ++EXAMPLES OF USE : ++ ++exten => s,2,MYSQL(Connect connid localhost asterisk mypass credit) ++exten => s,3,MYSQL(Query resultid ${connid} SELECT username,credit FROM credit WHERE callerid=${CALLERIDNUM}) ++exten => s,4,MYSQL(Fetch fetchid ${resultid} datavar1 datavar2) ++exten => s,5,GotoIf(${fetchid}?6:8) ++exten => s,6,Festival("User ${datavar1} currently has credit balance of ${datavar2} dollars.") ++exten => s,7,Goto(s,4) ++exten => s,8,MYSQL(Clear ${resultid}) ++exten => s,9,MYSQL(Disconnect ${connid}) ++*/ ++ ++STANDARD_LOCAL_USER; ++LOCAL_USER_DECL; ++ ++AST_MUTEX_DEFINE_STATIC(_mysql_mutex); ++ ++#define AST_MYSQL_ID_DUMMY 0 ++#define AST_MYSQL_ID_CONNID 1 ++#define AST_MYSQL_ID_RESID 2 ++#define AST_MYSQL_ID_FETCHID 3 ++ ++struct ast_MYSQL_id { ++ int identifier_type; /* 0=dummy, 1=connid, 2=resultid */ ++ int identifier; ++ void *data; ++ AST_LIST_ENTRY(ast_MYSQL_id) entries; ++} *ast_MYSQL_id; ++ ++AST_LIST_HEAD(MYSQLidshead,ast_MYSQL_id) _mysql_ids_head; ++ ++/* helpful procs */ ++static void *find_identifier(int identifier,int identifier_type) { ++ struct MYSQLidshead *headp; ++ struct ast_MYSQL_id *i; ++ void *res=NULL; ++ int found=0; ++ ++ headp=&_mysql_ids_head; ++ ++ if (AST_LIST_LOCK(headp)) { ++ ast_log(LOG_WARNING,"Unable to lock identifiers list\n"); ++ } else { ++ AST_LIST_TRAVERSE(headp,i,entries) { ++ if ((i->identifier==identifier) && (i->identifier_type==identifier_type)) { ++ found=1; ++ res=i->data; ++ break; ++ } ++ } ++ if (!found) { ++ ast_log(LOG_WARNING,"Identifier %d, identifier_type %d not found in identifier list\n",identifier,identifier_type); ++ } ++ AST_LIST_UNLOCK(headp); ++ } ++ ++ return res; ++} ++ ++static int add_identifier(int identifier_type,void *data) { ++ struct ast_MYSQL_id *i,*j; ++ struct MYSQLidshead *headp; ++ int maxidentifier=0; ++ ++ headp=&_mysql_ids_head; ++ i=NULL; ++ j=NULL; ++ ++ if (AST_LIST_LOCK(headp)) { ++ ast_log(LOG_WARNING,"Unable to lock identifiers list\n"); ++ return(-1); ++ } else { ++ i=malloc(sizeof(struct ast_MYSQL_id)); ++ AST_LIST_TRAVERSE(headp,j,entries) { ++ if (j->identifier>maxidentifier) { ++ maxidentifier=j->identifier; ++ } ++ } ++ i->identifier=maxidentifier+1; ++ i->identifier_type=identifier_type; ++ i->data=data; ++ AST_LIST_INSERT_HEAD(headp,i,entries); ++ AST_LIST_UNLOCK(headp); ++ } ++ return i->identifier; ++} ++ ++static int del_identifier(int identifier,int identifier_type) { ++ struct ast_MYSQL_id *i; ++ struct MYSQLidshead *headp; ++ int found=0; ++ ++ headp=&_mysql_ids_head; ++ ++ if (AST_LIST_LOCK(headp)) { ++ ast_log(LOG_WARNING,"Unable to lock identifiers list\n"); ++ } else { ++ AST_LIST_TRAVERSE(headp,i,entries) { ++ if ((i->identifier==identifier) && ++ (i->identifier_type==identifier_type)) { ++ AST_LIST_REMOVE(headp,i,entries); ++ free(i); ++ found=1; ++ break; ++ } ++ } ++ AST_LIST_UNLOCK(headp); ++ } ++ ++ if (found==0) { ++ ast_log(LOG_WARNING,"Could not find identifier %d, identifier_type %d in list to delete\n",identifier,identifier_type); ++ return(-1); ++ } else { ++ return(0); ++ } ++} ++ ++static int set_asterisk_int(struct ast_channel *chan, char *varname, int id) { ++ if( id>=0 ) { ++ char s[100] = ""; ++ snprintf(s, sizeof(s)-1, "%d", id); ++#if EXTRA_LOG ++ ast_log(LOG_WARNING,"MYSQL: setting var '%s' to value '%s'\n",varname,s); ++#endif ++ pbx_builtin_setvar_helper(chan,varname,s); ++ } ++ return id; ++} ++ ++static int add_identifier_and_set_asterisk_int(struct ast_channel *chan, char *varname, int identifier_type, void *data) { ++ return set_asterisk_int(chan,varname,add_identifier(identifier_type,data)); ++} ++ ++static int safe_scan_int( char** data, char* delim, int def ) { ++ char* end; ++ int res = def; ++ char* s = strsep(data,delim); ++ if( s ) { ++ res = strtol(s,&end,10); ++ if (*end) res = def; /* not an integer */ ++ } ++ return res; ++} ++ ++/* MYSQL operations */ ++static int aMYSQL_connect(struct ast_channel *chan, char *data) { ++ ++ MYSQL *mysql; ++ ++ char *connid_var; ++ char *dbhost; ++ char *dbuser; ++ char *dbpass; ++ char *dbname; ++ ++ strsep(&data," "); // eat the first token, we already know it :P ++ ++ connid_var=strsep(&data," "); ++ dbhost=strsep(&data," "); ++ dbuser=strsep(&data," "); ++ dbpass=strsep(&data," "); ++ dbname=strsep(&data,"\n"); ++ ++ if( connid_var && dbhost && dbuser && dbpass && dbname ) { ++ mysql = mysql_init(NULL); ++ if (mysql) { ++ if (mysql_real_connect(mysql,dbhost,dbuser,dbpass,dbname,0,NULL,0)) { ++ add_identifier_and_set_asterisk_int(chan,connid_var,AST_MYSQL_ID_CONNID,mysql); ++ return 0; ++ } ++ else { ++ ast_log(LOG_WARNING,"mysql_real_connect(mysql,%s,%s,dbpass,%s,...) failed\n",dbhost,dbuser,dbname); ++ } ++ } ++ else { ++ ast_log(LOG_WARNING,"myslq_init returned NULL\n"); ++ } ++ } ++ else { ++ ast_log(LOG_WARNING,"MYSQL(connect is missing some arguments\n"); ++ } ++ ++ return -1; ++} ++ ++static int aMYSQL_query(struct ast_channel *chan, char *data) { ++ ++ MYSQL *mysql; ++ MYSQL_RES *mysqlres; ++ ++ char *resultid_var; ++ int connid; ++ char *querystring; ++ ++ strsep(&data," "); // eat the first token, we already know it :P ++ ++ resultid_var = strsep(&data," "); ++ connid = safe_scan_int(&data," ",-1); ++ querystring = strsep(&data,"\n"); ++ ++ if (resultid_var && (connid>=0) && querystring) { ++ if ((mysql=find_identifier(connid,AST_MYSQL_ID_CONNID))!=NULL) { ++ mysql_query(mysql,querystring); ++ if ((mysqlres=mysql_use_result(mysql))!=NULL) { ++ add_identifier_and_set_asterisk_int(chan,resultid_var,AST_MYSQL_ID_RESID,mysqlres); ++ return 0; ++ } ++ else if( mysql_field_count(mysql)==0 ) { ++ return 0; // See http://dev.mysql.com/doc/mysql/en/mysql_field_count.html ++ } ++ else { ++ ast_log(LOG_WARNING,"aMYSQL_query: mysql_store_result() failed on query %s\n",querystring); ++ } ++ } ++ else { ++ ast_log(LOG_WARNING,"aMYSQL_query: Invalid connection identifier %d passed in aMYSQL_query\n",connid); ++ } ++ } ++ else { ++ ast_log(LOG_WARNING,"aMYSQL_query: missing some arguments\n"); ++ } ++ ++ return -1; ++} ++ ++ ++static int aMYSQL_fetch(struct ast_channel *chan, char *data) { ++ ++ MYSQL_RES *mysqlres; ++ MYSQL_ROW mysqlrow; ++ ++ char *fetchid_var,*s5,*s6; ++ int resultid,numFields,j; ++ ++ strsep(&data," "); // eat the first token, we already know it :P ++ ++ fetchid_var = strsep(&data," "); ++ resultid = safe_scan_int(&data," ",-1); ++ ++ if (fetchid_var && (resultid>=0) ) { ++ if ((mysqlres=find_identifier(resultid,AST_MYSQL_ID_RESID))!=NULL) { ++ /* Grab the next row */ ++ if ((mysqlrow=mysql_fetch_row(mysqlres))!=NULL) { ++ numFields=mysql_num_fields(mysqlres); ++ for (j=0;j<numFields;j++) { ++ s5=strsep(&data," "); ++ if (s5==NULL) { ++ ast_log(LOG_WARNING,"ast_MYSQL_fetch: More fields (%d) than variables (%d)\n",numFields,j); ++ break; ++ } ++ s6=mysqlrow[j]; ++ pbx_builtin_setvar_helper(chan,s5, s6 ? s6 : "NULL"); ++ } ++#ifdef EXTRA_LOG ++ ast_log(LOG_WARNING,"ast_MYSQL_fetch: numFields=%d\n",numFields); ++#endif ++ set_asterisk_int(chan,fetchid_var,1); // try more rows ++ } else { ++#if EXTRA_LOG ++ ast_log(LOG_WARNING,"ast_MYSQL_fetch : EOF\n"); ++#endif ++ set_asterisk_int(chan,fetchid_var,0); // no more rows ++ } ++ return 0; ++ } ++ else { ++ ast_log(LOG_WARNING,"aMYSQL_fetch: Invalid result identifier %d passed\n",resultid); ++ } ++ } ++ else { ++ ast_log(LOG_WARNING,"aMYSQL_fetch: missing some arguments\n"); ++ } ++ ++ return -1; ++} ++ ++static int aMYSQL_clear(struct ast_channel *chan, char *data) { ++ ++ MYSQL_RES *mysqlres; ++ ++ int id; ++ strsep(&data," "); // eat the first token, we already know it :P ++ id = safe_scan_int(&data," \n",-1); ++ if ((mysqlres=find_identifier(id,AST_MYSQL_ID_RESID))==NULL) { ++ ast_log(LOG_WARNING,"Invalid result identifier %d passed in aMYSQL_clear\n",id); ++ } else { ++ mysql_free_result(mysqlres); ++ del_identifier(id,AST_MYSQL_ID_RESID); ++ } ++ ++ return 0; ++} ++ ++static int aMYSQL_disconnect(struct ast_channel *chan, char *data) { ++ ++ MYSQL *mysql; ++ int id; ++ strsep(&data," "); // eat the first token, we already know it :P ++ ++ id = safe_scan_int(&data," \n",-1); ++ if ((mysql=find_identifier(id,AST_MYSQL_ID_CONNID))==NULL) { ++ ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aMYSQL_disconnect\n",id); ++ } else { ++ mysql_close(mysql); ++ del_identifier(id,AST_MYSQL_ID_CONNID); ++ } ++ ++ return 0; ++} ++ ++static int MYSQL_exec(struct ast_channel *chan, void *data) ++{ ++ struct localuser *u; ++ int result; ++ char sresult[10]; ++ ++#if EXTRA_LOG ++ fprintf(stderr,"MYSQL_exec: data=%s\n",(char*)data); ++#endif ++ ++ if (!data) { ++ ast_log(LOG_WARNING, "APP_MYSQL requires an argument (see manual)\n"); ++ return -1; ++ } ++ ++ LOCAL_USER_ADD(u); ++ result=0; ++ ++ ast_mutex_lock(&_mysql_mutex); ++ ++ if (strncasecmp("connect",data,strlen("connect"))==0) { ++ result=aMYSQL_connect(chan,ast_strdupa(data)); ++ } else if (strncasecmp("query",data,strlen("query"))==0) { ++ result=aMYSQL_query(chan,ast_strdupa(data)); ++ } else if (strncasecmp("fetch",data,strlen("fetch"))==0) { ++ result=aMYSQL_fetch(chan,ast_strdupa(data)); ++ } else if (strncasecmp("clear",data,strlen("clear"))==0) { ++ result=aMYSQL_clear(chan,ast_strdupa(data)); ++ } else if (strncasecmp("disconnect",data,strlen("disconnect"))==0) { ++ result=aMYSQL_disconnect(chan,ast_strdupa(data)); ++ } else { ++ ast_log(LOG_WARNING, "Unknown argument to MYSQL application : %s\n",(char *)data); ++ result=-1; ++ } ++ ++ ast_mutex_unlock(&_mysql_mutex); ++ ++ LOCAL_USER_REMOVE(u); ++ snprintf(sresult, sizeof(sresult), "%d", result); ++ pbx_builtin_setvar_helper(chan, "MYSQL_STATUS", sresult); ++ return 0; ++} ++ ++int unload_module(void) ++{ ++ STANDARD_HANGUP_LOCALUSERS; ++ return ast_unregister_application(app); ++} ++ ++int load_module(void) ++{ ++ struct MYSQLidshead *headp = &_mysql_ids_head; ++ AST_LIST_HEAD_INIT(headp); ++ return ast_register_application(app, MYSQL_exec, synopsis, descrip); ++} ++ ++char *description(void) ++{ ++ return tdesc; ++} ++ ++int usecount(void) ++{ ++ int res; ++ STANDARD_USECOUNT(res); ++ return res; ++} ++ ++char *key() ++{ ++ return ASTERISK_GPL_KEY; ++} diff --git a/package/asterisk/patches/asterisk-1.2.0-cdr_mysql.patch b/package/asterisk/patches/asterisk-1.2.0-cdr_mysql.patch new file mode 100644 index 000000000..f4dc29fd7 --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-cdr_mysql.patch @@ -0,0 +1,522 @@ +diff -ruN asterisk-1.2.0-old/configs/cdr_mysql.conf.sample asterisk-1.2.0-new/configs/cdr_mysql.conf.sample +--- asterisk-1.2.0-old/configs/cdr_mysql.conf.sample 1970-01-01 01:00:00.000000000 +0100 ++++ asterisk-1.2.0-new/configs/cdr_mysql.conf.sample 2005-01-21 02:43:20.000000000 +0100 +@@ -0,0 +1,21 @@ ++; ++; Note - if the database server is hosted on the same machine as the ++; asterisk server, you can achieve a local Unix socket connection by ++; setting hostname=localhost ++; ++; port and sock are both optional parameters. If hostname is specified ++; and is not "localhost", then cdr_mysql will attempt to connect to the ++; port specified or use the default port. If hostname is not specified ++; or if hostname is "localhost", then cdr_mysql will attempt to connect ++; to the socket file specified by sock or otherwise use the default socket ++; file. ++; ++;[global] ++;hostname=database.host.name ++;dbname=asteriskcdrdb ++;table=cdr ++;password=password ++;user=asteriskcdruser ++;port=3306 ++;sock=/tmp/mysql.sock ++;userfield=1 +diff -ruN asterisk-1.2.0-old/cdr/cdr_mysql.c asterisk-1.2.0-new/cdr/cdr_mysql.c +--- asterisk-1.2.0-old/cdr/cdr_mysql.c 1970-01-01 01:00:00.000000000 +0100 ++++ asterisk-1.2.0-new/cdr/cdr_mysql.c 2005-12-04 20:10:59.000000000 +0100 +@@ -0,0 +1,493 @@ ++/* ++ * Asterisk -- A telephony toolkit for Linux. ++ * ++ * MySQL CDR logger ++ * ++ * James Sharp <jsharp@psychoses.org> ++ * ++ * Modified August 2003 ++ * Tilghman Lesher <asterisk__cdr__cdr_mysql__200308@the-tilghman.com> ++ * ++ * Modified August 6, 2005 ++ * Joseph Benden <joe@thrallingpenguin.com> ++ * Added mysql connection timeout parameter ++ * Added an automatic reconnect as to not lose a cdr record ++ * Cleaned up the original code to match the coding guidelines ++ * ++ * This program is free software, distributed under the terms of ++ * the GNU General Public License. ++ * ++ */ ++ ++#include <sys/types.h> ++ ++#include <stdio.h> ++#include <string.h> ++ ++#include <stdlib.h> ++#include <unistd.h> ++#include <time.h> ++ ++#include <mysql.h> ++#include <errmsg.h> ++ ++#include <sys/stat.h> ++#include <sys/types.h> ++#include <errno.h> ++ ++#include <asterisk/config.h> ++#include <asterisk/options.h> ++#include <asterisk/channel.h> ++#include <asterisk/cdr.h> ++#include <asterisk/module.h> ++#include <asterisk/logger.h> ++#include <asterisk/cli.h> ++ ++#define DATE_FORMAT "%Y-%m-%d %T" ++ ++static char *desc = "MySQL CDR Backend"; ++static char *name = "mysql"; ++static char *config = "cdr_mysql.conf"; ++static char *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *dbsock = NULL, *dbtable = NULL; ++static int hostname_alloc = 0, dbname_alloc = 0, dbuser_alloc = 0, password_alloc = 0, dbsock_alloc = 0, dbtable_alloc = 0; ++static int dbport = 0; ++static int connected = 0; ++static time_t connect_time = 0; ++static int records = 0; ++static int totalrecords = 0; ++static int userfield = 0; ++static unsigned int timeout = 0; ++ ++AST_MUTEX_DEFINE_STATIC(mysql_lock); ++ ++static MYSQL mysql; ++ ++static char cdr_mysql_status_help[] = ++"Usage: cdr mysql status\n" ++" Shows current connection status for cdr_mysql\n"; ++ ++static int handle_cdr_mysql_status(int fd, int argc, char *argv[]) ++{ ++ if (connected) { ++ char status[256], status2[100] = ""; ++ int ctime = time(NULL) - connect_time; ++ if (dbport) ++ snprintf(status, 255, "Connected to %s@%s, port %d", dbname, hostname, dbport); ++ else if (dbsock) ++ snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock); ++ else ++ snprintf(status, 255, "Connected to %s@%s", dbname, hostname); ++ ++ if (dbuser && *dbuser) ++ snprintf(status2, 99, " with username %s", dbuser); ++ if (dbtable && *dbtable) ++ snprintf(status2, 99, " using table %s", dbtable); ++ if (ctime > 31536000) { ++ ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); ++ } else if (ctime > 86400) { ++ ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); ++ } else if (ctime > 3600) { ++ ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60); ++ } else if (ctime > 60) { ++ ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60); ++ } else { ++ ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime); ++ } ++ if (records == totalrecords) ++ ast_cli(fd, " Wrote %d records since last restart.\n", totalrecords); ++ else ++ ast_cli(fd, " Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records); ++ return RESULT_SUCCESS; ++ } else { ++ ast_cli(fd, "Not currently connected to a MySQL server.\n"); ++ return RESULT_FAILURE; ++ } ++} ++ ++static struct ast_cli_entry cdr_mysql_status_cli = ++ { { "cdr", "mysql", "status", NULL }, ++ handle_cdr_mysql_status, "Show connection status of cdr_mysql", ++ cdr_mysql_status_help, NULL }; ++ ++static int mysql_log(struct ast_cdr *cdr) ++{ ++ struct tm tm; ++ struct timeval tv; ++ struct localuser *u; ++ char *userfielddata = NULL; ++ char sqlcmd[2048], timestr[128]; ++ char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL; ++ int retries = 5; ++#ifdef MYSQL_LOGUNIQUEID ++ char *uniqueid = NULL; ++#endif ++ ++ ast_mutex_lock(&mysql_lock); ++ ++ memset(sqlcmd, 0, 2048); ++ ++ localtime_r(&cdr->start.tv_sec, &tm); ++ strftime(timestr, 128, DATE_FORMAT, &tm); ++ ++db_reconnect: ++ if ((!connected) && (hostname || dbsock) && dbuser && password && dbname && dbtable ) { ++ /* Attempt to connect */ ++ mysql_init(&mysql); ++ /* Add option to quickly timeout the connection */ ++ if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout)!=0) { ++ ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql)); ++ } ++ if (mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) { ++ connected = 1; ++ connect_time = time(NULL); ++ records = 0; ++ } else { ++ ast_log(LOG_ERROR, "cdr_mysql: cannot connect to database server %s.\n", hostname); ++ connected = 0; ++ } ++ } else { ++ /* Long connection - ping the server */ ++ int error; ++ if ((error = mysql_ping(&mysql))) { ++ connected = 0; ++ records = 0; ++ switch (error) { ++ case CR_SERVER_GONE_ERROR: ++ case CR_SERVER_LOST: ++ ast_log(LOG_ERROR, "cdr_mysql: Server has gone away. Attempting to reconnect.\n"); ++ break; ++ default: ++ ast_log(LOG_ERROR, "cdr_mysql: Unknown connection error: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql)); ++ } ++ retries--; ++ if (retries) ++ goto db_reconnect; ++ else ++ ast_log(LOG_ERROR, "cdr_mysql: Retried to connect fives times, giving up.\n"); ++ } ++ } ++ ++ /* Maximum space needed would be if all characters needed to be escaped, plus a trailing NULL */ ++ /* WARNING: This code previously used mysql_real_escape_string, but the use of said function ++ requires an active connection to a database. If we are not connected, then this function ++ cannot be used. This is a problem since we need to store off the SQL statement into our ++ spool file for later restoration. ++ So the question is, what's the best way to handle this? This works for now. ++ */ ++ if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL) ++ mysql_escape_string(clid, cdr->clid, strlen(cdr->clid)); ++ if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL) ++ mysql_escape_string(dcontext, cdr->dcontext, strlen(cdr->dcontext)); ++ if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL) ++ mysql_escape_string(channel, cdr->channel, strlen(cdr->channel)); ++ if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL) ++ mysql_escape_string(dstchannel, cdr->dstchannel, strlen(cdr->dstchannel)); ++ if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL) ++ mysql_escape_string(lastapp, cdr->lastapp, strlen(cdr->lastapp)); ++ if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL) ++ mysql_escape_string(lastdata, cdr->lastdata, strlen(cdr->lastdata)); ++#ifdef MYSQL_LOGUNIQUEID ++ if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL) ++ mysql_escape_string(uniqueid, cdr->uniqueid, strlen(cdr->uniqueid)); ++#endif ++ if (userfield && ((userfielddata = alloca(strlen(cdr->userfield) * 2 + 1)) != NULL)) ++ mysql_escape_string(userfielddata, cdr->userfield, strlen(cdr->userfield)); ++ ++ /* Check for all alloca failures above at once */ ++#ifdef MYSQL_LOGUNIQUEID ++ if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid)) { ++#else ++ if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata)) { ++#endif ++ ast_log(LOG_ERROR, "cdr_mysql: Out of memory error (insert fails)\n"); ++ ast_mutex_unlock(&mysql_lock); ++ return -1; ++ } ++ ++ ast_log(LOG_DEBUG, "cdr_mysql: inserting a CDR record.\n"); ++ ++ if (userfield && userfielddata) { ++#ifdef MYSQL_LOGUNIQUEID ++ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, uniqueid, userfielddata); ++#else ++ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, userfielddata); ++#endif ++ } else { ++#ifdef MYSQL_LOGUNIQUEID ++ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, uniqueid); ++#else ++ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode); ++#endif ++ } ++ ++ ast_log(LOG_DEBUG, "cdr_mysql: SQL command as follows: %s\n", sqlcmd); ++ ++ if (connected) { ++ if (mysql_real_query(&mysql, sqlcmd, strlen(sqlcmd))) { ++ ast_log(LOG_ERROR, "mysql_cdr: Failed to insert into database: (%d) %s", mysql_errno(&mysql), mysql_error(&mysql)); ++ connected = 0; ++ } else { ++ records++; ++ totalrecords++; ++ } ++ } ++ ast_mutex_unlock(&mysql_lock); ++ return 0; ++} ++ ++char *description(void) ++{ ++ return desc; ++} ++ ++static int my_unload_module(void) ++{ ++ ast_cli_unregister(&cdr_mysql_status_cli); ++ if (connected) { ++ mysql_close(&mysql); ++ connected = 0; ++ records = 0; ++ } ++ if (hostname && hostname_alloc) { ++ free(hostname); ++ hostname = NULL; ++ hostname_alloc = 0; ++ } ++ if (dbname && dbname_alloc) { ++ free(dbname); ++ dbname = NULL; ++ dbname_alloc = 0; ++ } ++ if (dbuser && dbuser_alloc) { ++ free(dbuser); ++ dbuser = NULL; ++ dbuser_alloc = 0; ++ } ++ if (dbsock && dbsock_alloc) { ++ free(dbsock); ++ dbsock = NULL; ++ dbsock_alloc = 0; ++ } ++ if (dbtable && dbtable_alloc) { ++ free(dbtable); ++ dbtable = NULL; ++ dbtable_alloc = 0; ++ } ++ if (password && password_alloc) { ++ free(password); ++ password = NULL; ++ password_alloc = 0; ++ } ++ dbport = 0; ++ ast_cdr_unregister(name); ++ return 0; ++} ++ ++static int my_load_module(void) ++{ ++ int res; ++ struct ast_config *cfg; ++ struct ast_variable *var; ++ char *tmp; ++ ++ cfg = ast_config_load(config); ++ if (!cfg) { ++ ast_log(LOG_WARNING, "Unable to load config for mysql CDR's: %s\n", config); ++ return 0; ++ } ++ ++ var = ast_variable_browse(cfg, "global"); ++ if (!var) { ++ /* nothing configured */ ++ return 0; ++ } ++ ++ tmp = ast_variable_retrieve(cfg, "global", "hostname"); ++ if (tmp) { ++ hostname = malloc(strlen(tmp) + 1); ++ if (hostname != NULL) { ++ hostname_alloc = 1; ++ strcpy(hostname, tmp); ++ } else { ++ ast_log(LOG_ERROR, "Out of memory error.\n"); ++ return -1; ++ } ++ } else { ++ ast_log(LOG_WARNING, "MySQL server hostname not specified. Assuming localhost\n"); ++ hostname = "localhost"; ++ } ++ ++ tmp = ast_variable_retrieve(cfg, "global", "dbname"); ++ if (tmp) { ++ dbname = malloc(strlen(tmp) + 1); ++ if (dbname != NULL) { ++ dbname_alloc = 1; ++ strcpy(dbname, tmp); ++ } else { ++ ast_log(LOG_ERROR, "Out of memory error.\n"); ++ return -1; ++ } ++ } else { ++ ast_log(LOG_WARNING, "MySQL database not specified. Assuming asteriskcdrdb\n"); ++ dbname = "asteriskcdrdb"; ++ } ++ ++ tmp = ast_variable_retrieve(cfg, "global", "user"); ++ if (tmp) { ++ dbuser = malloc(strlen(tmp) + 1); ++ if (dbuser != NULL) { ++ dbuser_alloc = 1; ++ strcpy(dbuser, tmp); ++ } else { ++ ast_log(LOG_ERROR, "Out of memory error.\n"); ++ return -1; ++ } ++ } else { ++ ast_log(LOG_WARNING, "MySQL database user not specified. Assuming root\n"); ++ dbuser = "root"; ++ } ++ ++ tmp = ast_variable_retrieve(cfg, "global", "sock"); ++ if (tmp) { ++ dbsock = malloc(strlen(tmp) + 1); ++ if (dbsock != NULL) { ++ dbsock_alloc = 1; ++ strcpy(dbsock, tmp); ++ } else { ++ ast_log(LOG_ERROR, "Out of memory error.\n"); ++ return -1; ++ } ++ } else { ++ ast_log(LOG_WARNING, "MySQL database sock file not specified. Using default\n"); ++ dbsock = NULL; ++ } ++ ++ tmp = ast_variable_retrieve(cfg, "global", "table"); ++ if (tmp) { ++ dbtable = malloc(strlen(tmp) + 1); ++ if (dbtable != NULL) { ++ dbtable_alloc = 1; ++ strcpy(dbtable, tmp); ++ } else { ++ ast_log(LOG_ERROR, "Out of memory error.\n"); ++ return -1; ++ } ++ } else { ++ ast_log(LOG_NOTICE, "MySQL database table not specified. Assuming \"cdr\"\n"); ++ dbtable = "cdr"; ++ } ++ ++ tmp = ast_variable_retrieve(cfg, "global", "password"); ++ if (tmp) { ++ password = malloc(strlen(tmp) + 1); ++ if (password != NULL) { ++ password_alloc = 1; ++ strcpy(password, tmp); ++ } else { ++ ast_log(LOG_ERROR, "Out of memory error.\n"); ++ return -1; ++ } ++ } else { ++ ast_log(LOG_WARNING, "MySQL database password not specified. Assuming blank\n"); ++ password = ""; ++ } ++ ++ tmp = ast_variable_retrieve(cfg, "global", "port"); ++ if (tmp) { ++ if (sscanf(tmp, "%d", &dbport) < 1) { ++ ast_log(LOG_WARNING, "Invalid MySQL port number. Using default\n"); ++ dbport = 0; ++ } ++ } ++ ++ tmp = ast_variable_retrieve(cfg, "global", "timeout"); ++ if (tmp) { ++ if (sscanf(tmp,"%d", &timeout) < 1) { ++ ast_log(LOG_WARNING, "Invalid MySQL timeout number. Using default\n"); ++ timeout = 0; ++ } ++ } ++ ++ tmp = ast_variable_retrieve(cfg, "global", "userfield"); ++ if (tmp) { ++ if (sscanf(tmp, "%d", &userfield) < 1) { ++ ast_log(LOG_WARNING, "Invalid MySQL configurtation file\n"); ++ userfield = 0; ++ } ++ } ++ ++ ast_config_destroy(cfg); ++ ++ ast_log(LOG_DEBUG, "cdr_mysql: got hostname of %s\n", hostname); ++ ast_log(LOG_DEBUG, "cdr_mysql: got port of %d\n", dbport); ++ ast_log(LOG_DEBUG, "cdr_mysql: got a timeout of %d\n", timeout); ++ if (dbsock) ++ ast_log(LOG_DEBUG, "cdr_mysql: got sock file of %s\n", dbsock); ++ ast_log(LOG_DEBUG, "cdr_mysql: got user of %s\n", dbuser); ++ ast_log(LOG_DEBUG, "cdr_mysql: got dbname of %s\n", dbname); ++ ast_log(LOG_DEBUG, "cdr_mysql: got password of %s\n", password); ++ ++ mysql_init(&mysql); ++ ++ if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout)!=0) { ++ ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql)); ++ } ++ ++ if (!mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) { ++ ast_log(LOG_ERROR, "Failed to connect to mysql database %s on %s.\n", dbname, hostname); ++ connected = 0; ++ records = 0; ++ } else { ++ ast_log(LOG_DEBUG, "Successfully connected to MySQL database.\n"); ++ connected = 1; ++ records = 0; ++ connect_time = time(NULL); ++ } ++ ++ res = ast_cdr_register(name, desc, mysql_log); ++ if (res) { ++ ast_log(LOG_ERROR, "Unable to register MySQL CDR handling\n"); ++ } else { ++ res = ast_cli_register(&cdr_mysql_status_cli); ++ } ++ ++ return res; ++} ++ ++int load_module(void) ++{ ++ return my_load_module(); ++} ++ ++int unload_module(void) ++{ ++ return my_unload_module(); ++} ++ ++int reload(void) ++{ ++ int ret; ++ ++ ast_mutex_lock(&mysql_lock); ++ my_unload_module(); ++ ret = my_load_module(); ++ ast_mutex_unlock(&mysql_lock); ++ ++ return ret; ++} ++ ++int usecount(void) ++{ ++ /* Simplistic use count */ ++ if (ast_mutex_trylock(&mysql_lock)) { ++ return 1; ++ } else { ++ ast_mutex_unlock(&mysql_lock); ++ return 0; ++ } ++} ++ ++char *key() ++{ ++ return ASTERISK_GPL_KEY; ++} diff --git a/package/asterisk/patches/asterisk-1.2.0-chan_bluetooth.patch b/package/asterisk/patches/asterisk-1.2.0-chan_bluetooth.patch new file mode 100644 index 000000000..6b9da7915 --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-chan_bluetooth.patch @@ -0,0 +1,11 @@ +diff -ruN asterisk-1.2.0-old/channels/chan_bluetooth.c asterisk-1.2.0-new/channels/chan_bluetooth.c +--- asterisk-1.2.0-old/channels/chan_bluetooth.c 2005-12-04 04:48:40.000000000 +0100 ++++ asterisk-1.2.0-new/channels/chan_bluetooth.c 2005-12-04 23:07:19.000000000 +0100 +@@ -104,6 +104,7 @@ + #include <asterisk/options.h> + #include <asterisk/cli.h> + #include <asterisk/callerid.h> ++#include <asterisk/version.h> + #include <sys/socket.h> + #include <sys/signal.h> + #include <sys/time.h> diff --git a/package/asterisk/patches/asterisk-1.2.0-compat-getloadavg.patch b/package/asterisk/patches/asterisk-1.2.0-compat-getloadavg.patch new file mode 100644 index 000000000..a909513b1 --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-compat-getloadavg.patch @@ -0,0 +1,13 @@ +diff -ruN asterisk-1.2.0-old/include/asterisk/compat.h asterisk-1.2.0-new/include/asterisk/compat.h +--- asterisk-1.2.0-old/include/asterisk/compat.h 2005-11-08 05:13:19.000000000 +0100 ++++ asterisk-1.2.0-new/include/asterisk/compat.h 2005-12-04 05:32:31.000000000 +0100 +@@ -75,7 +75,9 @@ + #define HAVE_STRTOQ + + #ifdef _BSD_SOURCE ++#ifndef __UCLIBC__ + #define HAVE_GETLOADAVG ++#endif /* __UCLIBC__ */ + #endif + + #ifdef __linux__ diff --git a/package/asterisk/patches/asterisk-1.2.0-res_mysql.patch b/package/asterisk/patches/asterisk-1.2.0-res_mysql.patch new file mode 100644 index 000000000..5de810741 --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.0-res_mysql.patch @@ -0,0 +1,698 @@ +diff -ruN asterisk-1.2.0-old/configs/res_mysql.conf.sample asterisk-1.2.0-new/configs/res_mysql.conf.sample +--- asterisk-1.2.0-old/configs/res_mysql.conf.sample 1970-01-01 01:00:00.000000000 +0100 ++++ asterisk-1.2.0-new/configs/res_mysql.conf.sample 2004-12-03 15:33:44.000000000 +0100 +@@ -0,0 +1,15 @@ ++; ++; Sample configuration for res_config_mysql.c ++; ++; The value of dbhost may be either a hostname or an IP address. ++; If dbhost is commented out or the string "localhost", a connection ++; to the local host is assumed and dbsock is used instead of TCP/IP ++; to connect to the server. ++; ++[general] ++;dbhost = 127.0.0.1 ++;dbname = asterisk ++;dbuser = myuser ++;dbpass = mypass ++;dbport = 3306 ++;dbsock = /tmp/mysql.sock +diff -ruN asterisk-1.2.0-old/res/res_config_mysql.c asterisk-1.2.0-new/res/res_config_mysql.c +--- asterisk-1.2.0-old/res/res_config_mysql.c 1970-01-01 01:00:00.000000000 +0100 ++++ asterisk-1.2.0-new/res/res_config_mysql.c 2005-10-13 21:43:54.000000000 +0200 +@@ -0,0 +1,675 @@ ++/* ++ * Asterisk -- A telephony toolkit for Linux. ++ * ++ * Copyright (C) 1999-2005, Digium, Inc. ++ * ++ * Mark Spencer <markster@digium.com> - Asterisk Author ++ * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author ++ * ++ * res_config_mysql.c <mysql plugin for RealTime configuration engine> ++ * ++ * v2.0 - (10-07-05) - mutex_lock fixes (bug #4973, comment #0034602) ++ * ++ * v1.9 - (08-19-05) - Added support to correctly honor the family database specified ++ * in extconfig.conf (bug #4973) ++ * ++ * v1.8 - (04-21-05) - Modified return values of update_mysql to better indicate ++ * what really happened. ++ * ++ * v1.7 - (01-28-05) - Fixed non-initialization of ast_category struct ++ * in realtime_multi_mysql function which caused segfault. ++ * ++ * v1.6 - (00-00-00) - Skipped to bring comments into sync with version number in CVS. ++ * ++ * v1.5.1 - (01-26-05) - Added better(?) locking stuff ++ * ++ * v1.5 - (01-26-05) - Brought up to date with new config.h changes (bug #3406) ++ * - Added in extra locking provided by georg (bug #3248) ++ * ++ * v1.4 - (12-02-04) - Added realtime_multi_mysql function ++ * This function will return an ast_config with categories, ++ * unlike standard realtime_mysql which only returns ++ * a linked list of ast_variables ++ * ++ * v1.3 - (12-01-04) - Added support other operators ++ * Ex: =, !=, LIKE, NOT LIKE, RLIKE, etc... ++ * ++ * v1.2 - (11-DD-04) - Added reload. Updated load and unload. ++ * Code beautification (doc/CODING-GUIDELINES) ++ */ ++ ++#include <asterisk/channel.h> ++#include <asterisk/logger.h> ++#include <asterisk/config.h> ++#include <asterisk/module.h> ++#include <asterisk/lock.h> ++#include <asterisk/options.h> ++#include <asterisk/cli.h> ++#include <asterisk/utils.h> ++#include <stdlib.h> ++#include <string.h> ++#include <mysql.h> ++#include <mysql_version.h> ++#include <errmsg.h> ++ ++static char *res_config_mysql_desc = "MySQL RealTime Configuration Driver"; ++ ++AST_MUTEX_DEFINE_STATIC(mysql_lock); ++#define RES_CONFIG_MYSQL_CONF "res_mysql.conf" ++MYSQL mysql; ++static char dbhost[50]; ++static char dbuser[50]; ++static char dbpass[50]; ++static char dbname[50]; ++static char dbsock[50]; ++static int dbport; ++static int connected; ++static time_t connect_time; ++ ++static int parse_config(void); ++static int mysql_reconnect(const char *database); ++static int realtime_mysql_status(int fd, int argc, char **argv); ++ ++STANDARD_LOCAL_USER; ++ ++LOCAL_USER_DECL; ++ ++static char cli_realtime_mysql_status_usage[] = ++"Usage: realtime mysql status\n" ++" Shows connection information for the MySQL RealTime driver\n"; ++ ++static struct ast_cli_entry cli_realtime_mysql_status = { ++ { "realtime", "mysql", "status", NULL }, realtime_mysql_status, ++ "Shows connection information for the MySQL RealTime driver", cli_realtime_mysql_status_usage, NULL }; ++ ++static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap) ++{ ++ MYSQL_RES *result; ++ MYSQL_ROW row; ++ MYSQL_FIELD *fields; ++ int numFields, i; ++ char sql[256]; ++ char *stringp; ++ char *chunk; ++ char *op; ++ const char *newparam, *newval; ++ struct ast_variable *var=NULL, *prev=NULL; ++ ++ if(!table) { ++ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n"); ++ return NULL; ++ } ++ ++ /* Get the first parameter and first value in our list of passed paramater/value pairs */ ++ newparam = va_arg(ap, const char *); ++ newval = va_arg(ap, const char *); ++ if(!newparam || !newval) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); ++ mysql_close(&mysql); ++ return NULL; ++ } ++ ++ /* Create the first part of the query using the first parameter/value pairs we just extracted ++ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ ++ ++ if(!strchr(newparam, ' ')) op = " ="; else op = ""; ++ ++ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval); ++ while((newparam = va_arg(ap, const char *))) { ++ newval = va_arg(ap, const char *); ++ if(!strchr(newparam, ' ')) op = " ="; else op = ""; ++ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval); ++ } ++ va_end(ap); ++ ++ ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql); ++ ++ /* We now have our complete statement; Lets connect to the server and execute it. */ ++ ast_mutex_lock(&mysql_lock); ++ if(!mysql_reconnect(database)) { ++ ast_mutex_unlock(&mysql_lock); ++ return NULL; ++ } ++ ++ if(mysql_real_query(&mysql, sql, strlen(sql))) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n"); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql)); ++ ast_mutex_unlock(&mysql_lock); ++ return NULL; ++ } ++ ++ if((result = mysql_store_result(&mysql))) { ++ numFields = mysql_num_fields(result); ++ fields = mysql_fetch_fields(result); ++ ++ while((row = mysql_fetch_row(result))) { ++ for(i = 0; i < numFields; i++) { ++ stringp = row[i]; ++ while(stringp) { ++ chunk = strsep(&stringp, ";"); ++ if(chunk && !ast_strlen_zero(ast_strip(chunk))) { ++ if(prev) { ++ prev->next = ast_variable_new(fields[i].name, chunk); ++ if (prev->next) { ++ prev = prev->next; ++ } ++ } else { ++ prev = var = ast_variable_new(fields[i].name, chunk); ++ } ++ } ++ } ++ } ++ } ++ } else { ++ ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table); ++ } ++ ++ ast_mutex_unlock(&mysql_lock); ++ mysql_free_result(result); ++ ++ return var; ++} ++ ++static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap) ++{ ++ MYSQL_RES *result; ++ MYSQL_ROW row; ++ MYSQL_FIELD *fields; ++ int numFields, i; ++ char sql[256]; ++ const char *initfield = NULL; ++ char *stringp; ++ char *chunk; ++ char *op; ++ const char *newparam, *newval; ++ struct ast_realloca ra; ++ struct ast_variable *var=NULL; ++ struct ast_config *cfg = NULL; ++ struct ast_category *cat = NULL; ++ ++ if(!table) { ++ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n"); ++ return NULL; ++ } ++ ++ memset(&ra, 0, sizeof(ra)); ++ ++ cfg = ast_config_new(); ++ if (!cfg) { ++ /* If I can't alloc memory at this point, why bother doing anything else? */ ++ ast_log(LOG_WARNING, "Out of memory!\n"); ++ return NULL; ++ } ++ ++ /* Get the first parameter and first value in our list of passed paramater/value pairs */ ++ newparam = va_arg(ap, const char *); ++ newval = va_arg(ap, const char *); ++ if(!newparam || !newval) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); ++ mysql_close(&mysql); ++ return NULL; ++ } ++ ++ initfield = ast_strdupa(newparam); ++ if(initfield && (op = strchr(initfield, ' '))) { ++ *op = '\0'; ++ } ++ ++ /* Create the first part of the query using the first parameter/value pairs we just extracted ++ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ ++ ++ if(!strchr(newparam, ' ')) op = " ="; else op = ""; ++ ++ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval); ++ while((newparam = va_arg(ap, const char *))) { ++ newval = va_arg(ap, const char *); ++ if(!strchr(newparam, ' ')) op = " ="; else op = ""; ++ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval); ++ } ++ ++ if(initfield) { ++ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield); ++ } ++ ++ va_end(ap); ++ ++ ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql); ++ ++ /* We now have our complete statement; Lets connect to the server and execute it. */ ++ ast_mutex_lock(&mysql_lock); ++ if(!mysql_reconnect(database)) { ++ ast_mutex_unlock(&mysql_lock); ++ return NULL; ++ } ++ ++ if(mysql_real_query(&mysql, sql, strlen(sql))) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n"); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql)); ++ ast_mutex_unlock(&mysql_lock); ++ return NULL; ++ } ++ ++ if((result = mysql_store_result(&mysql))) { ++ numFields = mysql_num_fields(result); ++ fields = mysql_fetch_fields(result); ++ ++ while((row = mysql_fetch_row(result))) { ++ var = NULL; ++ cat = ast_category_new(""); ++ if(!cat) { ++ ast_log(LOG_WARNING, "Out of memory!\n"); ++ continue; ++ } ++ for(i = 0; i < numFields; i++) { ++ stringp = row[i]; ++ while(stringp) { ++ chunk = strsep(&stringp, ";"); ++ if(chunk && !ast_strlen_zero(ast_strip(chunk))) { ++ if(initfield && !strcmp(initfield, fields[i].name)) { ++ ast_category_rename(cat, chunk); ++ } ++ var = ast_variable_new(fields[i].name, chunk); ++ ast_variable_append(cat, var); ++ } ++ } ++ } ++ ast_category_append(cfg, cat); ++ } ++ } else { ++ ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table); ++ } ++ ++ ast_mutex_unlock(&mysql_lock); ++ mysql_free_result(result); ++ ++ return cfg; ++} ++ ++static int update_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap) ++{ ++ my_ulonglong numrows; ++ char sql[256]; ++ const char *newparam, *newval; ++ ++ if(!table) { ++ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n"); ++ return -1; ++ } ++ ++ /* Get the first parameter and first value in our list of passed paramater/value pairs */ ++ newparam = va_arg(ap, const char *); ++ newval = va_arg(ap, const char *); ++ if(!newparam || !newval) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); ++ mysql_close(&mysql); ++ return -1; ++ } ++ ++ /* Create the first part of the query using the first parameter/value pairs we just extracted ++ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ ++ ++ snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, newval); ++ while((newparam = va_arg(ap, const char *))) { ++ newval = va_arg(ap, const char *); ++ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, newval); ++ } ++ va_end(ap); ++ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, lookup); ++ ++ ast_log(LOG_DEBUG,"MySQL RealTime: Update SQL: %s\n", sql); ++ ++ /* We now have our complete statement; Lets connect to the server and execute it. */ ++ ast_mutex_lock(&mysql_lock); ++ if(!mysql_reconnect(database)) { ++ ast_mutex_unlock(&mysql_lock); ++ return -1; ++ } ++ ++ if(mysql_real_query(&mysql, sql, strlen(sql))) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n"); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql)); ++ ast_mutex_unlock(&mysql_lock); ++ return -1; ++ } ++ ++ numrows = mysql_affected_rows(&mysql); ++ ast_mutex_unlock(&mysql_lock); ++ ++ ast_log(LOG_DEBUG,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table); ++ ++ /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html ++ * An integer greater than zero indicates the number of rows affected ++ * Zero indicates that no records were updated ++ * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.) ++ */ ++ ++ if(numrows >= 0) ++ return (int)numrows; ++ ++ return -1; ++} ++ ++static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg) ++{ ++ MYSQL_RES *result; ++ MYSQL_ROW row; ++ my_ulonglong num_rows; ++ struct ast_config *new; ++ struct ast_variable *cur_v, *new_v; ++ struct ast_category *cur_cat, *new_cat; ++ char sql[250] = ""; ++ char last[80] = ""; ++ int cat_started = 0; ++ int var_started = 0; ++ int last_cat_metric = 0; ++ ++ last[0] = '\0'; ++ ++ if(!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n"); ++ return NULL; ++ } ++ ++ snprintf(sql, sizeof(sql), "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file); ++ ++ ast_log(LOG_DEBUG, "MySQL RealTime: Static SQL: %s\n", sql); ++ ++ /* We now have our complete statement; Lets connect to the server and execute it. */ ++ ast_mutex_lock(&mysql_lock); ++ if(!mysql_reconnect(database)) { ++ ast_mutex_unlock(&mysql_lock); ++ return NULL; ++ } ++ ++ if(mysql_real_query(&mysql, sql, strlen(sql))) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n"); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql)); ++ ast_mutex_unlock(&mysql_lock); ++ return NULL; ++ } ++ ++ if((result = mysql_store_result(&mysql))) { ++ num_rows = mysql_num_rows(result); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows.\n", num_rows); ++ ++ /* There might exist a better way to access the column names other than counting, ++ but I believe that would require another loop that we don't need. */ ++ ++ while((row = mysql_fetch_row(result))) { ++ if(!strcmp(row[1], "#include")) { ++ if (!ast_config_internal_load(row[2], cfg)) { ++ mysql_free_result(result); ++ ast_mutex_unlock(&mysql_lock); ++ return NULL; ++ } ++ continue; ++ } ++ ++ if(strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) { ++ cur_cat = ast_category_new(row[0]); ++ if (!cur_cat) { ++ ast_log(LOG_WARNING, "Out of memory!\n"); ++ break; ++ } ++ strcpy(last, row[0]); ++ last_cat_metric = atoi(row[3]); ++ ast_category_append(cfg, cur_cat); ++ } ++ new_v = ast_variable_new(row[1], row[2]); ++ ast_variable_append(cur_cat, new_v); ++ } ++ } else { ++ ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file); ++ } ++ ++ mysql_free_result(result); ++ ast_mutex_unlock(&mysql_lock); ++ ++ return cfg; ++} ++ ++static struct ast_config_engine mysql_engine = { ++ .name = "mysql", ++ .load_func = config_mysql, ++ .realtime_func = realtime_mysql, ++ .realtime_multi_func = realtime_multi_mysql, ++ .update_func = update_mysql ++}; ++ ++int load_module (void) ++{ ++ parse_config(); ++ ++ ast_mutex_lock(&mysql_lock); ++ ++ if(!mysql_reconnect(NULL)) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n"); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql)); ++ } ++ ++ ast_config_engine_register(&mysql_engine); ++ if(option_verbose) { ++ ast_verbose("MySQL RealTime driver loaded.\n"); ++ } ++ ast_cli_register(&cli_realtime_mysql_status); ++ ++ ast_mutex_unlock(&mysql_lock); ++ ++ return 0; ++} ++ ++int unload_module (void) ++{ ++ /* Aquire control before doing anything to the module itself. */ ++ ast_mutex_lock(&mysql_lock); ++ ++ mysql_close(&mysql); ++ ast_cli_unregister(&cli_realtime_mysql_status); ++ ast_config_engine_deregister(&mysql_engine); ++ if(option_verbose) { ++ ast_verbose("MySQL RealTime unloaded.\n"); ++ } ++ ++ STANDARD_HANGUP_LOCALUSERS; ++ ++ /* Unlock so something else can destroy the lock. */ ++ ast_mutex_unlock(&mysql_lock); ++ ++ return 0; ++} ++ ++int reload (void) ++{ ++ /* Aquire control before doing anything to the module itself. */ ++ ast_mutex_lock(&mysql_lock); ++ ++ mysql_close(&mysql); ++ connected = 0; ++ parse_config(); ++ ++ if(!mysql_reconnect(NULL)) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n"); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql)); ++ } ++ ++ ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n"); ++ ++ /* Done reloading. Release lock so others can now use driver. */ ++ ast_mutex_unlock(&mysql_lock); ++ ++ return 0; ++} ++ ++int parse_config (void) ++{ ++ struct ast_config *config; ++ char *s; ++ ++ config = ast_config_load(RES_CONFIG_MYSQL_CONF); ++ ++ if(config) { ++ if(!(s=ast_variable_retrieve(config, "general", "dbuser"))) { ++ ast_log(LOG_WARNING, "MySQL RealTime: No database user found, using 'asterisk' as default.\n"); ++ strncpy(dbuser, "asterisk", sizeof(dbuser) - 1); ++ } else { ++ strncpy(dbuser, s, sizeof(dbuser) - 1); ++ } ++ ++ if(!(s=ast_variable_retrieve(config, "general", "dbpass"))) { ++ ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n"); ++ strncpy(dbpass, "asterisk", sizeof(dbpass) - 1); ++ } else { ++ strncpy(dbpass, s, sizeof(dbpass) - 1); ++ } ++ ++ if(!(s=ast_variable_retrieve(config, "general", "dbhost"))) { ++ ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n"); ++ dbhost[0] = '\0'; ++ } else { ++ strncpy(dbhost, s, sizeof(dbhost) - 1); ++ } ++ ++ if(!(s=ast_variable_retrieve(config, "general", "dbname"))) { ++ ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n"); ++ strncpy(dbname, "asterisk", sizeof(dbname) - 1); ++ } else { ++ strncpy(dbname, s, sizeof(dbname) - 1); ++ } ++ ++ if(!(s=ast_variable_retrieve(config, "general", "dbport"))) { ++ ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n"); ++ dbport = 3306; ++ } else { ++ dbport = atoi(s); ++ } ++ ++ if(dbhost && !(s=ast_variable_retrieve(config, "general", "dbsock"))) { ++ ast_log(LOG_WARNING, "MySQL RealTime: No database socket found, using '/tmp/mysql.sock' as default.\n"); ++ strncpy(dbsock, "/tmp/mysql.sock", sizeof(dbsock) - 1); ++ } else { ++ strncpy(dbsock, s, sizeof(dbsock) - 1); ++ } ++ } ++ ast_config_destroy(config); ++ ++ if(dbhost) { ++ ast_log(LOG_DEBUG, "MySQL RealTime Host: %s\n", dbhost); ++ ast_log(LOG_DEBUG, "MySQL RealTime Port: %i\n", dbport); ++ } else { ++ ast_log(LOG_DEBUG, "MySQL RealTime Socket: %s\n", dbsock); ++ } ++ ast_log(LOG_DEBUG, "MySQL RealTime User: %s\n", dbuser); ++ ast_log(LOG_DEBUG, "MySQL RealTime Password: %s\n", dbpass); ++ ++ return 1; ++} ++ ++char *description (void) ++{ ++ return res_config_mysql_desc; ++} ++ ++int usecount (void) ++{ ++ /* Try and get a lock. If unsuccessful, than that means another thread is using the mysql object. */ ++ if(ast_mutex_trylock(&mysql_lock)) { ++ ast_log(LOG_DEBUG, "MySQL RealTime: Module usage count is 1.\n"); ++ return 1; ++ } ++ ast_mutex_unlock(&mysql_lock); ++ return 0; ++} ++ ++char *key () ++{ ++ return ASTERISK_GPL_KEY; ++} ++ ++static int mysql_reconnect(const char *database) ++{ ++ char my_database[50]; ++ ++ if(!database || ast_strlen_zero(database)) ++ ast_copy_string(my_database, dbname, sizeof(my_database)); ++ else ++ ast_copy_string(my_database, database, sizeof(my_database)); ++ ++ /* mutex lock should have been locked before calling this function. */ ++ ++ if((!connected) && (dbhost || dbsock) && dbuser && dbpass && my_database) { ++ if(!mysql_init(&mysql)) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Insufficient memory to allocate MySQL resource.\n"); ++ connected = 0; ++ return 0; ++ } ++ if(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 0)) { ++ ast_log(LOG_DEBUG, "MySQL RealTime: Successfully connected to database.\n"); ++ connected = 1; ++ connect_time = time(NULL); ++ return 1; ++ } else { ++ ast_log(LOG_ERROR, "MySQL RealTime: Failed to connect database server %s on %s. Check debug for more info.\n", dbname, dbhost); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql)); ++ connected = 0; ++ return 0; ++ } ++ } else { ++ if(mysql_ping(&mysql) != 0) { ++ connected = 0; ++ ast_log(LOG_ERROR, "MySQL RealTime: Failed to reconnect. Check debug for more info.\n"); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Server Error: %s\n", mysql_error(&mysql)); ++ return 0; ++ } ++ ++ connected = 1; ++ ++ if(mysql_select_db(&mysql, my_database) != 0) { ++ ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected.\n", my_database); ++ ast_log(LOG_DEBUG, "MySQL RealTime: Database Select Failed: %s\n", mysql_error(&mysql)); ++ return 0; ++ } ++ ++ ast_log(LOG_DEBUG, "MySQL RealTime: Everything is fine.\n"); ++ return 1; ++ } ++} ++ ++static int realtime_mysql_status(int fd, int argc, char **argv) ++{ ++ char status[256], status2[100] = ""; ++ int ctime = time(NULL) - connect_time; ++ ++ if(mysql_reconnect(NULL)) { ++ if(dbhost) { ++ snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport); ++ } else if(dbsock) { ++ snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock); ++ } else { ++ snprintf(status, 255, "Connected to %s@%s", dbname, dbhost); ++ } ++ ++ if(dbuser && *dbuser) { ++ snprintf(status2, 99, " with username %s", dbuser); ++ } ++ ++ if (ctime > 31536000) { ++ ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); ++ } else if (ctime > 86400) { ++ ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); ++ } else if (ctime > 3600) { ++ ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60); ++ } else if (ctime > 60) { ++ ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60); ++ } else { ++ ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime); ++ } ++ ++ return RESULT_SUCCESS; ++ } else { ++ return RESULT_FAILURE; ++ } ++} diff --git a/package/asterisk/patches/asterisk-1.2.1-Makefile-versionnum.patch b/package/asterisk/patches/asterisk-1.2.1-Makefile-versionnum.patch new file mode 100644 index 000000000..230764205 --- /dev/null +++ b/package/asterisk/patches/asterisk-1.2.1-Makefile-versionnum.patch @@ -0,0 +1,11 @@ +--- asterisk-1.2.1/Makefile.orig 2005-12-09 10:22:06.000000000 +0100 ++++ asterisk-1.2.1/Makefile 2005-12-09 10:28:46.000000000 +0100 +@@ -322,8 +322,6 @@ + ifneq ($(wildcard .svn),) + ASTERISKVERSIONNUM=999999 + ASTERISKVERSION=SVN-$(shell build_tools/make_svn_branch_name) +- else +- ASTERISKVERSIONNUM=000000 + endif + endif + diff --git a/package/asterisk/patches/mysql+postgres-support.diff b/package/asterisk/patches/mysql+postgres-support.diff deleted file mode 100644 index c9665fe7a..000000000 --- a/package/asterisk/patches/mysql+postgres-support.diff +++ /dev/null @@ -1,1024 +0,0 @@ -diff -ruN asterisk-1.0.7-orig/apps/Makefile asterisk-1.0.7-2/apps/Makefile ---- asterisk-1.0.7-orig/apps/Makefile 2004-09-24 23:32:56.000000000 +0200 -+++ asterisk-1.0.7-2/apps/Makefile 2005-03-19 17:38:06.000000000 +0100 -@@ -79,11 +80,17 @@ - endif - endif - -+app_sql_mysql.o: app_sql_mysql.c -+ $(CC) $(CFLAGS) $(MYSQL_CFLAGS) -c -o $@ $< -+ -+app_sql_mysql.so: app_sql_mysql.o -+ $(CC) $(SOLINK) -o $@ $< $(LDFLAGS_EXTRA) -lmysqlclient -lz $(MYSQL_LFLAGS) -+ - app_sql_postgres.o: app_sql_postgres.c -- $(CC) -pipe -I/usr/local/pgsql/include $(CFLAGS) -c -o app_sql_postgres.o app_sql_postgres.c -+ $(CC) $(CFLAGS) $(PGSQL_CFLAGS) -c -o $@ $< - - app_sql_postgres.so: app_sql_postgres.o -- $(CC) $(SOLINK) -o $@ $< -L/usr/local/pgsql/lib -lpq -+ $(CC) $(SOLINK) -o $@ $< $(LDFLAGS_EXTRA) -lpq -lz $(PGSQL_LFLAGS) - - app_sql_odbc.so: app_sql_odbc.o - $(CC) $(SOLINK) -o $@ $< -lodbc -diff -ruN asterisk-1.0.7-orig/apps/app_sql_mysql.c asterisk-1.0.7-2/apps/app_sql_mysql.c ---- asterisk-1.0.7-orig/apps/app_sql_mysql.c 1970-01-01 01:00:00.000000000 +0100 -+++ asterisk-1.0.7-2/apps/app_sql_mysql.c 2005-03-19 18:01:13.000000000 +0100 -@@ -0,0 +1,443 @@ -+/* -+ * Asterisk -- A telephony toolkit for Linux. -+ * -+ * Connect to PostgreSQL -+ * -+ * Copyright (C) 2004, Constantine Filin and Christos Ricudis -+ * -+ * Christos Ricudis <ricudis@itc.auth.gr> -+ * Constantine Filin <cf@intermedia.net> -+ * -+ * This program is free software, distributed under the terms of -+ * the GNU General Public License -+ */ -+ -+#include <asterisk/file.h> -+#include <asterisk/logger.h> -+#include <asterisk/channel.h> -+#include <asterisk/pbx.h> -+#include <asterisk/module.h> -+#include <asterisk/linkedlists.h> -+#include <asterisk/chanvars.h> -+#include <asterisk/lock.h> -+#include <stdlib.h> -+#include <unistd.h> -+#include <string.h> -+#include <stdlib.h> -+#include <sys/types.h> -+#include <stdio.h> -+#include <unistd.h> -+ -+#include <mysql/mysql.h> -+ -+#define EXTRA_LOG 0 -+ -+static char *tdesc = "Simple Mysql Interface"; -+ -+static char *app = "MYSQL"; -+ -+static char *synopsis = "Do several mySQLy things"; -+ -+static char *descrip = -+"MYSQL(): Do several mySQLy things\n" -+"Syntax:\n" -+" MYSQL(Connect connid dhhost dbuser dbpass dbname)\n" -+" Connects to a database. Arguments contain standard MySQL parameters\n" -+" passed to function mysql_real_connect. Connection identifer returned\n" -+" in ${var}\n" -+" MYSQL(Query resultid ${connid} query-string)\n" -+" Executes standard MySQL query contained in query-string using established\n" -+" connection identified by ${connection_identifier}. Result of query is\n" -+" is stored in ${var}.\n" -+" MYSQL(Fetch fetchid ${resultid} var1 var2 ... varN)\n" -+" Fetches a single row from a result set contained in ${result_identifier}.\n" -+" Assigns returned fields to ${var1} ... ${varn}. ${fetchid} is set TRUE\n" -+" if additional rows exist in result set.\n" -+" MYSQL(Clear ${resultid})\n" -+" Frees memory and datastructures associated with result set.\n" -+" MYSQL(Disconnect ${connid})\n" -+" Disconnects from named connection to MySQL.\n" ; -+ -+/* -+EXAMPLES OF USE : -+ -+exten => s,2,MYSQL(Connect connid localhost asterisk mypass credit) -+exten => s,3,MYSQL(Query resultid ${connid} SELECT username,credit FROM credit WHERE callerid=${CALLERIDNUM}) -+exten => s,4,MYSQL(Fetch fetchid ${resultid} datavar1 datavar2) -+exten => s,5,GotoIf(${fetchid}?6:8) -+exten => s,6,Festival("User ${datavar1} currently has credit balance of ${datavar2} dollars.") -+exten => s,7,Goto(s,4) -+exten => s,8,MYSQL(Clear ${resultid}) -+exten => s,9,MYSQL(Disconnect ${connid}) -+*/ -+ -+STANDARD_LOCAL_USER; -+LOCAL_USER_DECL; -+ -+AST_MUTEX_DEFINE_STATIC(_mysql_mutex); -+ -+extern void pbx_builtin_setvar_helper(struct ast_channel *chan, char *name, char *value); -+ -+#define AST_MYSQL_ID_DUMMY 0 -+#define AST_MYSQL_ID_CONNID 1 -+#define AST_MYSQL_ID_RESID 2 -+#define AST_MYSQL_ID_FETCHID 3 -+ -+struct ast_MYSQL_id { -+ int identifier_type; /* 0=dummy, 1=connid, 2=resultid */ -+ int identifier; -+ void *data; -+ AST_LIST_ENTRY(ast_MYSQL_id) entries; -+} *ast_MYSQL_id; -+ -+AST_LIST_HEAD(MYSQLidshead,ast_MYSQL_id) _mysql_ids_head; -+ -+/* helpful procs */ -+static void *find_identifier(int identifier,int identifier_type) { -+ struct MYSQLidshead *headp; -+ struct ast_MYSQL_id *i; -+ void *res=NULL; -+ int found=0; -+ -+ headp=&_mysql_ids_head; -+ -+ if (AST_LIST_LOCK(headp)) { -+ ast_log(LOG_WARNING,"Unable to lock identifiers list\n"); -+ } else { -+ AST_LIST_TRAVERSE(headp,i,entries) { -+ if ((i->identifier==identifier) && (i->identifier_type==identifier_type)) { -+ found=1; -+ res=i->data; -+ break; -+ } -+ } -+ if (!found) { -+ ast_log(LOG_WARNING,"Identifier %d, identifier_type %d not found in identifier list\n",identifier,identifier_type); -+ } -+ AST_LIST_UNLOCK(headp); -+ } -+ -+ return res; -+} -+ -+static int add_identifier(int identifier_type,void *data) { -+ struct ast_MYSQL_id *i,*j; -+ struct MYSQLidshead *headp; -+ int maxidentifier=0; -+ -+ headp=&_mysql_ids_head; -+ i=NULL; -+ j=NULL; -+ -+ if (AST_LIST_LOCK(headp)) { -+ ast_log(LOG_WARNING,"Unable to lock identifiers list\n"); -+ return(-1); -+ } else { -+ i=malloc(sizeof(struct ast_MYSQL_id)); -+ AST_LIST_TRAVERSE(headp,j,entries) { -+ if (j->identifier>maxidentifier) { -+ maxidentifier=j->identifier; -+ } -+ } -+ i->identifier=maxidentifier+1; -+ i->identifier_type=identifier_type; -+ i->data=data; -+ AST_LIST_INSERT_HEAD(headp,i,entries); -+ AST_LIST_UNLOCK(headp); -+ } -+ return i->identifier; -+} -+ -+static int del_identifier(int identifier,int identifier_type) { -+ struct ast_MYSQL_id *i; -+ struct MYSQLidshead *headp; -+ int found=0; -+ -+ headp=&_mysql_ids_head; -+ -+ if (AST_LIST_LOCK(headp)) { -+ ast_log(LOG_WARNING,"Unable to lock identifiers list\n"); -+ } else { -+ AST_LIST_TRAVERSE(headp,i,entries) { -+ if ((i->identifier==identifier) && -+ (i->identifier_type==identifier_type)) { -+ AST_LIST_REMOVE(headp,i,ast_MYSQL_id,entries); -+ free(i); -+ found=1; -+ break; -+ } -+ } -+ AST_LIST_UNLOCK(headp); -+ } -+ -+ if (found==0) { -+ ast_log(LOG_WARNING,"Could not find identifier %d, identifier_type %d in list to delete\n",identifier,identifier_type); -+ return(-1); -+ } else { -+ return(0); -+ } -+} -+ -+static int set_asterisk_int(struct ast_channel *chan, char *varname, int id) { -+ if( id>=0 ) { -+ char s[100] = ""; -+ snprintf(s, sizeof(s)-1, "%d", id); -+#if EXTRA_LOG -+ ast_log(LOG_WARNING,"MYSQL: setting var '%s' to value '%s'\n",varname,s); -+#endif -+ pbx_builtin_setvar_helper(chan,varname,s); -+ } -+ return id; -+} -+ -+static int add_identifier_and_set_asterisk_int(struct ast_channel *chan, char *varname, int identifier_type, void *data) { -+ return set_asterisk_int(chan,varname,add_identifier(identifier_type,data)); -+} -+ -+static int safe_scan_int( char** data, char* delim, int def ) { -+ char* end; -+ int res = def; -+ char* s = strsep(data,delim); -+ if( s ) { -+ res = strtol(s,&end,10); -+ if (*end) res = def; /* not an integer */ -+ } -+ return res; -+} -+ -+/* MYSQL operations */ -+static int aMYSQL_connect(struct ast_channel *chan, char *data) { -+ -+ MYSQL *mysql; -+ -+ char *connid_var; -+ char *dbhost; -+ char *dbuser; -+ char *dbpass; -+ char *dbname; -+ -+ strsep(&data," "); // eat the first token, we already know it :P -+ -+ connid_var=strsep(&data," "); -+ dbhost=strsep(&data," "); -+ dbuser=strsep(&data," "); -+ dbpass=strsep(&data," "); -+ dbname=strsep(&data,"\n"); -+ -+ if( connid_var && dbhost && dbuser && dbpass && dbname ) { -+ mysql = mysql_init(NULL); -+ if (mysql) { -+ if (mysql_real_connect(mysql,dbhost,dbuser,dbpass,dbname,0,NULL,0)) { -+ add_identifier_and_set_asterisk_int(chan,connid_var,AST_MYSQL_ID_CONNID,mysql); -+ return 0; -+ } -+ else { -+ ast_log(LOG_WARNING,"mysql_real_connect(mysql,%s,%s,dbpass,%s,...) failed\n",dbhost,dbuser,dbname); -+ } -+ } -+ else { -+ ast_log(LOG_WARNING,"myslq_init returned NULL\n"); -+ } -+ } -+ else { -+ ast_log(LOG_WARNING,"MYSQL(connect is missing some arguments\n"); -+ } -+ -+ return -1; -+} -+ -+static int aMYSQL_query(struct ast_channel *chan, char *data) { -+ -+ MYSQL *mysql; -+ MYSQL_RES *mysqlres; -+ -+ char *resultid_var; -+ int connid; -+ char *querystring; -+ -+ strsep(&data," "); // eat the first token, we already know it :P -+ -+ resultid_var = strsep(&data," "); -+ connid = safe_scan_int(&data," ",-1); -+ querystring = strsep(&data,"\n"); -+ -+ if (resultid_var && (connid>=0) && querystring) { -+ if ((mysql=find_identifier(connid,AST_MYSQL_ID_CONNID))!=NULL) { -+ mysql_query(mysql,querystring); -+ if ((mysqlres=mysql_use_result(mysql))!=NULL) { -+ add_identifier_and_set_asterisk_int(chan,resultid_var,AST_MYSQL_ID_RESID,mysqlres); -+ return 0; -+ } -+ else if( mysql_field_count(mysql)==0 ) { -+ return 0; // See http://dev.mysql.com/doc/mysql/en/mysql_field_count.html -+ } -+ else { -+ ast_log(LOG_WARNING,"aMYSQL_query: mysql_store_result() failed on query %s\n",querystring); -+ } -+ } -+ else { -+ ast_log(LOG_WARNING,"aMYSQL_query: Invalid connection identifier %d passed in aMYSQL_query\n",connid); -+ } -+ } -+ else { -+ ast_log(LOG_WARNING,"aMYSQL_query: missing some arguments\n"); -+ } -+ -+ return -1; -+} -+ -+ -+static int aMYSQL_fetch(struct ast_channel *chan, char *data) { -+ -+ MYSQL_RES *mysqlres; -+ MYSQL_ROW mysqlrow; -+ -+ char *fetchid_var,*s5,*s6; -+ int resultid,numFields,j; -+ -+ strsep(&data," "); // eat the first token, we already know it :P -+ -+ fetchid_var = strsep(&data," "); -+ resultid = safe_scan_int(&data," ",-1); -+ -+ if (fetchid_var && (resultid>=0) ) { -+ if ((mysqlres=find_identifier(resultid,AST_MYSQL_ID_RESID))!=NULL) { -+ /* Grab the next row */ -+ if ((mysqlrow=mysql_fetch_row(mysqlres))!=NULL) { -+ numFields=mysql_num_fields(mysqlres); -+ for (j=0;j<numFields;j++) { -+ s5=strsep(&data," "); -+ if (s5==NULL) { -+ ast_log(LOG_WARNING,"ast_MYSQL_fetch: More fields (%d) than variables (%d)\n",numFields,j); -+ break; -+ } -+ s6=mysqlrow[j]; -+ pbx_builtin_setvar_helper(chan,s5, s6 ? s6 : "NULL"); -+ } -+#ifdef EXTRA_LOG -+ ast_log(LOG_WARNING,"ast_MYSQL_fetch: numFields=%d\n",numFields); -+#endif -+ set_asterisk_int(chan,fetchid_var,1); // try more rows -+ } else { -+#if EXTRA_LOG -+ ast_log(LOG_WARNING,"ast_MYSQL_fetch : EOF\n"); -+#endif -+ set_asterisk_int(chan,fetchid_var,0); // no more rows -+ } -+ return 0; -+ } -+ else { -+ ast_log(LOG_WARNING,"aMYSQL_fetch: Invalid result identifier %d passed\n",resultid); -+ } -+ } -+ else { -+ ast_log(LOG_WARNING,"aMYSQL_fetch: missing some arguments\n"); -+ } -+ -+ return -1; -+} -+ -+static int aMYSQL_clear(struct ast_channel *chan, char *data) { -+ -+ MYSQL_RES *mysqlres; -+ -+ int id; -+ strsep(&data," "); // eat the first token, we already know it :P -+ id = safe_scan_int(&data," \n",-1); -+ if ((mysqlres=find_identifier(id,AST_MYSQL_ID_RESID))==NULL) { -+ ast_log(LOG_WARNING,"Invalid result identifier %d passed in aMYSQL_clear\n",id); -+ } else { -+ mysql_free_result(mysqlres); -+ del_identifier(id,AST_MYSQL_ID_RESID); -+ } -+ -+ return 0; -+} -+ -+static int aMYSQL_disconnect(struct ast_channel *chan, char *data) { -+ -+ MYSQL *mysql; -+ int id; -+ strsep(&data," "); // eat the first token, we already know it :P -+ -+ id = safe_scan_int(&data," \n",-1); -+ if ((mysql=find_identifier(id,AST_MYSQL_ID_CONNID))==NULL) { -+ ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aMYSQL_disconnect\n",id); -+ } else { -+ mysql_close(mysql); -+ del_identifier(id,AST_MYSQL_ID_CONNID); -+ } -+ -+ return 0; -+} -+ -+static int MYSQL_exec(struct ast_channel *chan, void *data) -+{ -+ struct localuser *u; -+ int result; -+ -+#if EXTRA_LOG -+ fprintf(stderr,"MYSQL_exec: data=%s\n",(char*)data); -+#endif -+ -+ if (!data) { -+ ast_log(LOG_WARNING, "APP_MYSQL requires an argument (see manual)\n"); -+ return -1; -+ } -+ -+ LOCAL_USER_ADD(u); -+ result=0; -+ -+ ast_mutex_lock(&_mysql_mutex); -+ -+ if (strncasecmp("connect",data,strlen("connect"))==0) { -+ result=aMYSQL_connect(chan,ast_strdupa(data)); -+ } else if (strncasecmp("query",data,strlen("query"))==0) { -+ result=aMYSQL_query(chan,ast_strdupa(data)); -+ } else if (strncasecmp("fetch",data,strlen("fetch"))==0) { -+ result=aMYSQL_fetch(chan,ast_strdupa(data)); -+ } else if (strncasecmp("clear",data,strlen("clear"))==0) { -+ result=aMYSQL_clear(chan,ast_strdupa(data)); -+ } else if (strncasecmp("disconnect",data,strlen("disconnect"))==0) { -+ result=aMYSQL_disconnect(chan,ast_strdupa(data)); -+ } else { -+ ast_log(LOG_WARNING, "Unknown argument to MYSQL application : %s\n",(char *)data); -+ result=-1; -+ } -+ -+ ast_mutex_unlock(&_mysql_mutex); -+ -+ LOCAL_USER_REMOVE(u); -+ return result; -+ -+} -+ -+int unload_module(void) -+{ -+ STANDARD_HANGUP_LOCALUSERS; -+ return ast_unregister_application(app); -+} -+ -+int load_module(void) -+{ -+ struct MYSQLidshead *headp = &_mysql_ids_head; -+ AST_LIST_HEAD_INIT(headp); -+ return ast_register_application(app, MYSQL_exec, synopsis, descrip); -+} -+ -+char *description(void) -+{ -+ return tdesc; -+} -+ -+int usecount(void) -+{ -+ int res; -+ STANDARD_USECOUNT(res); -+ return res; -+} -+ -+char *key() -+{ -+ return ASTERISK_GPL_KEY; -+} -diff -ruN asterisk-1.0.7-orig/cdr/Makefile asterisk-1.0.7-2/cdr/Makefile ---- asterisk-1.0.7-orig/cdr/Makefile 2004-08-31 18:33:00.000000000 +0200 -+++ asterisk-1.0.7-2/cdr/Makefile 2005-03-19 17:38:06.000000000 +0100 -@@ -37,36 +37,36 @@ - # - # unixODBC stuff... - # --MODS+=$(shell if [ -f "/usr/include/odbcinst.h" ]; then echo "cdr_odbc.so"; fi) --MODS+=$(shell if [ -f "/usr/local/include/odbcinst.h" ]; then echo "cdr_odbc.so"; fi) -+#MODS+=$(shell if [ -f "/usr/include/odbcinst.h" ]; then echo "cdr_odbc.so"; fi) -+#MODS+=$(shell if [ -f "/usr/local/include/odbcinst.h" ]; then echo "cdr_odbc.so"; fi) - - # - # FreeTDS stuff... - # --MODS+=$(shell if [ -f "/usr/include/tds.h" ]; then echo "cdr_tds.so"; fi) --MODS+=$(shell if [ -f "/usr/local/include/tds.h" ]; then echo "cdr_tds.so"; fi) -+#MODS+=$(shell if [ -f "/usr/include/tds.h" ]; then echo "cdr_tds.so"; fi) -+#MODS+=$(shell if [ -f "/usr/local/include/tds.h" ]; then echo "cdr_tds.so"; fi) - - # - # PGSQL stuff... Autoconf anyone?? - # --MODS+=$(shell if [ -d /usr/local/pgsql/include ] || [ -d /usr/include/pgsql ] || [ -d /usr/local/include/pgsql ] || [ -d /opt/pgsql/include ] || [ -f /usr/include/libpq-fe.h ] ; then echo "cdr_pgsql.so"; fi) --CFLAGS+=$(shell if [ -d /usr/local/pgsql/include ]; then echo "-I/usr/local/pgsql/include"; fi) --CFLAGS+=$(shell if [ -d /usr/include/pgsql ]; then echo "-I/usr/include/pgsql"; fi) --CFLAGS+=$(shell if [ -d /usr/include/postgresql ]; then echo "-I/usr/include/postgresql"; fi) --CFLAGS+=$(shell if [ -d /usr/local/include/pgsql ]; then echo "-I/usr/local/include/pgsql"; fi) --CFLAGS+=$(shell if [ -d /opt/pgsql/include ]; then echo "-I/opt/pgsql/include"; fi) -+#MODS+=$(shell if [ -d /usr/local/pgsql/include ] || [ -d /usr/include/pgsql ] || [ -d /usr/local/include/pgsql ] || [ -d /opt/pgsql/include ] || [ -f /usr/include/libpq-fe.h ] ; then echo "cdr_pgsql.so"; fi) -+#CFLAGS+=$(shell if [ -d /usr/local/pgsql/include ]; then echo "-I/usr/local/pgsql/include"; fi) -+#CFLAGS+=$(shell if [ -d /usr/include/pgsql ]; then echo "-I/usr/include/pgsql"; fi) -+#CFLAGS+=$(shell if [ -d /usr/include/postgresql ]; then echo "-I/usr/include/postgresql"; fi) -+#CFLAGS+=$(shell if [ -d /usr/local/include/pgsql ]; then echo "-I/usr/local/include/pgsql"; fi) -+#CFLAGS+=$(shell if [ -d /opt/pgsql/include ]; then echo "-I/opt/pgsql/include"; fi) - #CFLAGS+=$(shell if [ -f /usr/include/libpq-fe.h ]; then echo "-I/usr/include"; fi) - MLFLAGS= --MLFLAGS+=$(shell if [ -d /usr/lib/pgsql ]; then echo "-L/usr/lib/pgsql"; fi) --MLFLAGS+=$(shell if [ -d /usr/local/pgsql/lib ]; then echo "-L/usr/local/pgsql/lib"; fi) --MLFLAGS+=$(shell if [ -d /usr/local/lib/pgsql ]; then echo "-L/usr/local/lib/pgsql"; fi) --MLFLAGS+=$(shell if [ -d /opt/pgsql/lib ]; then echo "-L/opt/pgsql/lib"; fi) --MLFLAGS+=$(shell if [ -f /usr/lib/libpq.so ]; then echo "-L/usr/lib"; fi) -+#MLFLAGS+=$(shell if [ -d /usr/lib/pgsql ]; then echo "-L/usr/lib/pgsql"; fi) -+#MLFLAGS+=$(shell if [ -d /usr/local/pgsql/lib ]; then echo "-L/usr/local/pgsql/lib"; fi) -+#MLFLAGS+=$(shell if [ -d /usr/local/lib/pgsql ]; then echo "-L/usr/local/lib/pgsql"; fi) -+#MLFLAGS+=$(shell if [ -d /opt/pgsql/lib ]; then echo "-L/opt/pgsql/lib"; fi) -+#MLFLAGS+=$(shell if [ -f /usr/lib/libpq.so ]; then echo "-L/usr/lib"; fi) - - # - # SQLIte stuff... - # --MODS+=$(shell if [ -f "/usr/include/sqlite.h" ]; then echo "cdr_sqlite.so"; fi) -+#MODS+=$(shell if [ -f "/usr/include/sqlite.h" ]; then echo "cdr_sqlite.so"; fi) - - all: depend $(MODS) - -@@ -89,8 +89,17 @@ - cdr_tds.so: cdr_tds.o - $(CC) $(SOLINK) -o $@ $< -ltds $(MLFLAGS) - -+cdr_mysql.o: cdr_mysql.c -+ $(CC) $(CFLAGS) $(MYSQL_CFLAGS) -c -o $@ $< -+ -+cdr_mysql.so: cdr_mysql.o -+ $(CC) $(SOLINK) -o $@ $< $(LDFLAGS_EXTRA) -lmysqlclient -lz $(MYSQL_LFLAGS) -+ -+cdr_pgsql.o: cdr_pgsql.c -+ $(CC) $(CFLAGS) $(PGSQL_CFLAGS) -c -o $@ $< -+ - cdr_pgsql.so: cdr_pgsql.o -- $(CC) $(SOLINK) -o $@ $< -lpq -lz $(MLFLAGS) -+ $(CC) $(SOLINK) -o $@ $< $(LDFLAGS_EXTRA) -lpq -lz $(PGSQL_LFLAGS) - - cdr_sqlite.so: cdr_sqlite.o - $(CC) $(SOLINK) -o $@ $< -lsqlite $(MLFLAGS) -diff -ruN asterisk-1.0.7-orig/cdr/cdr_mysql.c asterisk-1.0.7-2/cdr/cdr_mysql.c ---- asterisk-1.0.7-orig/cdr/cdr_mysql.c 1970-01-01 01:00:00.000000000 +0100 -+++ asterisk-1.0.7-2/cdr/cdr_mysql.c 2005-03-19 17:46:30.000000000 +0100 -@@ -0,0 +1,450 @@ -+/* -+ * Asterisk -- A telephony toolkit for Linux. -+ * -+ * MySQL CDR logger -+ * -+ * James Sharp <jsharp@psychoses.org> -+ * -+ * Modified August 2003 -+ * Tilghman Lesher <asterisk__cdr__cdr_mysql__200308@the-tilghman.com> -+ * -+ * This program is free software, distributed under the terms of -+ * the GNU General Public License. -+ * -+ */ -+ -+#include <sys/types.h> -+#include <asterisk/config.h> -+#include <asterisk/options.h> -+#include <asterisk/channel.h> -+#include <asterisk/cdr.h> -+#include <asterisk/module.h> -+#include <asterisk/logger.h> -+#include <asterisk/cli.h> -+#include "../asterisk.h" -+ -+#include <stdio.h> -+#include <string.h> -+ -+#include <stdlib.h> -+#include <unistd.h> -+#include <time.h> -+ -+#include <mysql/mysql.h> -+#include <mysql/errmsg.h> -+ -+#define DATE_FORMAT "%Y-%m-%d %T" -+ -+static char *desc = "MySQL CDR Backend"; -+static char *name = "mysql"; -+static char *config = "cdr_mysql.conf"; -+static char *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *dbsock = NULL, *dbtable = NULL; -+static int hostname_alloc = 0, dbname_alloc = 0, dbuser_alloc = 0, password_alloc = 0, dbsock_alloc = 0, dbtable_alloc = 0; -+static int dbport = 0; -+static int connected = 0; -+static time_t connect_time = 0; -+static int records = 0; -+static int totalrecords = 0; -+static int userfield = 0; -+ -+AST_MUTEX_DEFINE_STATIC(mysql_lock); -+ -+static MYSQL mysql; -+ -+static char cdr_mysql_status_help[] = -+"Usage: cdr mysql status\n" -+" Shows current connection status for cdr_mysql\n"; -+ -+static int handle_cdr_mysql_status(int fd, int argc, char *argv[]) -+{ -+ if (connected) { -+ char status[256], status2[100] = ""; -+ int ctime = time(NULL) - connect_time; -+ if (dbport) -+ snprintf(status, 255, "Connected to %s@%s, port %d", dbname, hostname, dbport); -+ else if (dbsock) -+ snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock); -+ else -+ snprintf(status, 255, "Connected to %s@%s", dbname, hostname); -+ -+ if (dbuser && *dbuser) -+ snprintf(status2, 99, " with username %s", dbuser); -+ if (dbtable && *dbtable) -+ snprintf(status2, 99, " using table %s", dbtable); -+ if (ctime > 31536000) { -+ ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); -+ } else if (ctime > 86400) { -+ ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); -+ } else if (ctime > 3600) { -+ ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60); -+ } else if (ctime > 60) { -+ ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60); -+ } else { -+ ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime); -+ } -+ if (records == totalrecords) -+ ast_cli(fd, " Wrote %d records since last restart.\n", totalrecords); -+ else -+ ast_cli(fd, " Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records); -+ return RESULT_SUCCESS; -+ } else { -+ ast_cli(fd, "Not currently connected to a MySQL server.\n"); -+ return RESULT_FAILURE; -+ } -+} -+ -+static struct ast_cli_entry cdr_mysql_status_cli = -+ { { "cdr", "mysql", "status", NULL }, -+ handle_cdr_mysql_status, "Show connection status of cdr_mysql", -+ cdr_mysql_status_help, NULL }; -+ -+static int mysql_log(struct ast_cdr *cdr) -+{ -+ struct tm tm; -+ struct timeval tv; -+ struct localuser *u; -+ char *userfielddata = NULL; -+ char sqlcmd[2048], timestr[128]; -+ -+ ast_mutex_lock(&mysql_lock); -+ -+ memset(sqlcmd,0,2048); -+ -+ localtime_r(&cdr->start.tv_sec,&tm); -+ strftime(timestr,128,DATE_FORMAT,&tm); -+ -+ if ((!connected) && (hostname || dbsock) && dbuser && password && dbname && dbtable ) { -+ /* Attempt to connect */ -+ mysql_init(&mysql); -+ if (mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) { -+ connected = 1; -+ connect_time = time(NULL); -+ records = 0; -+ } else { -+ ast_log(LOG_ERROR, "cdr_mysql: cannot connect to database server %s. Call will not be logged\n", hostname); -+ } -+ } else { -+ /* Long connection - ping the server */ -+ int error; -+ if ((error = mysql_ping(&mysql))) { -+ connected = 0; -+ records = 0; -+ switch (error) { -+ case CR_SERVER_GONE_ERROR: -+ ast_log(LOG_ERROR, "cdr_mysql: Server has gone away\n"); -+ break; -+ default: -+ ast_log(LOG_ERROR, "cdr_mysql: Unknown connection error\n"); -+ } -+ } -+ } -+ -+ if (connected) { -+ char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL; -+#ifdef MYSQL_LOGUNIQUEID -+ char *uniqueid=NULL; -+#endif -+ -+ /* Maximum space needed would be if all characters needed to be escaped, plus a trailing NULL */ -+ if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL) -+ mysql_real_escape_string(&mysql, clid, cdr->clid, strlen(cdr->clid)); -+ if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL) -+ mysql_real_escape_string(&mysql, dcontext, cdr->dcontext, strlen(cdr->dcontext)); -+ if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL) -+ mysql_real_escape_string(&mysql, channel, cdr->channel, strlen(cdr->channel)); -+ if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL) -+ mysql_real_escape_string(&mysql, dstchannel, cdr->dstchannel, strlen(cdr->dstchannel)); -+ if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL) -+ mysql_real_escape_string(&mysql, lastapp, cdr->lastapp, strlen(cdr->lastapp)); -+ if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL) -+ mysql_real_escape_string(&mysql, lastdata, cdr->lastdata, strlen(cdr->lastdata)); -+#ifdef MYSQL_LOGUNIQUEID -+ if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL) -+ mysql_real_escape_string(&mysql, uniqueid, cdr->uniqueid, strlen(cdr->uniqueid)); -+#endif -+ -+ if (userfield && ((userfielddata = alloca(strlen(cdr->userfield) * 2 + 1)) != NULL)) -+ mysql_real_escape_string(&mysql, userfielddata, cdr->userfield, strlen(cdr->userfield)); -+ -+ /* Check for all alloca failures above at once */ -+#ifdef MYSQL_LOGUNIQUEID -+ if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid)) { -+#else -+ if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata)) { -+#endif -+ ast_log(LOG_ERROR, "cdr_mysql: Out of memory error (insert fails)\n"); -+ ast_mutex_unlock(&mysql_lock); -+ return -1; -+ } -+ -+ ast_log(LOG_DEBUG,"cdr_mysql: inserting a CDR record.\n"); -+ -+ if (userfield && userfielddata) -+ { -+#ifdef MYSQL_LOGUNIQUEID -+ sprintf(sqlcmd,"INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s','%s')",dbtable,timestr,clid,cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata,cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, uniqueid, userfielddata); -+#else -+ sprintf(sqlcmd,"INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')",dbtable,timestr,clid,cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata,cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, userfielddata); -+#endif -+ } -+ else -+ { -+#ifdef MYSQL_LOGUNIQUEID -+ sprintf(sqlcmd,"INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')",dbtable,timestr,clid,cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata,cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, uniqueid); -+#else -+ sprintf(sqlcmd,"INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s')",dbtable,timestr,clid,cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata,cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode); -+#endif -+ } -+ -+ ast_log(LOG_DEBUG,"cdr_mysql: SQL command as follows: %s\n",sqlcmd); -+ -+ if (mysql_real_query(&mysql,sqlcmd,strlen(sqlcmd))) { -+ ast_log(LOG_ERROR,"Failed to insert into database."); -+ ast_mutex_unlock(&mysql_lock); -+ return -1; -+ } else { -+ records++; -+ totalrecords++; -+ } -+ } -+ ast_mutex_unlock(&mysql_lock); -+ return 0; -+} -+ -+char *description(void) -+{ -+ return desc; -+} -+ -+static int my_unload_module(void) -+{ -+ ast_cli_unregister(&cdr_mysql_status_cli); -+ if (connected) { -+ mysql_close(&mysql); -+ connected = 0; -+ records = 0; -+ } -+ if (hostname && hostname_alloc) { -+ free(hostname); -+ hostname = NULL; -+ hostname_alloc = 0; -+ } -+ if (dbname && dbname_alloc) { -+ free(dbname); -+ dbname = NULL; -+ dbname_alloc = 0; -+ } -+ if (dbuser && dbuser_alloc) { -+ free(dbuser); -+ dbuser = NULL; -+ dbuser_alloc = 0; -+ } -+ if (dbsock && dbsock_alloc) { -+ free(dbsock); -+ dbsock = NULL; -+ dbsock_alloc = 0; -+ } -+ if (dbtable && dbtable_alloc) { -+ free(dbtable); -+ dbtable = NULL; -+ dbtable_alloc = 0; -+ } -+ if (password && password_alloc) { -+ free(password); -+ password = NULL; -+ password_alloc = 0; -+ } -+ dbport = 0; -+ ast_cdr_unregister(name); -+ return 0; -+} -+ -+static int my_load_module(void) -+{ -+ int res; -+ struct ast_config *cfg; -+ struct ast_variable *var; -+ char *tmp; -+ -+ cfg = ast_config_load(config); -+ if (!cfg) { -+ ast_log(LOG_WARNING, "Unable to load config for mysql CDR's: %s\n", config); -+ return 0; -+ } -+ -+ var = ast_variable_browse(cfg, "global"); -+ if (!var) { -+ /* nothing configured */ -+ return 0; -+ } -+ -+ tmp = ast_variable_retrieve(cfg,"global","hostname"); -+ if (tmp) { -+ hostname = malloc(strlen(tmp) + 1); -+ if (hostname != NULL) { -+ hostname_alloc = 1; -+ strcpy(hostname,tmp); -+ } else { -+ ast_log(LOG_ERROR,"Out of memory error.\n"); -+ return -1; -+ } -+ } else { -+ ast_log(LOG_WARNING,"MySQL server hostname not specified. Assuming localhost\n"); -+ hostname = "localhost"; -+ } -+ -+ tmp = ast_variable_retrieve(cfg,"global","dbname"); -+ if (tmp) { -+ dbname = malloc(strlen(tmp) + 1); -+ if (dbname != NULL) { -+ dbname_alloc = 1; -+ strcpy(dbname,tmp); -+ } else { -+ ast_log(LOG_ERROR,"Out of memory error.\n"); -+ return -1; -+ } -+ } else { -+ ast_log(LOG_WARNING,"MySQL database not specified. Assuming asteriskcdrdb\n"); -+ dbname = "asteriskcdrdb"; -+ } -+ -+ tmp = ast_variable_retrieve(cfg,"global","user"); -+ if (tmp) { -+ dbuser = malloc(strlen(tmp) + 1); -+ if (dbuser != NULL) { -+ dbuser_alloc = 1; -+ strcpy(dbuser,tmp); -+ } else { -+ ast_log(LOG_ERROR,"Out of memory error.\n"); -+ return -1; -+ } -+ } else { -+ ast_log(LOG_WARNING,"MySQL database user not specified. Assuming root\n"); -+ dbuser = "root"; -+ } -+ -+ tmp = ast_variable_retrieve(cfg,"global","sock"); -+ if (tmp) { -+ dbsock = malloc(strlen(tmp) + 1); -+ if (dbsock != NULL) { -+ dbsock_alloc = 1; -+ strcpy(dbsock,tmp); -+ } else { -+ ast_log(LOG_ERROR,"Out of memory error.\n"); -+ return -1; -+ } -+ } else { -+ ast_log(LOG_WARNING,"MySQL database sock file not specified. Using default\n"); -+ dbsock = NULL; -+ } -+ -+ tmp = ast_variable_retrieve(cfg,"global","table"); -+ if (tmp) { -+ dbtable = malloc(strlen(tmp) + 1); -+ if (dbtable != NULL) { -+ dbtable_alloc = 1; -+ strcpy(dbtable,tmp); -+ } else { -+ ast_log(LOG_ERROR,"Out of memory error.\n"); -+ return -1; -+ } -+ } else { -+ ast_log(LOG_NOTICE,"MySQL database table not specified. Assuming \"cdr\"\n"); -+ dbtable = "cdr"; -+ } -+ -+ tmp = ast_variable_retrieve(cfg,"global","password"); -+ if (tmp) { -+ password = malloc(strlen(tmp) + 1); -+ if (password != NULL) { -+ password_alloc = 1; -+ strcpy(password,tmp); -+ } else { -+ ast_log(LOG_ERROR,"Out of memory error.\n"); -+ return -1; -+ } -+ } else { -+ ast_log(LOG_WARNING,"MySQL database password not specified. Assuming blank\n"); -+ password = ""; -+ } -+ -+ tmp = ast_variable_retrieve(cfg,"global","port"); -+ if (tmp) { -+ if (sscanf(tmp,"%d",&dbport) < 1) { -+ ast_log(LOG_WARNING,"Invalid MySQL port number. Using default\n"); -+ dbport = 0; -+ } -+ } -+ -+ tmp = ast_variable_retrieve(cfg,"global","userfield"); -+ if (tmp) { -+ if (sscanf(tmp,"%d",&userfield) < 1) { -+ ast_log(LOG_WARNING,"Invalid MySQL configurtation file\n"); -+ userfield = 0; -+ } -+ } -+ -+ ast_config_destroy(cfg); -+ -+ ast_log(LOG_DEBUG,"cdr_mysql: got hostname of %s\n",hostname); -+ ast_log(LOG_DEBUG,"cdr_mysql: got port of %d\n",dbport); -+ if (dbsock) -+ ast_log(LOG_DEBUG,"cdr_mysql: got sock file of %s\n",dbsock); -+ ast_log(LOG_DEBUG,"cdr_mysql: got user of %s\n",dbuser); -+ ast_log(LOG_DEBUG,"cdr_mysql: got dbname of %s\n",dbname); -+ ast_log(LOG_DEBUG,"cdr_mysql: got password of %s\n",password); -+ -+ mysql_init(&mysql); -+ -+ if (!mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) { -+ ast_log(LOG_ERROR, "Failed to connect to mysql database %s on %s.\n", dbname, hostname); -+ connected = 0; -+ records = 0; -+ } else { -+ ast_log(LOG_DEBUG,"Successfully connected to MySQL database.\n"); -+ connected = 1; -+ records = 0; -+ connect_time = time(NULL); -+ } -+ -+ res = ast_cdr_register(name, desc, mysql_log); -+ if (res) { -+ ast_log(LOG_ERROR, "Unable to register MySQL CDR handling\n"); -+ } else { -+ res = ast_cli_register(&cdr_mysql_status_cli); -+ } -+ -+ return res; -+} -+ -+int load_module(void) -+{ -+ return my_load_module(); -+} -+ -+int unload_module(void) -+{ -+ return my_unload_module(); -+} -+ -+int reload(void) -+{ -+ my_unload_module(); -+ return my_load_module(); -+} -+ -+int usecount(void) -+{ -+ /* Simplistic use count */ -+ if (ast_mutex_trylock(&mysql_lock)) { -+ return 1; -+ } else { -+ ast_mutex_unlock(&mysql_lock); -+ return 0; -+ } -+} -+ -+char *key() -+{ -+ return ASTERISK_GPL_KEY; -+} -diff -ruN asterisk-1.0.7-orig/configs/cdr_mysql.conf.sample asterisk-1.0.7-2/configs/cdr_mysql.conf.sample ---- asterisk-1.0.7-orig/configs/cdr_mysql.conf.sample 1970-01-01 01:00:00.000000000 +0100 -+++ asterisk-1.0.7-2/configs/cdr_mysql.conf.sample 2005-01-21 02:43:19.000000000 +0100 -@@ -0,0 +1,21 @@ -+; -+; Note - if the database server is hosted on the same machine as the -+; asterisk server, you can achieve a local Unix socket connection by -+; setting hostname=localhost -+; -+; port and sock are both optional parameters. If hostname is specified -+; and is not "localhost", then cdr_mysql will attempt to connect to the -+; port specified or use the default port. If hostname is not specified -+; or if hostname is "localhost", then cdr_mysql will attempt to connect -+; to the socket file specified by sock or otherwise use the default socket -+; file. -+; -+;[global] -+;hostname=database.host.name -+;dbname=asteriskcdrdb -+;table=cdr -+;password=password -+;user=asteriskcdruser -+;port=3306 -+;sock=/tmp/mysql.sock -+;userfield=1 |