[Pkg-voip-commits] [asterisk] 03/03: Refresh patches

tzafrir at debian.org tzafrir at debian.org
Thu Dec 19 11:00:29 UTC 2013


This is an automated email from the git hooks/post-receive script.

tzafrir pushed a commit to branch squeeze
in repository asterisk.

commit 2c881c35572e0df5d7f8e0fca4df6aac2cb2c2e1
Author: Tzafrir Cohen <tzafrir at debian.org>
Date:   Thu Dec 19 12:59:46 2013 +0200

    Refresh patches
---
 CHANGES                            |  12 ++
 Makefile                           |  10 +-
 Makefile.moddir_rules              |   2 +-
 README-SERIOUSLY.bestpractices.txt |  75 +++++++++
 UPGRADE.txt                        |   9 ++
 apps/Makefile                      |  18 +++
 apps/app_meetme.c                  |  16 +-
 apps/app_milliwatt.c               |   2 +-
 apps/app_sms.c                     |   3 +-
 apps/app_voicemail.c               |   5 +-
 channels/chan_agent.c              |  18 +--
 channels/chan_dahdi.c              |  47 +++---
 channels/chan_iax2.c               |  88 ++++++----
 channels/chan_local.c              |   3 +
 channels/chan_sip.c                | 317 ++++++++++++++++++++++++++++++-------
 channels/chan_skinny.c             | 156 +++++++++++++++---
 configs/http.conf.sample           |   5 +
 configs/manager.conf.sample        |  11 ++
 configs/sip.conf.sample            |  27 +++-
 configs/skinny.conf.sample         |   9 ++
 contrib/scripts/astgenkey          |   4 +
 contrib/scripts/astgenkey.8        |  15 +-
 contrib/scripts/autosupport.8      |   2 +-
 contrib/scripts/safe_asterisk      |  11 +-
 contrib/scripts/safe_asterisk.8    |   4 +-
 debian/patches/AST-2013-006        |   9 +-
 debian/patches/AST-2013-007        | 107 +++++--------
 debian/patches/ASTERISK-20658      |  13 +-
 doc/asterisk.8                     |  94 +++++------
 funcs/func_db.c                    |  20 ++-
 funcs/func_devstate.c              |   6 +-
 funcs/func_env.c                   |  14 +-
 funcs/func_lock.c                  |  21 ++-
 funcs/func_realtime.c              |  59 +++++--
 funcs/func_shell.c                 |  19 ++-
 include/asterisk/channel.h         |   6 +
 include/asterisk/devicestate.h     |  16 +-
 include/asterisk/event_defs.h      |   8 +-
 include/asterisk/pbx.h             |  54 +++++++
 main/Makefile                      |  11 --
 main/asterisk.c                    |  12 ++
 main/bridging.c                    |   7 +-
 main/channel.c                     |   6 +-
 main/config.c                      |  36 +++--
 main/devicestate.c                 |  51 +++---
 main/event.c                       |   1 +
 main/features.c                    |  17 +-
 main/http.c                        |  24 ++-
 main/loader.c                      |   6 +
 main/manager.c                     | 184 +++++++++++++++++----
 main/pbx.c                         | 252 ++++++++++++++++++++++++++++-
 main/rtp.c                         |   4 +-
 main/tcptls.c                      |  19 ++-
 main/udptl.c                       |  48 +++---
 main/utils.c                       |  27 ++--
 res/res_agi.c                      |   4 +-
 res/res_musiconhold.c              |   2 +-
 sounds/sounds.xml                  |   1 -
 58 files changed, 1557 insertions(+), 470 deletions(-)

diff --git a/CHANGES b/CHANGES
index f4b4fbd..5598adc 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,18 @@
 ======================================================================
 
 ------------------------------------------------------------------------------
+--- Functionality changes since Asterisk 1.6.2.20                -------------
+------------------------------------------------------------------------------
+
+SIP Changes
+-----------
+    * Due to potential username discovery vulnerabilities, the 'nat' setting in sip.conf
+      now defaults to yes. It is very important that phones requiring nat=no be
+      specifically set as such instead of relying on the default setting. If at all
+      possible, all devices should have nat settings configured in the general section as
+      opposed to configuring nat per-device.
+
+------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 1.6.1 to Asterisk 1.6.2  -------------
 ------------------------------------------------------------------------------
 
diff --git a/Makefile b/Makefile
index 832f879..e5e15ef 100644
--- a/Makefile
+++ b/Makefile
@@ -465,7 +465,6 @@ dist-clean: distclean
 
 distclean: $(SUBDIRS_DIST_CLEAN) _clean
 	@$(MAKE) -C menuselect dist-clean
-	@$(MAKE) -C sounds dist-clean
 	rm -f menuselect.makeopts makeopts menuselect-tree menuselect.makedeps
 	rm -f makeopts.embed_rules
 	rm -f config.log config.status config.cache
@@ -475,6 +474,10 @@ distclean: $(SUBDIRS_DIST_CLEAN) _clean
 	rm -rf doc/api
 	rm -f build_tools/menuselect-deps
 
+# tarballs distributed by Digium include sounds
+all-clean: distclean
+	@$(MAKE) -C sounds dist-clean
+
 datafiles: _all
 	CFLAGS="$(_ASTCFLAGS) $(ASTCFLAGS)" build_tools/mkpkgconfig $(DESTDIR)$(libdir)/pkgconfig;
 # Should static HTTP be installed during make samples or even with its own target ala
@@ -613,7 +616,7 @@ oldmodcheck:
 	fi
 
 badshell:
-ifneq ($(findstring ~,$(DESTDIR)),)
+ifneq ($(findstring ~/,$(DESTDIR)),)
 	@echo "Your shell doesn't do ~ expansion when expected (specifically, when doing \"make install DESTDIR=~/path\")."
 	@echo "Try replacing ~ with \$$HOME, as in \"make install DESTDIR=\$$HOME/path\"."
 	@exit 1
@@ -966,6 +969,9 @@ txt: asterisk.txt
 asterisk.txt:
 	$(MAKE) -C doc/tex asterisk.txt
 
+h323-mak:
+	$(MAKE) -C channels h323/Makefile.ast
+
 .PHONY: menuselect
 .PHONY: main
 .PHONY: sounds
diff --git a/Makefile.moddir_rules b/Makefile.moddir_rules
index dab0c58..874ce6d 100644
--- a/Makefile.moddir_rules
+++ b/Makefile.moddir_rules
@@ -37,7 +37,7 @@ include $(ASTTOPDIR)/Makefile.rules
 # Use MODULE_EXCLUDE to specify additional modules to exclude.
 
 ifneq ($(MODULE_PREFIX),)
-    ALL_C_MODS:=
+    ALL_C_MODS:=$(MODS_C_ADDITIONAL)
     ALL_C_MODS+=$(foreach p,$(MODULE_PREFIX),$(patsubst %.c,%,$(wildcard $(p)_*.c)))
     ALL_CC_MODS:=
     ALL_CC_MODS+=$(foreach p,$(MODULE_PREFIX),$(patsubst %.cc,%,$(wildcard $(p)_*.cc)))
diff --git a/README-SERIOUSLY.bestpractices.txt b/README-SERIOUSLY.bestpractices.txt
index 0e2af3b..281d0d3 100644
--- a/README-SERIOUSLY.bestpractices.txt
+++ b/README-SERIOUSLY.bestpractices.txt
@@ -23,6 +23,12 @@ Sections
 * Reducing Pattern Match Typos: 
         Using the 'same' prefix, or using Goto()
 
+* Manager Class Authorizations:
+        Recognizing potential issues with certain classes of authorization
+
+* Avoid Privilege Escalations:
+        Disable the ability to execute functions that may escalate privileges
+
 ----------------
 Additional Links
 ----------------
@@ -293,3 +299,72 @@ same => n,Hangup()
 exten => error,1,Verbose(2,Unable to lookup technology or device for extension)
 same => n,Playback(silence/1&num-not-in-db)
 same => n,Hangup()
+
+
+============================
+Manager Class Authorizations
+============================
+
+Manager accounts have associated class authorizations that define what actions
+and events that account can execute/receive.  In order to run Asterisk commands
+or dialplan applications that affect the system Asterisk executes on, the
+"system" class authorization should be set on the account.
+
+However, Manager commands that originate new calls into the Asterisk dialplan
+have the potential to alter or affect the system as well, even though the
+class authorization for origination commands is "originate".  Take, for example,
+the Originate manager command:
+
+Action: Originate
+Channel: SIP/foo
+Exten: s
+Context: default
+Priority: 1
+Application: System
+Data: echo hello world!
+
+This manager command will attempt to execute an Asterisk application, System,
+which is normally associated with the "system" class authorication.  While some
+checks have been put into Asterisk to take this into account, certain dialplan
+configurations and/or clever manipulation of the Originate manager action can
+circumvent these checks.  For example, take the following dialplan:
+
+exten => s,1,Verbose(Incoming call)
+same => n,MixMonitor(foo.wav,,${EXEC_COMMAND})
+same => n,Dial(SIP/bar)
+same => n,Hangup()
+
+Whatever has been defined in the variable EXEC_COMMAND will be executed after
+MixMonitor has finished recording the call.  The dialplan writer may have
+intended that this variable to be set by some other location in the dialplan;
+however, the Manager action Originate allows for channel variables to be set by
+the account initiating the new call.  This could allow the Originate action to
+execute some command on the system by setting the EXEC_COMMAND dialplan variable
+in the Variable: header.
+
+In general, you should treat the Manager class authorization "originate" the
+same as the class authorization "system".  Good system configuration, such as
+not running Asterisk as root, can prevent serious problems from arising when
+allowing external connections to originate calls into Asterisk.
+
+===========================
+Avoid Privilege Escalations
+===========================
+
+External control protocols, such as Manager, often have the ability to get and
+set channel variables; which allows the execution of dialplan functions.
+
+Dialplan functions within Asterisk are incredibly powerful, which is wonderful
+for building applications using Asterisk. But during the read or write
+execution, certain diaplan functions do much more. For example, reading the
+SHELL() function can execute arbitrary commands on the system Asterisk is
+running on. Writing to the FILE() function can change any file that Asterisk has
+write access to.
+
+When these functions are executed from an external protocol, that execution
+could result in a privilege escalation. Asterisk can inhibit the execution of
+these functions, if live_dangerously in the [options] section of asterisk.conf
+is set to no.
+
+For backwards compatibility, live_dangerously defaults to yes, and must be
+explicitly set to no to enable this privilege escalation protection.
diff --git a/UPGRADE.txt b/UPGRADE.txt
index bd02694..c3a22b7 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -18,6 +18,15 @@
 ===
 ===========================================================
 
+from 1.6.2.9-2+squeeze11 to 1.6.2.9-2+squeeze12 (backported from 1.8.24.1):
+* Certain dialplan functions have been marked as 'dangerous', and may only be
+  executed from the dialplan. Execution from extenal sources (AMI's GetVar and
+  SetVar actions; etc.) may be inhibited by setting live_dangerously in the
+  [options] section of asterisk.conf to no. SHELL(), channel locking, and direct
+  file read/write functions are marked as dangerous. DB_DELETE() and
+  REALTIME_DESTROY() are marked as dangerous for reads, but can now safely
+  accept writes (which ignore the provided value).
+
 From 1.6.1 to 1.6.2:
 
 * SIP no longer sends the 183 progress message for early media by
diff --git a/apps/Makefile b/apps/Makefile
index 4343f3c..91a6cc6 100644
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -15,6 +15,8 @@ MODULE_PREFIX=app
 MENUSELECT_CATEGORY=APPS
 MENUSELECT_DESCRIPTION=Applications
 
+MODS_C_ADDITIONAL=app_voicemail_imap app_voicemail_odbc
+
 MENUSELECT_OPTS_app_directory:=$(MENUSELECT_OPTS_app_voicemail)
 ifneq ($(findstring ODBC_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),)
   MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_ODBC_STORAGE)
@@ -25,6 +27,22 @@ endif
 
 all: _all
 
+add_depends_cmd = sed -i -e '/^\/\*\*\* MODULEINFO/a\\t<depend>$(1)<\/depend>'
+app_voicemail_imap.c: app_voicemail.c
+	cp $< $@
+	$(call add_depends_cmd,imap_tk) $@
+	$(call add_depends_cmd,openssl) $@
+app_voicemail_odbc.c: app_voicemail.c
+	cp $< $@
+	$(call add_depends_cmd,unixodbc) $@
+	$(call add_depends_cmd,ltdl) $@
+
+clean::
+	rm -f app_voicemail_*.c
+
+app_voicemail_imap.o: _ASTCFLAGS+=-DIMAP_STORAGE
+app_voicemail_odbc.o: _ASTCFLAGS+=-DODBC_STORAGE
+
 include $(ASTTOPDIR)/Makefile.moddir_rules
 
 ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index f7f7740..b986d6f 100644
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -2279,7 +2279,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
 
 	/* This device changed state now - if this is the first user */
 	if (conf->users == 1)
-		ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
+		ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
 
 	ast_mutex_unlock(&conf->playlock);
 
@@ -3355,7 +3355,7 @@ bailoutandtrynormal:
 
 		/* Change any states */
 		if (!conf->users) {
-			ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
+			ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
 		}
 
 		/* Return the number of seconds the user was in the conf */
@@ -4662,8 +4662,8 @@ static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk
 				|| trunk_ref == exclude)
 				continue;
 			trunk_ref->state = state;
-			ast_devstate_changed(sla_state_to_devstate(state), 
-				"SLA:%s_%s", station->name, trunk->name);
+			ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
+					     "SLA:%s_%s", station->name, trunk->name);
 			break;
 		}
 	}
@@ -5162,8 +5162,8 @@ static void sla_handle_hold_event(struct sla_event *event)
 {
 	ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
 	event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
-	ast_devstate_changed(AST_DEVICE_ONHOLD, "SLA:%s_%s", 
-		event->station->name, event->trunk_ref->trunk->name);
+	ast_devstate_changed(AST_DEVICE_ONHOLD, AST_DEVSTATE_CACHABLE, "SLA:%s_%s",
+			     event->station->name, event->trunk_ref->trunk->name);
 	sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 
 		INACTIVE_TRUNK_REFS, event->trunk_ref);
 
@@ -5672,8 +5672,8 @@ static int sla_station_exec(struct ast_channel *chan, void *data)
 			sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
 		else {
 			trunk_ref->state = SLA_TRUNK_STATE_UP;
-			ast_devstate_changed(AST_DEVICE_INUSE, 
-				"SLA:%s_%s", station->name, trunk_ref->trunk->name);
+			ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE,
+					     "SLA:%s_%s", station->name, trunk_ref->trunk->name);
 		}
 	} else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
 		struct sla_ringing_trunk *ringing_trunk;
diff --git a/apps/app_milliwatt.c b/apps/app_milliwatt.c
index 44e992a..6176661 100644
--- a/apps/app_milliwatt.c
+++ b/apps/app_milliwatt.c
@@ -74,7 +74,7 @@ static void milliwatt_release(struct ast_channel *chan, void *data)
 static int milliwatt_generate(struct ast_channel *chan, void *data, int len, int samples)
 {
 	unsigned char buf[AST_FRIENDLY_OFFSET + 640];
-	const int maxsamples = ARRAY_LEN(buf);
+	const int maxsamples = ARRAY_LEN(buf) - (AST_FRIENDLY_OFFSET / sizeof(buf[0]));
 	int i, *indexp = (int *) data;
 	struct ast_frame wf = {
 		.frametype = AST_FRAME_VOICE,
diff --git a/apps/app_sms.c b/apps/app_sms.c
index 95fd05d..d797344 100644
--- a/apps/app_sms.c
+++ b/apps/app_sms.c
@@ -692,7 +692,7 @@ static void unpacksms16(unsigned char *i, unsigned char l, unsigned char *udh, i
 	}
 	while (l--) {
 		int v = *i++;
-		if (l--) {
+		if (l && l--) {
 			v = (v << 8) + *i++;
 		}
 		*o++ = v;
@@ -710,6 +710,7 @@ static int unpacksms(unsigned char dcs, unsigned char *i, unsigned char *udh, in
 	} else if (is8bit(dcs)) {
 		unpacksms8(i, l, udh, udhl, ud, udl, udhi);
 	} else {
+		l += l % 2;
 		unpacksms16(i, l, udh, udhl, ud, udl, udhi);
 	}
 	return l + 1;
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index ab43cf0..4186623 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -40,6 +40,9 @@ c-client (http://www.washington.edu/imap/
  *        with a plan to clean this up.
  */
 
+/*** MODULEINFO
+ ***/
+
 /*** MAKEOPTS
 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_voicemail.so apps/app_directory.o apps/app_directory.so">
 	<member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
@@ -4497,7 +4500,7 @@ static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format,
 				attach = newtmp;
 				ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
 			} else {
-				ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
+				ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
 					soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
 				ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
 			}
diff --git a/channels/chan_agent.c b/channels/chan_agent.c
index 248001f..4ed5baa 100644
--- a/channels/chan_agent.c
+++ b/channels/chan_agent.c
@@ -563,7 +563,7 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
 					p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
 			}
 			p->chan = NULL;
-			ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
+			ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
 			p->acknowledged = 0;
 		}
 	} else {
@@ -815,7 +815,7 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout)
 	} else {
 		/* Agent hung-up */
 		p->chan = NULL;
-		ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
+		ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
 	}
 
 	if (!res) {
@@ -936,7 +936,7 @@ static int agent_hangup(struct ast_channel *ast)
 				/* Recognize the hangup and pass it along immediately */
 				ast_hangup(p->chan);
 				p->chan = NULL;
-				ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
+				ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
 			}
 			ast_debug(1, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
 			if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) {
@@ -970,7 +970,7 @@ static int agent_hangup(struct ast_channel *ast)
 		if (persistent_agents)
 			dump_agents();
 	} else {
-		ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
+		ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
 	}
 
 	if (p->pending) {
@@ -1675,7 +1675,7 @@ static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long
 	set_agentbycallerid(p->logincallerid, NULL);
 	p->loginchan[0] ='\0';
 	p->logincallerid[0] = '\0';
-	ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
+	ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
 	if (persistent_agents)
 		dump_agents();
 
@@ -2187,7 +2187,7 @@ static int login_exec(struct ast_channel *chan, void *data)
 							check_availability(p, 0);
 						ast_mutex_unlock(&p->lock);
 						AST_LIST_UNLOCK(&agents);
-						ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
+						ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
 						while (res >= 0) {
 							ast_mutex_lock(&p->lock);
 							if (p->deferlogoff && p->chan) {
@@ -2208,7 +2208,7 @@ static int login_exec(struct ast_channel *chan, void *data)
 								if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
 									ast_debug(1, "Wrapup time for %s expired!\n", p->agent);
 									p->lastdisc = ast_tv(0, 0);
-									ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
+									ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
 									if (p->ackcall > 1)
 										check_beep(p, 0);
 									else
@@ -2258,7 +2258,7 @@ static int login_exec(struct ast_channel *chan, void *data)
 						ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
 						ast_verb(2, "Agent '%s' logged out\n", p->agent);
 						/* If there is no owner, go ahead and kill it now */
-						ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
+						ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
 						if (p->dead && !p->owner) {
 							ast_mutex_destroy(&p->lock);
 							ast_mutex_destroy(&p->app_lock);
@@ -2424,7 +2424,7 @@ static void reload_agents(void)
 				cur_agent->logincallerid[0] = '\0';
 			if (cur_agent->loginstart == 0)
 				time(&cur_agent->loginstart);
-			ast_devstate_changed(AST_DEVICE_UNKNOWN, "Agent/%s", cur_agent->agent);	
+			ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent/%s", cur_agent->agent);	
 		}
 	}
 	AST_LIST_UNLOCK(&agents);
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index ebc572f..f372220 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -5018,10 +5018,10 @@ static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int
 	case AST_OPTION_ECHOCAN:
 		cp = (char *) data;
 		if (*cp) {
-			ast_debug(1, "Enabling echo cancelation on %s\n", chan->name);
+			ast_debug(1, "Enabling echo cancellation on %s\n", chan->name);
 			dahdi_enable_ec(p);
 		} else {
-			ast_debug(1, "Disabling echo cancelation on %s\n", chan->name);
+			ast_debug(1, "Disabling echo cancellation on %s\n", chan->name);
 			dahdi_disable_ec(p);
 		}
 		break;
@@ -6806,7 +6806,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
 
 	/* Hang up if we don't really exist */
 	if (idx < 0)	{
-		ast_log(LOG_WARNING, "We dont exist?\n");
+		ast_log(LOG_WARNING, "We don't exist?\n");
 		ast_mutex_unlock(&p->lock);
 		return NULL;
 	}
@@ -7665,7 +7665,7 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
 	/* Configure the new channel jb */
 	ast_jb_configure(tmp, &global_jbconf);
 
-	ast_devstate_changed_literal(ast_state_chan2dev(state), tmp->name);
+	ast_devstate_changed_literal(ast_state_chan2dev(state), AST_DEVSTATE_CACHABLE, tmp->name);
 
 	for (v = i->vars ; v ; v = v->next)
 		pbx_builtin_setvar_helper(tmp, v->name, v->value);
@@ -10807,10 +10807,10 @@ static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t g
 				return 1;
 		}
 #endif
-		if (!(p->radio || (p->oprmode < 0)))
+
+		/* Trust hook state */
+		if (p->sig && !(p->radio || (p->oprmode < 0)))
 		{
-			if (!p->sig || (p->sig == SIG_FXSLS))
-				return 1;
 			/* Check hook state */
 			if (p->subs[SUB_REAL].dfd > -1) {
 				memset(&par, 0, sizeof(par));
@@ -10820,28 +10820,30 @@ static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t g
 				res = 0;
 				par.rxisoffhook = 0;
 			}
+
 			if (res) {
 				ast_log(LOG_WARNING, "Unable to check hook state on channel %d: %s\n", p->channel, strerror(errno));
-			} else if ((p->sig == SIG_FXSKS) || (p->sig == SIG_FXSGS)) {
-				/* When "onhook" that means no battery on the line, and thus
-				  it is out of service..., if it's on a TDM card... If it's a channel
-				  bank, there is no telling... */
+			}
+			else if ((p->sig != SIG_FXSKS) && (p->sig != SIG_FXSGS) && (p->sig != SIG_FXSLS)) {
+				if (par.rxisoffhook) {
+					ast_debug(1, "Channel %d off hook, can't use\n", p->channel);
+					/* Not available when the other end is off hook */
+					return 0;
+				}
+			}
+#ifdef DAHDI_CHECK_HOOKSTATE
+			} else { /* FXO channel case (SIG_FXS--) */
+				/* Channel bank (using CAS), "onhook" does not necessarily means out of service, so return 1 */
 				if (par.rxbits > -1)
 					return 1;
+				/* TDM FXO card, "onhook" means out of service (no battery on the line) */
 				if (par.rxisoffhook)
 					return 1;
 				else
 					return 0;
-			} else if (par.rxisoffhook) {
-				ast_debug(1, "Channel %d off hook, can't use\n", p->channel);
-				/* Not available when the other end is off hook */
-#ifdef DAHDI_CHECK_HOOKSTATE
-				return 0;
-#else
-				return 1;
 #endif
-			}
 		}
+
 		return 1;
 	}
 
@@ -12344,6 +12346,8 @@ static void dahdi_pri_message(struct pri *pri, char *s)
 		}
 		if (dchancount > 1 && (span > -1))
 			ast_verbose("[Span %d D-Channel %d]%s", span, dchan, s);
+		else if (span > -1)
+			ast_verbose("%d %s", span+1, s);
 		else
 			ast_verbose("%s", s);
 	} else
@@ -12385,6 +12389,8 @@ static void dahdi_pri_error(struct pri *pri, char *s)
 		}
 		if ((dchancount > 1) && (span > -1))
 			ast_log(LOG_ERROR, "[Span %d D-Channel %d] PRI: %s", span, dchan, s);
+		else if (span > -1)
+			ast_log(LOG_ERROR, "%d %s", span+1, s);
 		else
 			ast_log(LOG_ERROR, "%s", s);
 	} else
@@ -16613,7 +16619,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 					confp->chan.sig = SIG_BRI_PTMP;
 					confp->pri.nodetype = PRI_CPE;
 				} else if (!strcasecmp(v->value, "bri_net_ptmp")) {
-					ast_log(LOG_WARNING, "How cool would it be if someone implemented this mode!  For now, sucks for you. (line %d)\n", v->lineno);
+					confp->chan.sig = SIG_BRI_PTMP;
+					confp->pri.nodetype = PRI_NETWORK;
 				} else if (!strcasecmp(v->value, "gr303fxoks_net")) {
 					confp->chan.sig = SIG_GR303FXOKS;
 					confp->pri.nodetype = PRI_NETWORK;
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index ae9567a..aef10c6 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -1690,24 +1690,25 @@ static void send_signaling(struct chan_iax2_pvt *pvt)
  *  we have received a destination call number. */
 static int queue_signalling(struct chan_iax2_pvt *pvt, struct ast_frame *f)
 {
-	struct signaling_queue_entry *new;
+	struct signaling_queue_entry *qe;
 
 	if (f->frametype == AST_FRAME_IAX || !pvt->hold_signaling) {
 		return 1; /* do not queue this frame */
-	} else if (!(new = ast_calloc(1, sizeof(struct signaling_queue_entry)))) {
+	} else if (!(qe = ast_calloc(1, sizeof(struct signaling_queue_entry)))) {
 		return -1;  /* out of memory */
 	}
 
-	memcpy(&new->f, f, sizeof(new->f)); /* copy ast_frame into our queue entry */
-
-	if (new->f.datalen) { /* if there is data in this frame copy it over as well */
-		if (!(new->f.data.ptr = ast_calloc(1, new->f.datalen))) {
-			free_signaling_queue_entry(new);
+	/* copy ast_frame into our queue entry */
+	qe->f = *f;
+	if (qe->f.datalen) {
+		/* if there is data in this frame copy it over as well */
+		if (!(qe->f.data.ptr = ast_malloc(qe->f.datalen))) {
+			free_signaling_queue_entry(qe);
 			return -1;
 		}
-		memcpy(new->f.data.ptr, f->data.ptr, sizeof(*new->f.data.ptr));
+		memcpy(qe->f.data.ptr, f->data.ptr, qe->f.datalen);
 	}
-	AST_LIST_INSERT_TAIL(&pvt->signaling_queue, new, next);
+	AST_LIST_INSERT_TAIL(&pvt->signaling_queue, qe, next);
 
 	return 0;
 }
@@ -3974,7 +3975,16 @@ static int schedule_delivery(struct iax_frame *fr, int updatehistory, int fromtr
 	int needfree = 0;
 	struct ast_channel *owner = NULL;
 	struct ast_channel *bridge = NULL;
-	
+
+	/*
+	 * Clear fr->af.data if there is no data in the buffer.  Things
+	 * like AST_CONTROL_HOLD without a suggested music class must
+	 * have a NULL pointer.
+	 */
+	if (!fr->af.datalen) {
+		memset(&fr->af.data, 0, sizeof(fr->af.data));
+	}
+
 	/* Attempt to recover wrapped timestamps */
 	unwrap_timestamp(fr);
 
@@ -5048,7 +5058,14 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
 	case AST_OPTION_OPRMODE:
 		errno = EINVAL;
 		return -1;
-	default:
+ 	/* These options are sent to the other side across the network where
+ 	 * they will be passed to whatever channel is bridged there. Don't
+ 	 * do anything silly like pass an option that transmits pointers to
+ 	 * memory on this machine to a remote machine to use */
+ 	case AST_OPTION_TONE_VERIFY:
+ 	case AST_OPTION_TDD:
+ 	case AST_OPTION_RELAXDTMF:
+ 	case AST_OPTION_AUDIO_MODE:
 	{
 		unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
 		struct chan_iax2_pvt *pvt;
@@ -5076,7 +5093,12 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
 		ast_free(h);
 		return res;
 	}
+	default:
+		return -1;
 	}
+
+	/* Just in case someone does a break instead of a return */
+	return -1;
 }
 
 static struct ast_frame *iax2_read(struct ast_channel *c) 
@@ -5412,7 +5434,7 @@ static int iax2_getpeertrunk(struct sockaddr_in sin)
 }
 
 /*! \brief  Create new call, interface with the PBX core */
-static struct ast_channel *ast_iax2_new(int callno, int state, int capability)
+static struct ast_channel *ast_iax2_new(int callno, int state, int capability, unsigned int cachable)
 {
 	struct ast_channel *tmp;
 	struct chan_iax2_pvt *i;
@@ -5474,6 +5496,10 @@ static struct ast_channel *ast_iax2_new(int callno, int state, int capability)
 	i->owner = tmp;
 	i->capability = capability;
 
+	if (!cachable) {
+		tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE;
+	}
+
 	/* Set inherited variables */
 	if (i->vars) {
 		for (v = i->vars ; v ; v = v->next)
@@ -7263,10 +7289,10 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
 	i = ao2_iterator_init(users, 0);
 	while ((user = ao2_iterator_next(&i))) {
 		if ((ast_strlen_zero(iaxs[callno]->username) ||				/* No username specified */
-			!strcmp(iaxs[callno]->username, user->name))	/* Or this username specified */
-			&& ast_apply_ha(user->ha, sin) 	/* Access is permitted from this IP */
+			!strcmp(iaxs[callno]->username, user->name))			/* Or this username specified */
+			&& ast_apply_ha(user->ha, sin) == AST_SENSE_ALLOW		/* Access is permitted from this IP */
 			&& (ast_strlen_zero(iaxs[callno]->context) ||			/* No context specified */
-			     apply_context(user->contexts, iaxs[callno]->context))) {			/* Context is permitted */
+				apply_context(user->contexts, iaxs[callno]->context))) {			/* Context is permitted */
 			if (!ast_strlen_zero(iaxs[callno]->username)) {
 				/* Exact match, stop right now. */
 				if (best)
@@ -7322,8 +7348,9 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
 	user = best;
 	if (!user && !ast_strlen_zero(iaxs[callno]->username)) {
 		user = realtime_user(iaxs[callno]->username, sin);
-		if (user && !ast_strlen_zero(iaxs[callno]->context) &&			/* No context specified */
-		    !apply_context(user->contexts, iaxs[callno]->context)) {		/* Context is permitted */
+		if (user && (ast_apply_ha(user->ha, sin) == AST_SENSE_DENY		/* Access is denied from this IP */
+			|| (!ast_strlen_zero(iaxs[callno]->context) &&					/* No context specified */
+				!apply_context(user->contexts, iaxs[callno]->context)))) {	/* Context is permitted */
 			user = user_unref(user);
 		}
 	}
@@ -7537,7 +7564,7 @@ static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
 		user = user_unref(user);
 	}
 	if (ast_test_flag(p, IAX_FORCE_ENCRYPT) && !p->encmethods) { 
-		ast_log(LOG_NOTICE, "Call Terminated, Incomming call is unencrypted while force encrypt is enabled.");
+		ast_log(LOG_NOTICE, "Call Terminated, Incoming call is unencrypted while force encrypt is enabled.");
 		return res;
 	}
 	if (!ast_test_flag(&p->state, IAX_STATE_AUTHENTICATED))
@@ -7730,7 +7757,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 		/* if challenge has been sent, but no challenge response if given, reject. */
 		goto return_unref;
 	}
-	ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */
+	ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */
 
 	/* either Authentication has taken place, or a REGAUTH must be sent before verifying registration */
 	res = 0;
@@ -8273,7 +8300,7 @@ static void __expire_registry(const void *data)
 	if (!ast_test_flag(peer, IAX_TEMPONLY))
 		ast_db_del("IAX/Registry", peer->name);
 	register_peer_exten(peer, 0);
-	ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", peer->name); /* Activate notification */
+	ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */
 	if (iax2_regfunk)
 		iax2_regfunk(peer->name, 0);
 
@@ -8323,7 +8350,7 @@ static void reg_source_db(struct iax2_peer *p)
  							peer_unref(p);
  						}
  					}
-  					ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */
+  					ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */
  					p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p));
  					if (p->expire == -1)
  						peer_unref(p);
@@ -8395,14 +8422,14 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i
 					    ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
 			manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Registered\r\n", p->name);
 			register_peer_exten(p, 1);
-			ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */
+			ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */
 		} else if (!ast_test_flag(p, IAX_TEMPONLY)) {
 			ast_verb(3, "Unregistered IAX2 '%s' (%s)\n", p->name,
 					    ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED");
 			manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unregistered\r\n", p->name);
 			register_peer_exten(p, 0);
 			ast_db_del("IAX/Registry", p->name);
-			ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", p->name); /* Activate notification */
+			ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */
 		}
 		/* Update the host */
 		/* Verify that the host is really there */
@@ -9828,7 +9855,8 @@ static int socket_process(struct iax2_thread *thread)
 		    (f.frametype == AST_FRAME_IAX)) {
 			if (ast_test_flag(iaxs[fr->callno], IAX_DELAYPBXSTART)) {
 				ast_clear_flag(iaxs[fr->callno], IAX_DELAYPBXSTART);
-				if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat)) {
+				if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat,
+						  ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED))) {
 					ast_mutex_unlock(&iaxsl[fr->callno]);
 					return 1;
 				}
@@ -10417,13 +10445,13 @@ retryowner2:
 						if (iaxs[fr->callno]->pingtime <= peer->maxms) {
 							ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE! Time: %d\n", peer->name, iaxs[fr->callno]->pingtime);
 							manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Reachable\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime); 
-							ast_devstate_changed(AST_DEVICE_NOT_INUSE, "IAX2/%s", peer->name); /* Activate notification */
+							ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */
 						}
 					} else if ((peer->historicms > 0) && (peer->historicms <= peer->maxms)) {
 						if (iaxs[fr->callno]->pingtime > peer->maxms) {
 							ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED (%d ms)!\n", peer->name, iaxs[fr->callno]->pingtime);
 							manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Lagged\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime); 
-							ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", peer->name); /* Activate notification */
+							ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */
 						}
 					}
 					peer->lastms = iaxs[fr->callno]->pingtime;
@@ -10652,7 +10680,7 @@ retryowner2:
 											using_prefs);
 
 							ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
-							if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format)))
+							if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, 1)))
 								iax2_destroy(fr->callno);
 							else if (ies.vars) {
 								struct ast_datastore *variablestore;
@@ -10721,7 +10749,7 @@ immediatedial:
 						ast_verb(3, "Accepting DIAL from %s, formats = 0x%x\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat);
 						ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
 						send_command(iaxs[fr->callno], AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, 0, NULL, 0, -1);
-						if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat)))
+						if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat, 1)))
 							iax2_destroy(fr->callno);
 						else if (ies.vars) {
 							struct ast_datastore *variablestore;
@@ -11446,7 +11474,7 @@ static void __iax2_poke_noanswer(const void *data)
 	if (peer->lastms > -1) {
 		ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Time: %d\n", peer->name, peer->lastms);
 		manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, peer->lastms);
-		ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", peer->name); /* Activate notification */
+		ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */
 	}
 	if ((callno = peer->callno) > 0) {
 		ast_mutex_lock(&iaxsl[callno]);
@@ -11610,7 +11638,7 @@ static struct ast_channel *iax2_request(const char *type, int format, void *data
 	if (cai.found)
 		ast_string_field_set(iaxs[callno], host, pds.peer);
 
-	c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability);
+	c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, cai.found);
 
 	ast_mutex_unlock(&iaxsl[callno]);
 
diff --git a/channels/chan_local.c b/channels/chan_local.c
index 157b5e2..0d956e0 100644
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -771,6 +771,9 @@ static struct ast_channel *local_new(struct local_pvt *p, int state)
 	tmp->tech_pvt = p;
 	tmp2->tech_pvt = p;
 
+	tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE;
+	tmp2->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE;
+
 	p->owner = tmp;
 	p->chan = tmp2;
 	p->u_owner = ast_module_user_add(p->owner);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 2daa637..c039e32 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -500,6 +500,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 266293 $")
 #define DEFAULT_MWI_EXPIRY      3600
 #define DEFAULT_REGISTRATION_TIMEOUT 20
 #define DEFAULT_MAX_FORWARDS    "70"
+#define DEFAULT_AUTHLIMIT       100
+#define DEFAULT_AUTHTIMEOUT     30
 
 /* guard limit must be larger than guard secs */
 /* guard min must be < 1000, and should be >= 250 */
@@ -542,6 +544,7 @@ static int mwi_expiry = DEFAULT_MWI_EXPIRY;
 
 #define SIP_MAX_HEADERS              64               /*!< Max amount of SIP headers to read */
 #define SIP_MAX_LINES                256              /*!< Max amount of lines in SIP attachment (like SDP) */
+#define SIP_MAX_PACKET_SIZE       20480  /*!< Max SIP packet size */
 #define SIP_MIN_PACKET               4096             /*!< Initialize size of memory to allocate for packets */
 #define MAX_HISTORY_ENTRIES 	     50	              /*!< Max entires in the history list for a sip_pvt */
 
@@ -552,6 +555,10 @@ static int mwi_expiry = DEFAULT_MWI_EXPIRY;
 
 #define SDP_MAX_RTPMAP_CODECS        32               /*!< Maximum number of codecs allowed in received SDP */
 
+static int unauth_sessions = 0;
+static int authlimit = DEFAULT_AUTHLIMIT;
+static int authtimeout = DEFAULT_AUTHTIMEOUT;
+
 /*! \brief Global jitterbuffer configuration - by default, jb is disabled */
 static struct ast_jb_conf default_jbconf =
 {
@@ -1212,6 +1219,7 @@ struct sip_request {
 	char debug;		/*!< print extra debugging if non zero */
 	char has_to_tag;	/*!< non-zero if packet has To: tag */
 	char ignore;		/*!< if non-zero This is a re-transmit, ignore it */
+	char authenticated;     /*!< non-zero if this request was authenticated */
 	/* Array of offsets into the request string of each SIP header*/
 	ptrdiff_t header[SIP_MAX_HEADERS];
 	/* Array of offsets into the request string of each SDP line*/
@@ -1655,6 +1663,7 @@ struct sip_pvt {
 	struct sip_auth *peerauth;		/*!< Realm authentication */
 	int noncecount;				/*!< Nonce-count */
 	unsigned int stalenonce:1;	/*!< Marks the current nonce as responded too */
+	unsigned int ongoing_reinvite:1;    /*!< There is a reinvite in progress that might need to be cleaned up */
 	char lastmsg[256];			/*!< Last Message sent/received */
 	int amaflags;				/*!< AMA Flags */
 	int pendinginvite;			/*!< Any pending INVITE or state NOTIFY (in subscribe pvt's) ? (seqno of this) */
@@ -1667,6 +1676,7 @@ struct sip_pvt {
 
 	int initid;				/*!< Auto-congest ID if appropriate (scheduler) */
 	int waitid;				/*!< Wait ID for scheduler after 491 or other delays */
+	int reinviteid;                     /*!< Reinvite in case of provisional, but no final response */
 	int autokillid;				/*!< Auto-kill ID (scheduler) */
 	int t38id;                              /*!< T.38 Response ID */
 	enum transfermodes allowtransfer;	/*!< REFER: restriction scheme */
@@ -2908,21 +2918,48 @@ static void *sip_tcp_worker_fn(void *data)
 	return _sip_tcp_helper_thread(NULL, tcptls_session);
 }
 
+/*! \brief Check if the authtimeout has expired.
+ * \param start the time when the session started
+ *
+ * \retval 0 the timeout has expired
+ * \retval -1 error
+ * \return the number of milliseconds until the timeout will expire
+ */
+static int sip_check_authtimeout(time_t start)
+{
+	int timeout;
+	time_t now;
+	if(time(&now) == -1) {
+		ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
+		return -1;
+	}
+
+	timeout = (authtimeout - (now - start)) * 1000;
+	if (timeout < 0) {
+		/* we have timed out */
+		return 0;
+	}
+
+	return timeout;
+}
+
 /*! \brief SIP TCP thread management function 
 	This function reads from the socket, parses the packet into a request
 */
 static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session) 
 {
-	int res, cl;
+	int res, cl, timeout = -1, authenticated = 0, flags;
+	time_t start;
 	struct sip_request req = { 0, } , reqcpy = { 0, };
 	struct sip_threadinfo *me = NULL;
 	char buf[1024] = "";
 	struct pollfd fds[2] = { { 0 }, { 0 }, };
 	struct ast_tcptls_session_args *ca = NULL;
 
-	/* If this is a server session, then the connection has already been setup,
-	 * simply create the threadinfo object so we can access this thread for writing.
-	 * 
+	/* If this is a server session, then the connection has already been
+	 * setup. Check if the authlimit has been reached and if not create the
+	 * threadinfo object so we can access this thread for writing.
+	 *
 	 * if this is a client connection more work must be done.
 	 * 1. We own the parent session args for a client connection.  This pointer needs
 	 *    to be held on to so we can decrement it's ref count on thread destruction.
@@ -2931,6 +2968,22 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
 	 * 3. Last, the tcptls_session must be started.
 	 */
 	if (!tcptls_session->client) {
+		if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= authlimit) {
+			/* unauth_sessions is decremented in the cleanup code */
+			goto cleanup;
+		}
+
+		if ((flags = fcntl(tcptls_session->fd, F_GETFL)) == -1) {
+			ast_log(LOG_ERROR, "error setting socket to non blocking mode, fcntl() failed: %s\n", strerror(errno));
+			goto cleanup;
+		}
+
+		flags |= O_NONBLOCK;
+		if (fcntl(tcptls_session->fd, F_SETFL, flags) == -1) {
+			ast_log(LOG_ERROR, "error setting socket to non blocking mode, fcntl() failed: %s\n", strerror(errno));
+			goto cleanup;
+		}
+
 		if (!(me = sip_threadinfo_create(tcptls_session, tcptls_session->ssl ? SIP_TRANSPORT_TLS : SIP_TRANSPORT_TCP))) {
 			goto cleanup;
 		}
@@ -2960,14 +3013,41 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
 	if (!(reqcpy.data = ast_str_create(SIP_MIN_PACKET)))
 		goto cleanup;
 
+	if(time(&start) == -1) {
+		ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
+		goto cleanup;
+	}
+
 	for (;;) {
 		struct ast_str *str_save;
 
-		res = ast_poll(fds, 2, -1); /* polls for both socket and alert_pipe */
+		if (!tcptls_session->client && req.authenticated && !authenticated) {
+			authenticated = 1;
+			ast_atomic_fetchadd_int(&unauth_sessions, -1);
+		}
 
+		/* calculate the timeout for unauthenticated server sessions */
+		if (!tcptls_session->client && !authenticated ) {
+			if ((timeout = sip_check_authtimeout(start)) < 0) {
+				goto cleanup;
+			}
+
+			if (timeout == 0) {
+				ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
+				goto cleanup;
+			}
+		} else {
+			timeout = -1;
+		}
+
+		res = ast_poll(fds, 2, timeout); /* polls for both socket and alert_pipe */
 		if (res < 0) {
 			ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res);
 			goto cleanup;
+		} else if (res == 0) {
+			/* timeout */
+			ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
+			goto cleanup;
 		}
 
 		/* handle the socket event, check for both reads from the socket fd,
@@ -2999,7 +3079,30 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
 			req.socket.fd = tcptls_session->fd;
 
 			/* Read in headers one line at a time */
-			while (req.len < 4 || strncmp(REQ_OFFSET_TO_STR(&req, len - 4), "\r\n\r\n", 4)) {
+			while ((req.len <= SIP_MAX_PACKET_SIZE) && (req.len < 4 || strncmp(REQ_OFFSET_TO_STR(&req, len - 4), "\r\n\r\n", 4))) {
+				if (!tcptls_session->client && !authenticated ) {
+					if ((timeout = sip_check_authtimeout(start)) < 0) {
+						goto cleanup;
+					}
+
+					if (timeout == 0) {
+						ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
+						goto cleanup;
+					}
+				} else {
+					timeout = -1;
+				}
+
+				res = ast_wait_for_input(tcptls_session->fd, timeout);
+				if (res < 0) {
+					ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res);
+					goto cleanup;
+				} else if (res == 0) {
+					/* timeout */
+					ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
+					goto cleanup;
+				}
+
 				ast_mutex_lock(&tcptls_session->lock);
 				if (!fgets(buf, sizeof(buf), tcptls_session->f)) {
 					ast_mutex_unlock(&tcptls_session->lock);
@@ -3017,6 +3120,29 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
 			if (sscanf(get_header(&reqcpy, "Content-Length"), "%30d", &cl)) {
 				while (cl > 0) {
 					size_t bytes_read;
+					if (!tcptls_session->client && !authenticated ) {
+						if ((timeout = sip_check_authtimeout(start)) < 0) {
+							goto cleanup;
+						}
+
+						if (timeout == 0) {
+							ast_debug(2, "SIP %s server timed out", tcptls_session->ssl ? "SSL": "TCP");
+							goto cleanup;
+						}
+					} else {
+						timeout = -1;
+					}
+
+					res = ast_wait_for_input(tcptls_session->fd, timeout);
+					if (res < 0) {
+						ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res);
+						goto cleanup;
+					} else if (res == 0) {
+						/* timeout */
+						ast_debug(2, "SIP %s server timed out", tcptls_session->ssl ? "SSL": "TCP");
+						goto cleanup;
+					}
+
 					ast_mutex_lock(&tcptls_session->lock);
 					if (!(bytes_read = fread(buf, 1, MIN(sizeof(buf) - 1, cl), tcptls_session->f))) {
 						ast_mutex_unlock(&tcptls_session->lock);
@@ -3074,6 +3200,10 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
 	ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
 
 cleanup:
+	if (tcptls_session && !tcptls_session->client && !authenticated) {
+		ast_atomic_fetchadd_int(&unauth_sessions, -1);
+	}
+
 	if (me) {
 		ao2_t_unlink(threadt, me, "Removing tcptls helper thread, thread is closing");
 		ao2_t_ref(me, -1, "Removing tcp_helper_threads threadinfo ref");
@@ -3954,7 +4084,7 @@ static int __sip_autodestruct(const void *data)
 			ast_debug(3, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>");
 			append_history(p, "ReliableXmit", "timeout");
 			if (sscanf(p->lastmsg, "Tx: %30s", method_str) == 1 || sscanf(p->lastmsg, "Rx: %30s", method_str) == 1) {
-				if (method_match(SIP_CANCEL, method_str) || method_match(SIP_BYE, method_str)) {
+				if (p->ongoing_reinvite || method_match(SIP_CANCEL, method_str) || method_match(SIP_BYE, method_str)) {
 					pvt_set_needdestroy(p, "autodestruct");
 				}
 			}
@@ -5781,7 +5911,7 @@ static int update_call_counter(struct sip_pvt *fup, int event)
 	}
 
 	if (p) {
-		ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", p->name);
+		ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", p->name);
 		unref_peer(p, "update_call_counter: unref_peer from call counter");
 	} 
 	return 0;
@@ -5965,6 +6095,21 @@ static const char *hangup_cause2sip(int cause)
 	return 0;
 }
 
+static int reinvite_timeout(const void *data)
+{
+	struct sip_pvt *dialog = (struct sip_pvt *) data;
+	struct ast_channel *owner;
+	sip_pvt_lock(dialog);
+	dialog->reinviteid = -1;
+	check_pendings(dialog);
+	owner = dialog->owner;
+	if (owner) {
+		ast_channel_free(owner);
+	}
+	sip_pvt_unlock(dialog);
+	dialog_unref(dialog, "unref for reinvite timeout");
+	return 0;
+}
 
 /*! \brief  sip_hangup: Hangup SIP call
  * Part of PBX interface, called from ast_hangup */
@@ -6162,8 +6307,16 @@ static int sip_hangup(struct ast_channel *ast)
 				ast_set_flag(&p->flags[0], SIP_PENDINGBYE);	
 				ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE);	
 				AST_SCHED_DEL_UNREF(sched, p->waitid, dialog_unref(p, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
-				if (sip_cancel_destroy(p))
+				if (sip_cancel_destroy(p)) {
 					ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
+				}
+				/* If we have an ongoing reinvite, there is a chance that we have gotten a provisional
+				 * response, but something weird has happened and we will never receive a final response.
+				 * So, just in case, check for pending actions after a bit of time to trigger the pending
+				 * bye that we are setting above */
+				if (p->ongoing_reinvite && p->reinviteid < 0) {
+					p->reinviteid = ast_sched_add(sched, 32 * p->timer_t1, reinvite_timeout, dialog_ref(p, "ref for reinvite_timeout"));
+				}
 			}
 		}
 	}
@@ -6811,6 +6964,9 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
 	if (i->rtp)
 		ast_jb_configure(tmp, &global_jbconf);
 
+	if (!i->relatedpeer) {
+		tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE;
+	}
 	/* Set channel variables for this call from configuration */
 	for (v = i->chanvars ; v ; v = v->next) {
 		char valuebuf[1024];
@@ -7211,6 +7367,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
 	p->method = intended_method;
 	p->initid = -1;
 	p->waitid = -1;
+	p->reinviteid = -1;
 	p->autokillid = -1;
 	p->request_queue_sched_id = -1;
 	p->provisional_keepalive_sched_id = -1;
@@ -7509,7 +7666,7 @@ restartsearch:
 	/* We do not respond to responses for dialogs that we don't know about, we just drop
 	   the session quickly */
 	if (intended_method == SIP_RESPONSE)
-		ast_debug(2, "That's odd...  Got a response on a call we dont know about. Callid %s\n", callid ? callid : "<unknown>");
+		ast_debug(2, "That's odd...  Got a response on a call we don't know about. Callid %s\n", callid ? callid : "<unknown>");
 
 	return NULL;
 }
@@ -8463,7 +8620,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 
 	/* Setup audio address and port */
 	if (p->rtp) {
-		if (portno > 0) {
+		if (hp && portno > 0) {
 			sin.sin_family = AF_INET;
 			sin.sin_port = htons(portno);
 			memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
@@ -8502,7 +8659,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 
 	/* Setup video address and port */
 	if (p->vrtp) {
-		if (vportno > 0) {
+		if (vhp && vportno > 0) {
 			vsin.sin_family = AF_INET;
 			vsin.sin_port = htons(vportno);
 			memcpy(&vsin.sin_addr, vhp->h_addr, sizeof(vsin.sin_addr));
@@ -8519,7 +8676,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 
 	/* Setup text address and port */
 	if (p->trtp) {
-		if (tportno > 0) {
+		if (thp && tportno > 0) {
 			tsin.sin_family = AF_INET;
 			tsin.sin_port = htons(tportno);
 			memcpy(&tsin.sin_addr, thp->h_addr, sizeof(tsin.sin_addr));
@@ -8541,7 +8698,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 	}
 	/* Setup image address and port */
 	if (p->udptl) {
-		if (udptlportno > 0) {
+		if (ihp && udptlportno > 0) {
 			isin.sin_family = AF_INET;
 			isin.sin_port = htons(udptlportno);
 			if (ast_test_flag(&p->flags[0], SIP_NAT) && ast_test_flag(&p->flags[1], SIP_PAGE2_UDPTL_DESTINATION)) {
@@ -10588,7 +10745,7 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int old
 	initialize_initreq(p, &req);
 	p->lastinvite = p->ocseq;
 	ast_set_flag(&p->flags[0], SIP_OUTGOING);       /* Change direction of this dialog */
-
+	p->ongoing_reinvite = 1;
 	return send_request(p, &req, XMIT_CRITICAL, p->ocseq);
 }
 
@@ -12090,7 +12247,7 @@ static int expire_register(const void *data)
 
 	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
 	register_peer_exten(peer, FALSE);	/* Remove regexten */
-	ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
+	ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
 
 	/* Do we need to release this peer from memory? 
 		Only for realtime peers and autocreated peers
@@ -12789,7 +12946,7 @@ static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *
 		return AUTH_CHALLENGE_SENT;
 	} 
 	if (good_response) {
-		append_history(p, "AuthOK", "Auth challenge succesful for %s", username);
+		append_history(p, "AuthOK", "Auth challenge successful for %s", username);
 		return AUTH_SUCCESSFUL;
 	}
 
@@ -12813,7 +12970,8 @@ static void sip_peer_hold(struct sip_pvt *p, int hold)
 	ast_atomic_fetchadd_int(&peer->onHold, (hold ? +1 : -1));
 
 	/* Request device state update */
-	ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
+	ast_devstate_changed(AST_DEVICE_UNKNOWN, (p->owner->flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE),
+			     "SIP/%s", peer->name);
 	unref_peer(peer, "sip_peer_hold: from find_peer operation");
 	
 	return;
@@ -12877,9 +13035,9 @@ static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct
 {
 	/* We have to emulate EXACTLY what we'd get with a good peer
 	 * and a bad password, or else we leak information. */
-	const char *response = "407 Proxy Authentication Required";
-	const char *reqheader = "Proxy-Authorization";
-	const char *respheader = "Proxy-Authenticate";
+	const char *response = "401 Unauthorized";
+	const char *reqheader = "Authorization";
+	const char *respheader = "WWW-Authenticate";
 	const char *authtoken;
 	struct ast_str *buf;
 	char *c;
@@ -12894,23 +13052,18 @@ static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct
 		[K_LAST] = { NULL, NULL}
 	};
 
-	if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) {
-		response = "401 Unauthorized";
-		reqheader = "Authorization";
-		respheader = "WWW-Authenticate";
-	}
 	authtoken = get_header(req, reqheader);
 	if (req->ignore && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) {
 		/* This is a retransmitted invite/register/etc, don't reconstruct authentication
 		 * information */
-		transmit_response_with_auth(p, response, req, p->randdata, 0, respheader, 0);
+		transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
 		/* Schedule auto destroy in 32 seconds (according to RFC 3261) */
 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
 		return;
 	} else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
 		/* We have no auth, so issue challenge and request authentication */
 		set_nonce_randdata(p, 1);
-		transmit_response_with_auth(p, response, req, p->randdata, 0, respheader, 0);
+		transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
 		/* Schedule auto destroy in 32 seconds */
 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
 		return;
@@ -13169,7 +13322,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr
 			}
 		}
 	}
-	if (!peer && sip_cfg.alwaysauthreject) {
+	if (!peer && sip_cfg.alwaysauthreject && ast_test_flag(&p->flags[1], SIP_PAGE2_REGISTERTRYING)) {
 		/* If we found a peer, we transmit a 100 Trying.  Therefore, if we're
 		 * trying to avoid leaking information, we MUST also transmit the same
 		 * response when we DON'T find a peer. */
@@ -13178,7 +13331,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr
 		sched_yield();
 	}
 	if (!res) {
-		ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
+		ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
 	}
 	if (res < 0) {
 		switch (res) {
@@ -16839,11 +16992,18 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
 			per device. I don't want incoming callers to record calls in my
 			pbx.
 		*/
-		/* first, get the feature string, if it exists */
+		
 		struct ast_call_feature *feat;
 		int j;
 		struct ast_frame f = { AST_FRAME_DTMF, };
 
+		if (!p->owner) {        /* not a PBX call */
+			transmit_response(p, "481 Call leg/transaction does not exist", req);
+			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+			return;
+		}
+
+		/* first, get the feature string, if it exists */
 		ast_rdlock_call_features();
 		feat = ast_find_call_feature("automon");
 		if (!feat || ast_strlen_zero(feat->exten)) {
@@ -17607,17 +17767,20 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req)
 static void check_pendings(struct sip_pvt *p)
 {
 	if (ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
-		/* if we can't BYE, then this is really a pending CANCEL */
-		if (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA)
+                if (p->reinviteid > -1) {
+                        /* Outstanding p->reinviteid timeout, so wait... */
+                        return;
+                } else if (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA) {
+                        /* if we can't BYE, then this is really a pending CANCEL */
 			transmit_request(p, SIP_CANCEL, p->lastinvite, XMIT_RELIABLE, FALSE);
 			/* Actually don't destroy us yet, wait for the 487 on our original 
 			   INVITE, but do set an autodestruct just in case we never get it. */
-		else {
+		} else {
 			/* We have a pending outbound invite, don't send something
-				new in-transaction */
-			if (p->pendinginvite)
+			 * new in-transaction, unless it is a pending reinvite, then
+			 * by the time we are called here, we should probably just hang up. */
+			if (p->pendinginvite && !p->ongoing_reinvite)
 				return;
-
 			/* Perhaps there is an SD change INVITE outstanding */
 			transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE);
 		}
@@ -17692,9 +17855,17 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru
  	if (resp >= 300 && (p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA ))
  		p->invitestate = INV_COMPLETED;
  	
+	if ((resp >= 200 && reinvite)) {
+		p->ongoing_reinvite = 0;
+		if (p->reinviteid > -1) {
+			AST_SCHED_DEL_UNREF(sched, p->reinviteid, dialog_unref(p, "unref dialog for reinvite timeout because of a final response"));
+		}
+	}
+
 	/* Final response, clear out pending invite */
-	if ((resp == 200 || resp >= 300) && p->pendinginvite && seqno == p->pendinginvite)
+	if ((resp == 200 || resp >= 300) && p->pendinginvite && seqno == p->pendinginvite) {
 		p->pendinginvite = 0;
+	}
 
 	switch (resp) {
 	case 100:	/* Trying */
@@ -18313,7 +18484,7 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req
 
 		ast_log(LOG_NOTICE, "Peer '%s' is now %s. (%dms / %dms)\n",
 			peer->name, s, pingtime, peer->maxms);
-		ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
+		ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
 		if (sip_cfg.peer_rtupdate) {
 			ast_update_realtime(ast_check_realtime("sipregs") ? "sipregs" : "sippeers", "name", peer->name, "lastms", str_lastms, SENTINEL);
 		}
@@ -18663,7 +18834,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
 					}
 				}
 			} else
-				ast_log(LOG_NOTICE, "Dont know how to handle a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : ast_inet_ntoa(p->sa.sin_addr));
+				ast_log(LOG_NOTICE, "Don't know how to handle a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : ast_inet_ntoa(p->sa.sin_addr));
 		}
 	} else {	
 		/* Responses to OUTGOING SIP requests on INCOMING calls 
@@ -20092,6 +20263,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
 			}
 		}
 
+		req->authenticated = 1;
+
 		/* We have a succesful authentication, process the SDP portion if there is one */
 		if (find_sdp(req)) {
 			if (process_sdp(p, req, SDP_T38_INITIATE)) {
@@ -21640,8 +21813,10 @@ static int handle_request_register(struct sip_pvt *p, struct sip_request *req, s
 			get_header(req, "To"), ast_inet_ntoa(sin->sin_addr),
 			reason);
 		append_history(p, "RegRequest", "Failed : Account %s : %s", get_header(req, "To"), reason);
-	} else
+	} else {
+		req->authenticated = 1;
 		append_history(p, "RegRequest", "Succeeded : Account %s", get_header(req, "To"));
+	}
 
 	if (res < 1) {
 		/* Destroy the session, but keep us around for just a bit in case they don't
@@ -21843,7 +22018,7 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct so
 			p->invitestate = INV_TERMINATED;
 			p->pendinginvite = 0;
 			acked = __sip_ack(p, seqno, 1 /* response */, 0);
-			if (find_sdp(req)) {
+			if (p->owner && find_sdp(req)) {
 				if (process_sdp(p, req, SDP_T38_NONE))
 					return -1;
 			}
@@ -22000,7 +22175,8 @@ static int sipsock_read(int *id, int fd, short events, void *ignore)
 		return -1;
 	}
 
-	req.len = res;
+	/* req.data will have the correct length in case of nulls */
+	req.len = ast_str_strlen(req.data);
 	req.socket.fd = sipsock;
 	set_socket_transport(&req.socket, SIP_TRANSPORT_UDP);
 	req.socket.tcptls_session	= NULL;
@@ -22079,6 +22255,11 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin)
 	}
 	p->recv = *sin;
 
+	/* if we have an owner, then this request has been authenticated */
+	if (p->owner) {
+		req->authenticated = 1;
+	}
+
 	if (p->do_history) /* This is a request or response, note what it was for */
 		append_history(p, "Rx", "%s / %s / %s", req->data->str, get_header(req, "CSeq"), REQ_OFFSET_TO_STR(req, rlPart2));
 
@@ -22957,7 +23138,7 @@ static int sip_poke_noanswer(const void *data)
 	}
 	
 	peer->lastms = -1;
-	ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
+	ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
 
 	/* Try again quickly */
 	AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched, 
@@ -23436,15 +23617,14 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
 		}
 	} else if (!strcasecmp(v->name, "nat")) {
 		ast_set_flag(&mask[0], SIP_NAT);
-		ast_clear_flag(&flags[0], SIP_NAT);
-		if (!strcasecmp(v->value, "never"))
-			ast_set_flag(&flags[0], SIP_NAT_NEVER);
-		else if (!strcasecmp(v->value, "route"))
-			ast_set_flag(&flags[0], SIP_NAT_ROUTE);
-		else if (ast_true(v->value))
-			ast_set_flag(&flags[0], SIP_NAT_ALWAYS);
-		else
-			ast_set_flag(&flags[0], SIP_NAT_RFC3581);
+		ast_set_flag(&flags[0], SIP_NAT_ALWAYS);
+		if (!strcasecmp(v->value, "never")) {
+			ast_set_flags_to(&flags[0], SIP_NAT, SIP_NAT_NEVER);
+		} else if (!strcasecmp(v->value, "route")) {
+			ast_set_flags_to(&flags[0], SIP_NAT, SIP_NAT_ROUTE);
+		} else if (ast_false(v->value)) {
+			ast_set_flags_to(&flags[0], SIP_NAT, SIP_NAT_RFC3581);
+		}
 	} else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) {
 		ast_set_flag(&mask[0], SIP_REINVITE);
 		ast_clear_flag(&flags[0], SIP_REINVITE);
@@ -24359,6 +24539,15 @@ static int peer_markall_func(void *device, void *arg, int flags)
 	return 0;
 }
 
+static void display_nat_warning(const char *cat, int reason, struct ast_flags *flags) {
+	int global_nat, specific_nat;
+
+	if (reason == CHANNEL_MODULE_LOAD && (specific_nat = ast_test_flag(&flags[0], SIP_NAT)) != (global_nat = ast_test_flag(&global_flags[0], SIP_NAT))) {
+		ast_log(LOG_WARNING, "sip.conf: Different 'nat' settings between [general] and section [%s]. See /usr/share/doc/asterisk/README.Debian.gz (global='%s' peer/user='%s')\n",
+				cat, nat2str(global_nat), nat2str(specific_nat));
+	}
+}
+
 /*! \brief Re-read SIP.conf config file
 \note	This function reloads all config data, except for
 	active peers (with registrations). They will only
@@ -24573,9 +24762,10 @@ static int reload_config(enum channelreloadreason reason)
 	ast_copy_string(default_mohinterpret, DEFAULT_MOHINTERPRET, sizeof(default_mohinterpret));
 	ast_copy_string(default_mohsuggest, DEFAULT_MOHSUGGEST, sizeof(default_mohsuggest));
 	ast_copy_string(default_vmexten, DEFAULT_VMEXTEN, sizeof(default_vmexten));
-	ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833);			/*!< Default DTMF setting: RFC2833 */
-	ast_set_flag(&global_flags[0], SIP_NAT_RFC3581);			/*!< NAT support if requested by device with rport */
-	ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA);			/*!< Allow re-invites */
+	ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */
+	ast_set_flag(&global_flags[0], SIP_NAT_RFC3581);  /*!< NAT support if requested by device with rport */
+	ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA); /*!< Allow re-invites */
+	ast_set_flag(&global_flags[0], SIP_NAT_ALWAYS);   /*!< Default to nat=yes */
 
 	/* Debugging settings, always default to off */
 	dumphistory = FALSE;
@@ -24592,6 +24782,8 @@ static int reload_config(enum channelreloadreason reason)
 	global_qualifyfreq = DEFAULT_QUALIFYFREQ;
 	global_t38_maxdatagram = -1;
 	global_shrinkcallerid = 1;
+	authlimit = DEFAULT_AUTHLIMIT;
+	authtimeout = DEFAULT_AUTHTIMEOUT;
 
 	sip_cfg.matchexterniplocally = DEFAULT_MATCHEXTERNIPLOCALLY;
 
@@ -24838,6 +25030,18 @@ static int reload_config(enum channelreloadreason reason)
 			mwi_expiry = atoi(v->value);
 			if (mwi_expiry < 1)
 				mwi_expiry = DEFAULT_MWI_EXPIRY;
+		} else if (!strcasecmp(v->name, "tcpauthtimeout")) {
+			if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
+					  &authtimeout, DEFAULT_AUTHTIMEOUT, 1, INT_MAX)) {
+				ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n",
+					v->name, v->value, v->lineno, config);
+			}
+		} else if (!strcasecmp(v->name, "tcpauthlimit")) {
+			if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
+					  &authlimit, DEFAULT_AUTHLIMIT, 1, INT_MAX)) {
+				ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n",
+					v->name, v->value, v->lineno, config);
+			}
 		} else if (!strcasecmp(v->name, "sipdebug")) {
 			if (ast_true(v->value))
 				sipdebug |= sip_debug_config;
@@ -25155,6 +25359,7 @@ static int reload_config(enum channelreloadreason reason)
 			}
 			peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0, 0);
 			if (peer) {
+				display_nat_warning(cat, reason, &peer->flags[0]);
 				ao2_t_link(peers, peer, "link peer into peers table");
 				if ((peer->type & SIP_TYPE_PEER) && peer->addr.sin_addr.s_addr) {
 					ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index db3a866..8437705 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -100,6 +100,8 @@ enum skinny_codecs {
 #define DEFAULT_SKINNY_PORT 2000
 #define DEFAULT_SKINNY_BACKLOG 2
 #define SKINNY_MAX_PACKET 1000
+#define DEFAULT_AUTH_TIMEOUT 30
+#define DEFAULT_AUTH_LIMIT 50
 
 static struct {
 	unsigned int tos;
@@ -111,6 +113,9 @@ static struct {
 } qos = { 0, 0, 0, 0, 0, 0 };
 
 static int keep_alive = 120;
+static int auth_timeout = DEFAULT_AUTH_TIMEOUT;
+static int auth_limit = DEFAULT_AUTH_LIMIT;
+static int unauth_sessions = 0;
 static char global_vmexten[AST_MAX_EXTENSION];      /* Voicemail pilot number */
 static char used_context[AST_MAX_EXTENSION]; /* placeholder to check if context are already used in regcontext */
 static char regcontext[AST_MAX_CONTEXT];     /* Context for auto-extension */
@@ -1307,6 +1312,7 @@ static struct ast_jb_conf global_jbconf;*/
 struct skinnysession {
 	pthread_t t;
 	ast_mutex_t lock;
+	time_t start;
 	struct sockaddr_in sin;
 	int fd;
 	char inbuf[SKINNY_MAX_PACKET];
@@ -1864,7 +1870,7 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
 					register_exten(l);
 					/* initialize MWI on line and device */
 					mwi_event_cb(0, l);
-					ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+					ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 				}
 				--instance;
 			}
@@ -1901,7 +1907,7 @@ static int skinny_unregister(struct skinny_req *req, struct skinnysession *s)
 				ast_parse_allow_disallow(&l->prefs, &l->capability, "all", 0);			
 				l->instance = 0;
 				unregister_exten(l);
-				ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Skinny/%s@%s", l->name, d->name);
+				ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 			}
 		}
 	}
@@ -3627,6 +3633,11 @@ static void *skinny_ss(void *data)
 	int res = 0;
 	int loop_pause = 100;
 
+	if (!d) {
+		ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
+		return NULL;
+	}
+
 	ast_verb(3, "Starting simple switch on '%s@%s'\n", l->name, d->name);
 
 	len = strlen(d->exten);
@@ -3728,7 +3739,7 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
 	struct skinny_line *l = sub->parent;
 	struct skinny_device *d = l->device;
 
-	if (!d->registered) {
+	if (!d || !d->registered) {
 		ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
 		return -1;
 	}
@@ -3793,6 +3804,11 @@ static int skinny_hangup(struct ast_channel *ast)
 	d = l->device;
 	s = d->session;
 
+	if (!d) {
+		ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
+		return 0;
+	}
+
 	if (skinnydebug)
 		ast_verb(3,"Hanging up %s/%d\n",d->name,sub->callid);
 
@@ -4171,7 +4187,13 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
 	struct skinny_subchannel *sub = ast->tech_pvt;
 	struct skinny_line *l = sub->parent;
 	struct skinny_device *d = l->device;
-	struct skinnysession *s = d->session;
+	struct skinnysession *s;
+
+	if (!d) {
+		ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
+		return -1;
+	}
+	s = d->session;
 
 	if (!s) {
 		ast_log(LOG_NOTICE, "Asked to indicate '%s' condition on channel %s, but session does not exist.\n", control2str(ind), ast->name);
@@ -4383,8 +4405,13 @@ static int skinny_hold(struct skinny_subchannel *sub)
 	struct skinny_device *d = l->device;
 
 	/* Don't try to hold a channel that doesn't exist */
-	if (!sub || !sub->owner)
+	if (!sub || !sub->owner) {
 		return 0;
+	}
+	if (!d) {
+		ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
+		return 0;
+	}
 
 	/* Channel needs to be put on hold */
 	if (skinnydebug)
@@ -4410,8 +4437,13 @@ static int skinny_unhold(struct skinny_subchannel *sub)
 	struct skinny_device *d = l->device;
 
 	/* Don't try to unhold a channel that doesn't exist */
-	if (!sub || !sub->owner)
+	if (!sub || !sub->owner) {
+		return 0;
+	}
+	if (!d) {
+		ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
 		return 0;
+	}
 
 	/* Channel is on hold, so we will unhold */
 	if (skinnydebug)
@@ -4451,8 +4483,8 @@ static int handle_hold_button(struct skinny_subchannel *sub)
 
 static int handle_transfer_button(struct skinny_subchannel *sub)
 {
-	struct skinny_line *l = sub->parent;
-	struct skinny_device *d = l->device;
+	struct skinny_line *l;
+	struct skinny_device *d;
 	struct skinny_subchannel *newsub;
 	struct ast_channel *c;
 	pthread_t t;
@@ -4461,6 +4493,15 @@ static int handle_transfer_button(struct skinny_subchannel *sub)
 		ast_verbose("Transfer: No subchannel to transfer\n");
 		return -1;
 	}
+
+	l = sub->parent;
+	d = l->device;
+
+	if (!d) {
+		ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
+		return -1;
+	}
+
 	if (!sub->related) {
 		/* Another sub has not been created so this must be first XFER press */
 		if (!sub->onhold) {
@@ -4558,6 +4599,8 @@ static int handle_register_message(struct skinny_req *req, struct skinnysession
 
 		return 0;
 	}
+	ast_atomic_fetchadd_int(&unauth_sessions, -1);
+
 	ast_verb(3, "Device '%s' successfully registered\n", name);
 
 	d = s->device;
@@ -4591,6 +4634,11 @@ static int handle_callforward_button(struct skinny_subchannel *sub, int cfwdtype
 	struct ast_channel *c = sub->owner;
 	pthread_t t;
 
+	if (!d) {
+		ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
+		return 0;
+	}
+
 	if (l->hookstate == SKINNY_ONHOOK) {
 		l->hookstate = SKINNY_OFFHOOK;
 		transmit_speaker_mode(d, SKINNY_SPEAKERON);
@@ -5040,7 +5088,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
 			ast_verb(1, "RECEIVED UNKNOWN STIMULUS:  %d(%d/%d)\n", event, instance, callreference);
 		break;
 	}
-	ast_devstate_changed(AST_DEVICE_UNKNOWN, "Skinny/%s@%s", l->name, d->name);
+	ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 
 	return 1;
 }
@@ -5090,7 +5138,7 @@ static int handle_offhook_message(struct skinny_req *req, struct skinnysession *
 	transmit_ringer_mode(d, SKINNY_RING_OFF);
 	l->hookstate = SKINNY_OFFHOOK;
 
-	ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
+	ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 
 	if (sub && sub->onhold) {
 		return 1;
@@ -5166,7 +5214,7 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
 		return 0;
 	}
 
-	ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+	ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 
 	if (sub->onhold) {
 		return 0;
@@ -5694,7 +5742,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
 		return 0;
 	}
 
-	ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
+	ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 
 	switch(event) {
 	case SOFTKEY_NONE:
@@ -5900,7 +5948,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
 			}
 
 			transmit_callstate(d, l->instance, l->hookstate, sub->callid);
-			ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+			ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 			if (skinnydebug)
 				ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
 			if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
@@ -5924,7 +5972,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
 				}
 			}
 			if ((l->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) && !AST_LIST_NEXT(sub, list)->rtp)) {
-				ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+				ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 			}
 		}
 		break;
@@ -6056,6 +6104,7 @@ static int handle_register_available_lines_message(struct skinny_req *req, struc
 static int handle_message(struct skinny_req *req, struct skinnysession *s)
 {
 	int res = 0;
+	size_t len;
 
 	if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) {
 		ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
@@ -6121,8 +6170,13 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
 				ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
 			}
 
-			d->exten[strlen(d->exten)] = dgt;
-			d->exten[strlen(d->exten)+1] = '\0';
+			len = strlen(d->exten);
+			if (len < sizeof(d->exten) - 1) {
+				d->exten[len] = dgt;
+				d->exten[len + 1] = '\0';
+			} else {
+				ast_log(AST_LOG_WARNING, "Dropping digit with value %d because digit queue is full\n", dgt);
+			}
 		} else
 			res = handle_keypad_button_message(req, s);
 		}
@@ -6236,6 +6290,9 @@ static void destroy_session(struct skinnysession *s)
 			if (s->fd > -1) 
 				close(s->fd);
 			
+			if (!s->device)
+				ast_atomic_fetchadd_int(&unauth_sessions, -1);
+
 			ast_mutex_destroy(&s->lock);
 			
 			ast_free(s);
@@ -6251,13 +6308,30 @@ static int get_input(struct skinnysession *s)
 {
 	int res;
 	int dlen = 0;
+	int timeout = keep_alive * 1100;
+	time_t now;
 	int *bufaddr;
 	struct pollfd fds[1];
 
+	if (!s->device) {
+		if(time(&now) == -1) {
+			ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
+			return -1;
+		}
+
+		timeout = (auth_timeout - (now - s->start)) * 1000;
+		if (timeout < 0) {
+			/* we have timed out */
+			if (skinnydebug)
+				ast_verb(1, "Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
+			return -1;
+		}
+	}
+
 	fds[0].fd = s->fd;
 	fds[0].events = POLLIN;
 	fds[0].revents = 0;
-	res = ast_poll(fds, 1, (keep_alive * 1100)); /* If nothing has happen, client is dead */
+	res = ast_poll(fds, 1, timeout); /* If nothing has happen, client is dead */
 						 /* we add 10% to the keep_alive to deal */
 						 /* with network delays, etc */
 	if (res < 0) {
@@ -6266,8 +6340,13 @@ static int get_input(struct skinnysession *s)
 			return res;
 		}
 	} else if (res == 0) {
-		if (skinnydebug)
-			ast_verb(1, "Skinny Client was lost, unregistering\n");
+		if (skinnydebug) {
+			if (s->device) {
+				ast_verb(1, "Skinny Client was lost, unregistering\n");
+			} else {
+				ast_verb(1, "Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
+			}
+		}
 		skinny_unregister(NULL, s);
 		return -1;
 	}
@@ -6400,18 +6479,35 @@ static void *accept_thread(void *ignore)
 			ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
 			continue;
 		}
+
+		if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= auth_limit) {
+			close(as);
+			ast_atomic_fetchadd_int(&unauth_sessions, -1);
+			continue;
+		}
+
 		p = getprotobyname("tcp");
 		if(p) {
 			if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
 				ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
 			}
 		}
-		if (!(s = ast_calloc(1, sizeof(struct skinnysession))))
+		if (!(s = ast_calloc(1, sizeof(struct skinnysession)))) {
+			close(as);
+			ast_atomic_fetchadd_int(&unauth_sessions, -1);
 			continue;
+		}
 
 		memcpy(&s->sin, &sin, sizeof(sin));
 		ast_mutex_init(&s->lock);
 		s->fd = as;
+
+		if(time(&s->start) == -1) {
+			ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
+			destroy_session(s);
+			continue;
+		}
+
 		AST_LIST_LOCK(&sessions);
 		AST_LIST_INSERT_HEAD(&sessions, s, list);
 		AST_LIST_UNLOCK(&sessions);
@@ -6565,6 +6661,26 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
  			} else if (!strcasecmp(v->name, "keepalive")) {
  				keep_alive = atoi(v->value);
  				continue;
+			} else if (!strcasecmp(v->name, "authtimeout")) {
+				int timeout = atoi(v->value);
+
+				if (timeout < 1) {
+					ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", v->value);
+					auth_timeout = DEFAULT_AUTH_TIMEOUT;
+				} else {
+					auth_timeout = timeout;
+				}
+				continue;
+			} else if (!strcasecmp(v->name, "authlimit")) {
+				int limit = atoi(v->value);
+
+				if (limit < 1) {
+					ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", v->value);
+					auth_limit = DEFAULT_AUTH_LIMIT;
+				} else {
+					auth_limit = limit;
+				}
+				continue;
  			} else if (!strcasecmp(v->name, "regcontext")) {
  				ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
  				stringp = newcontexts;
diff --git a/configs/http.conf.sample b/configs/http.conf.sample
index f15c9cf..6da4ca6 100644
--- a/configs/http.conf.sample
+++ b/configs/http.conf.sample
@@ -32,6 +32,11 @@ bindaddr=127.0.0.1
 ;
 ;prefix=asterisk
 ;
+; sessionlimit specifies the maximum number of httpsessions that will be
+; allowed to exist at any given time. (default: 100)
+;
+;sessionlimit=100
+;
 ; Whether Asterisk should serve static content from http-static
 ; Default is no.
 ;
diff --git a/configs/manager.conf.sample b/configs/manager.conf.sample
index 79c1ba8..7f005b8 100644
--- a/configs/manager.conf.sample
+++ b/configs/manager.conf.sample
@@ -25,6 +25,17 @@ enabled = no
 ;webenabled = yes
 port = 5038
 
+; authtimeout specifies the maximum number of seconds a client has to
+; authenticate.  If the client does not authenticate beofre this timeout
+; expires, the client will be disconnected. (default: 30 seconds)
+
+;authtimeout = 30
+
+; authlimit specifies the maximum number of unauthenticated sessions that will
+; be allowed to connect at any given time.
+
+;authlimit = 50
+
 ;httptimeout = 60
 ; a) httptimeout sets the Max-Age of the http cookie
 ; b) httptimeout is the amount of time the webserver waits 
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 291f86e..0e59389 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -144,6 +144,16 @@ tcpbindaddr=0.0.0.0             ; IP address for TCP server to bind to (0.0.0.0
 ;        A list of valid SSL cipher strings can be found at: 
 ;                http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
 
+;tcpauthtimeout = 30            ; tcpauthtimeout specifies the maximum number
+				; of seconds a client has to authenticate.  If
+				; the client does not authenticate beofre this
+				; timeout expires, the client will be
+                                ; disconnected. (default: 30 seconds)
+
+;tcpauthlimit = 100             ; tcpauthlimit specifies the maximum number of
+				; unauthenticated sessions that will be allowed
+                                ; to connect at any given time. (default: 100)
+
 srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
                                 ; Note: Asterisk only uses the first host 
                                 ; in SRV records
@@ -646,10 +656,18 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ; The following settings are allowed (both globally and in individual sections):
 ;
 ;        nat = no                ; default. Use NAT mode only according to RFC3581 (;rport)
-;        nat = yes               ; Always ignore info and assume NAT
+;        nat = yes               ; Always ignore info and assume NAT (default)
 ;        nat = never             ; Never attempt NAT mode or RFC3581 support
 ;        nat = route             ; route = Assume NAT, don't send rport 
 ;                                ; (work around more UNIDEN bugs)
+;
+; IT IS IMPORTANT TO NOTE that if the nat setting in the general section differs from
+; the nat setting in a peer definition, then the peer username will be discoverable
+; by outside parties as Asterisk will respond to different ports for defined and
+; undefined peers. For this reason it is recommended to ONLY DEFINE NAT SETTINGS IN THE
+; GENERAL SECTION. Specifically, if nat=route or nat=yes in one section and nat=no or
+; nat=never in the other, then valid users with settings differing from those in the
+; general section will be discoverable.
 
 ;----------------------------------- MEDIA HANDLING --------------------------------
 ; By default, Asterisk tries to re-invite media streams to an optimal path. If there's
@@ -972,12 +990,10 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
         type=friend
 
 [natted-phone](!,basic-options)   ; another template inheriting basic-options
-        nat=yes
         directmedia=no
         host=dynamic
 
 [public-phone](!,basic-options)   ; another template inheriting basic-options
-        nat=no
         directmedia=yes
 
 [my-codecs](!)                    ; a template for my preferred codecs
@@ -1012,7 +1028,6 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
                                  ; on incoming calls to Asterisk
 ;host=192.168.0.23               ; we have a static but private IP address
                                  ; No registration allowed
-;nat=no                          ; there is not NAT between phone and Asterisk
 ;directmedia=yes                 ; allow RTP voice traffic to bypass Asterisk
 ;dtmfmode=info                   ; either RFC2833 or INFO for the BudgeTone
 ;call-limit=1                    ; permit only 1 outgoing call and 1 incoming call at a time
@@ -1042,7 +1057,6 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ;regexten=1234                   ; When they register, create extension 1234
 ;callerid="Jane Smith" <5678>
 ;host=dynamic                    ; This device needs to register
-;nat=yes                         ; X-Lite is behind a NAT router
 ;directmedia=no                  ; Typically set to NO if behind NAT
 ;disallow=all
 ;allow=gsm                       ; GSM consumes far less bandwidth than ulaw
@@ -1113,9 +1127,6 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ;type=friend
 ;secret=blah
 ;qualify=200                     ; Qualify peer is no more than 200ms away
-;nat=yes                         ; This phone may be natted
-                                 ; Send SIP and RTP to the IP address that packet is 
-                                 ; received from instead of trusting SIP headers 
 ;host=dynamic                    ; This device registers with us
 ;directmedia=no                  ; Asterisk by default tries to redirect the
                                  ; RTP media stream (audio) to go directly from
diff --git a/configs/skinny.conf.sample b/configs/skinny.conf.sample
index 2c26e6b..1fdf4cb 100644
--- a/configs/skinny.conf.sample
+++ b/configs/skinny.conf.sample
@@ -9,6 +9,15 @@ dateformat=M-D-Y	; M,D,Y in any order (6 chars max)
 			; Use M for month, D for day, Y for year, A for 12-hour time.
 keepalive=120
 
+;authtimeout = 30       ; authtimeout specifies the maximum number of seconds a
+			; client has to authenticate.  If the client does not
+			; authenticate beofre this timeout expires, the client
+                        ; will be disconnected.  (default: 30 seconds)
+
+;authlimit = 50         ; authlimit specifies the maximum number of
+			; unauthenticated sessions that will be allowed to
+                        ; connect at any given time. (default: 50)
+
 ;vmexten=8500		; Systemwide voicemailmain pilot number
 			; It must be in the same context as the calling
 			; device/line
diff --git a/contrib/scripts/astgenkey b/contrib/scripts/astgenkey
index 6376048..be8853c 100644
--- a/contrib/scripts/astgenkey
+++ b/contrib/scripts/astgenkey
@@ -47,7 +47,11 @@ done
 rm -f ${KEY}.key ${KEY}.pub
 
 echo "Generating SSL key '$KEY': "
+oldumask="`umask`"
+umask 0077
 openssl genrsa -out ${KEY}.key ${DES3} 1024
+[ "$(id -u)" = 0 ] && chown asterisk: ${KEY}.key
+umask $oldumask
 openssl rsa -in ${KEY}.key -pubout -out ${KEY}.pub
 
 if [ -f "${KEY}.key" ] && [ -f "${KEY}.pub" ]; then
diff --git a/contrib/scripts/astgenkey.8 b/contrib/scripts/astgenkey.8
index 328a4d2..063feca 100644
--- a/contrib/scripts/astgenkey.8
+++ b/contrib/scripts/astgenkey.8
@@ -53,12 +53,11 @@
 .ta 8n 16n 24n 32n 40n 48n 56n 64n 72n  
 .TH ASTGENKEY 8 "May 14th, 2005" "Asterisk" "Linux Programmer's Manual"
 .SH NAME
-.B astgenkey
--- generates keys for for Asterisk IAX2 RSA authentication
+.B astgenkey \- generates keys for for Asterisk IAX2 RSA authentication
 .SH SYNOPSIS
 .PP 
 .B astgenkey
-[ -q ] [ -n ] [ \fIkeyname\fP ]
+[ \-q ] [ \-n ] [ \fIkeyname\fP ]
 
 .SH DESCRIPTION
 .B astgenkey 
@@ -66,7 +65,7 @@ This script generates an RSA private and public key pair in PEM format
 for use by Asterisk.  The private key should be kept a secret, as it can 
 be used to fake your system's identity.  Thus by default (without the 
 option 
-.I -n
+.I \-n
 ) the script will create a passphrase-encrypted copy of your secret key: 
 without entering the passphrase you won't be able to use it. 
 
@@ -99,12 +98,12 @@ show keys
 .RE
 
 .SH OPTIONS
-.B -q
+.B \-q
 .RS
 Run quietly.
 .RE
 
-.B -n
+.B \-n
 .RS
 Don't encrypt the private key.
 .RE
@@ -132,7 +131,7 @@ user "asterisk").
 .SH "SEE ALSO" 
 asterisk(8), genrsa(1), rsa(1), 
 
-http://www.voip-info.org/wiki-Asterisk+iax+rsa+auth
+http://www.voip\-info.org/wiki\-Asterisk+iax+rsa+auth
 
 .SH "AUTHOR" 
 This manual page was written by Tzafrir Cohen <tzafrir.cohen at xorcom.com> 
@@ -141,4 +140,4 @@ the terms of the GNU General Public License, Version 2 any
 later version published by the Free Software Foundation. 
 
 On Debian systems, the complete text of the GNU General Public 
-License can be found in /usr/share/common-licenses/GPL. 
+License can be found in /usr/share/common\-licenses/GPL\-2. 
diff --git a/contrib/scripts/autosupport.8 b/contrib/scripts/autosupport.8
index 50949e9..ed4d4a8 100644
--- a/contrib/scripts/autosupport.8
+++ b/contrib/scripts/autosupport.8
@@ -37,4 +37,4 @@ the terms of the GNU General Public License, Version 2 any
 later version published by the Free Software Foundation. 
 
 On Debian systems, the complete text of the GNU General Public 
-License can be found in /usr/share/common-licenses/GPL. 
+License can be found in /usr/share/common\-licenses/GPL\-2. 
diff --git a/contrib/scripts/safe_asterisk b/contrib/scripts/safe_asterisk
index 96333e4..376e505 100644
--- a/contrib/scripts/safe_asterisk
+++ b/contrib/scripts/safe_asterisk
@@ -14,6 +14,7 @@ SLEEPSECS=4
 ASTSBINDIR=__ASTERISK_SBIN_DIR__
 ASTVARRUNDIR=__ASTERISK_VARRUN_DIR__
 ASTPIDFILE=${ASTVARRUNDIR}/asterisk.pid
+BACKGROUND=0
 
 # comment this line out to have this script _not_ kill all mpg123 processes when
 # asterisk exits
@@ -105,7 +106,7 @@ ulimit -c unlimited
 # Don't fork when running "safely"
 #
 ASTARGS=""
-if test "x$TTY" != "x" ; then
+if test "x$TTY" != "xno" ; then
 	if test -c /dev/tty${TTY} ; then
 		TTY=tty${TTY}
 	elif test -c /dev/vc/${TTY} ; then
@@ -145,7 +146,7 @@ run_asterisk()
 {
 	while :; do 
 
-		if test "x$TTY" != "x" ; then
+		if test "x$TTY" != "xno" ; then
 			cd /tmp
 			stty sane < /dev/${TTY}
 			nice -n $PRIORITY ${ASTSBINDIR}/asterisk -f ${CLIARGS} ${ASTARGS} > /dev/${TTY} 2>&1 < /dev/${TTY}
@@ -197,4 +198,8 @@ run_asterisk()
 	done
 }
 
-run_asterisk &
+if [ "$BACKGROUND" = "1" ]; then
+	run_asterisk &
+else
+	run_asterisk
+fi
diff --git a/contrib/scripts/safe_asterisk.8 b/contrib/scripts/safe_asterisk.8
index ebd9514..b5395cd 100644
--- a/contrib/scripts/safe_asterisk.8
+++ b/contrib/scripts/safe_asterisk.8
@@ -14,7 +14,7 @@ fear asterisk may crash.
 
 The script does not run in the background like a standard service. Rather, 
 it runs in its own linux virtual console (9, by default).
-It also uses the option '-c' of asterisk(8) to avoid detaching asterisk 
+It also uses the option '\-c' of asterisk(8) to avoid detaching asterisk 
 from that terminal.
 
 safe_asterisk also runs asterisk with unlimited core file size, and thus 
@@ -66,4 +66,4 @@ the terms of the GNU General Public License, Version 2 any
 later version published by the Free Software Foundation. 
 
 On Debian systems, the complete text of the GNU General Public 
-License can be found in /usr/share/common-licenses/GPL. 
+License can be found in /usr/share/common\-licenses/GPL\-2. 
diff --git a/debian/patches/AST-2013-006 b/debian/patches/AST-2013-006
index 5291cfe..3431758 100644
--- a/debian/patches/AST-2013-006
+++ b/debian/patches/AST-2013-006
@@ -12,11 +12,9 @@ the length of the message is an odd number of bytes.
  apps/app_sms.c |    3 ++-
  1 file changed, 2 insertions(+), 1 deletion(-)
 
-diff --git a/apps/app_sms.c b/apps/app_sms.c
-index 08b90d1..75d399a 100644
 --- a/apps/app_sms.c
 +++ b/apps/app_sms.c
-@@ -696,7 +696,7 @@ static void unpacksms16(unsigned char *i, unsigned char l, unsigned char *udh, i
+@@ -692,7 +692,7 @@ static void unpacksms16(unsigned char *i
  	}
  	while (l--) {
  		int v = *i++;
@@ -25,7 +23,7 @@ index 08b90d1..75d399a 100644
  			v = (v << 8) + *i++;
  		}
  		*o++ = v;
-@@ -714,6 +714,7 @@ static int unpacksms(unsigned char dcs, unsigned char *i, unsigned char *udh, in
+@@ -710,6 +710,7 @@ static int unpacksms(unsigned char dcs,
  	} else if (is8bit(dcs)) {
  		unpacksms8(i, l, udh, udhl, ud, udl, udhi);
  	} else {
@@ -33,6 +31,3 @@ index 08b90d1..75d399a 100644
  		unpacksms16(i, l, udh, udhl, ud, udl, udhi);
  	}
  	return l + 1;
--- 
-1.7.10.4
-
diff --git a/debian/patches/AST-2013-007 b/debian/patches/AST-2013-007
index 267a480..7614b28 100644
--- a/debian/patches/AST-2013-007
+++ b/debian/patches/AST-2013-007
@@ -33,8 +33,6 @@ Review: http://reviewboard.digium.internal/r/432/
  main/tcptls.c                      |   11 ++
  12 files changed, 473 insertions(+), 38 deletions(-)
 
-diff --git a/README-SERIOUSLY.bestpractices.txt b/README-SERIOUSLY.bestpractices.txt
-index b470fd6..281d0d3 100644
 --- a/README-SERIOUSLY.bestpractices.txt
 +++ b/README-SERIOUSLY.bestpractices.txt
 @@ -26,6 +26,9 @@ Sections
@@ -47,7 +45,7 @@ index b470fd6..281d0d3 100644
  ----------------
  Additional Links
  ----------------
-@@ -344,3 +347,24 @@ same as the class authorization "system".  Good system configuration, such as
+@@ -344,3 +347,24 @@ same as the class authorization "system"
  not running Asterisk as root, can prevent serious problems from arising when
  allowing external connections to originate calls into Asterisk.
  
@@ -72,11 +70,9 @@ index b470fd6..281d0d3 100644
 +
 +For backwards compatibility, live_dangerously defaults to yes, and must be
 +explicitly set to no to enable this privilege escalation protection.
-diff --git a/UPGRADE.txt b/UPGRADE.txt
-index c28be5a..408a3fa 100644
 --- a/UPGRADE.txt
 +++ b/UPGRADE.txt
-@@ -27,6 +27,15 @@ from 1.8.23.0 to 1.8.24.0:
+@@ -18,6 +18,15 @@
  ===
  ===========================================================
  
@@ -92,11 +88,9 @@ index c28be5a..408a3fa 100644
  From 1.6.1 to 1.6.2:
  
  * SIP no longer sends the 183 progress message for early media by
-diff --git a/funcs/func_db.c b/funcs/func_db.c
-index 8bd4d58..282a962 100644
 --- a/funcs/func_db.c
 +++ b/funcs/func_db.c
-@@ -97,6 +97,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+@@ -93,6 +93,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
  			<para>This function will retrieve a value from the Asterisk database
  			and then remove that key from the database. <variable>DB_RESULT</variable>
  			will be set to the key's value if it exists.</para>
@@ -109,7 +103,7 @@ index 8bd4d58..282a962 100644
  		</description>
  		<see-also>
  			<ref type="application">DBdel</ref>
-@@ -243,10 +249,22 @@ static int function_db_delete(struct ast_channel *chan, const char *cmd,
+@@ -238,10 +244,22 @@ static int function_db_delete(struct ast
  	return 0;
  }
  
@@ -132,7 +126,7 @@ index 8bd4d58..282a962 100644
  };
  
  static int unload_module(void)
-@@ -266,7 +284,7 @@ static int load_module(void)
+@@ -261,7 +279,7 @@ static int load_module(void)
  
  	res |= ast_custom_function_register(&db_function);
  	res |= ast_custom_function_register(&db_exists_function);
@@ -141,11 +135,9 @@ index 8bd4d58..282a962 100644
  
  	return res;
  }
-diff --git a/funcs/func_env.c b/funcs/func_env.c
-index cbc80e8..7fc1823 100644
 --- a/funcs/func_env.c
 +++ b/funcs/func_env.c
-@@ -71,6 +71,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+@@ -65,6 +65,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
  			<parameter name="filename" required="true" />
  		</syntax>
  		<description>
@@ -157,7 +149,7 @@ index cbc80e8..7fc1823 100644
  		</description>
  	</function>
  	<function name="FILE" language="en_US">
-@@ -167,6 +172,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+@@ -83,6 +88,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
  			</parameter>
  		</syntax>
  		<description>
@@ -169,7 +161,7 @@ index cbc80e8..7fc1823 100644
  		</description>
  	</function>
   ***/
-@@ -1258,8 +1278,8 @@ static int load_module(void)
+@@ -259,8 +269,8 @@ static int load_module(void)
  	int res = 0;
  
  	res |= ast_custom_function_register(&env_function);
@@ -180,11 +172,9 @@ index cbc80e8..7fc1823 100644
  
  	return res;
  }
-diff --git a/funcs/func_lock.c b/funcs/func_lock.c
-index 92d5776..40deb20 100644
 --- a/funcs/func_lock.c
 +++ b/funcs/func_lock.c
-@@ -59,6 +59,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+@@ -55,6 +55,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
  			Returns <literal>1</literal> if the lock was obtained or <literal>0</literal> on error.</para>
  			<note><para>To avoid the possibility of a deadlock, LOCK will only attempt to
  			obtain the lock for 3 seconds if the channel already has another lock.</para></note>
@@ -196,7 +186,7 @@ index 92d5776..40deb20 100644
  		</description>
  	</function>
  	<function name="TRYLOCK" language="en_US">
-@@ -72,6 +77,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+@@ -68,6 +73,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
  			<para>Attempts to grab a named lock exclusively, and prevents other channels
  			from obtaining the same lock.  Returns <literal>1</literal> if the lock was 
  			available or <literal>0</literal> otherwise.</para>
@@ -208,7 +198,7 @@ index 92d5776..40deb20 100644
  		</description>
  	</function>
  	<function name="UNLOCK" language="en_US">
-@@ -86,6 +96,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+@@ -82,6 +92,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
  			had a lock or <literal>0</literal> otherwise.</para>
  			<note><para>It is generally unnecessary to unlock in a hangup routine, as any locks 
  			held are automatically freed when the channel is destroyed.</para></note>
@@ -220,7 +210,7 @@ index 92d5776..40deb20 100644
  		</description>
  	</function>
   ***/
-@@ -502,9 +517,9 @@ static int unload_module(void)
+@@ -484,9 +499,9 @@ static int unload_module(void)
  
  static int load_module(void)
  {
@@ -233,10 +223,9 @@ index 92d5776..40deb20 100644
  	ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL);
  	return res;
  }
-index 6d7338c..8e0d2c6 100644
 --- a/funcs/func_realtime.c
 +++ b/funcs/func_realtime.c
-@@ -115,6 +115,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+@@ -99,6 +99,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
  		<description>
  			<para>This function acts in the same way as REALTIME(....) does, except that
  			it destroys the matched record in the RT engine.</para>
@@ -249,7 +238,7 @@ index 6d7338c..8e0d2c6 100644
  		</description>
  	</function>
  	<function name="REALTIME_FIELD" language="en_US">
-@@ -439,28 +445,32 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
+@@ -398,28 +404,32 @@ static int function_realtime_readdestroy
  		return -1;
  	}
  
@@ -259,14 +248,6 @@ index 6d7338c..8e0d2c6 100644
 -		resultslen += strlen(var->name) + strlen(var->value);
 -	/* add space for delimiters and final '\0' */
 -	resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
--
--	if (resultslen > len) {
--		/* Unfortunately this does mean that we cannot destroy the row
--		 * anymore. But OTOH, we're not destroying someones data without
--		 * giving him the chance to look at it. */
--		ast_log(LOG_WARNING, "Failed to fetch/destroy. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
--		return -1;
--	}
 +	if (len > 0) {
 +		resultslen = 0;
 +		n = 0;
@@ -275,7 +256,14 @@ index 6d7338c..8e0d2c6 100644
 +		}
 +		/* add space for delimiters and final '\0' */
 +		resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
-+
+ 
+-	if (resultslen > len) {
+-		/* Unfortunately this does mean that we cannot destroy the row
+-		 * anymore. But OTOH, we're not destroying someones data without
+-		 * giving him the chance to look at it. */
+-		ast_log(LOG_WARNING, "Failed to fetch/destroy. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
+-		return -1;
+-	}
 +		if (resultslen > len) {
 +			/* Unfortunately this does mean that we cannot destroy
 +			 * the row anymore. But OTOH, we're not destroying
@@ -302,7 +290,7 @@ index 6d7338c..8e0d2c6 100644
  
  	ast_destroy_realtime(args.family, args.fieldmatch, args.value, SENTINEL);
  	ast_variables_destroy(head);
-@@ -471,6 +481,15 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
+@@ -430,6 +440,15 @@ static int function_realtime_readdestroy
  	return 0;
  }
  
@@ -318,7 +306,7 @@ index 6d7338c..8e0d2c6 100644
  struct ast_custom_function realtime_function = {
  	.name = "REALTIME",
  	.read = function_realtime_read,
-@@ -496,6 +515,7 @@ static struct ast_custom_function realtime_store_function = {
+@@ -455,6 +474,7 @@ struct ast_custom_function realtime_stor
  struct ast_custom_function realtime_destroy_function = {
  	.name = "REALTIME_DESTROY",
  	.read = function_realtime_readdestroy,
@@ -326,7 +314,7 @@ index 6d7338c..8e0d2c6 100644
  };
  
  static int unload_module(void)
-@@ -514,7 +534,7 @@ static int load_module(void)
+@@ -473,7 +493,7 @@ static int load_module(void)
  	int res = 0;
  	res |= ast_custom_function_register(&realtime_function);
  	res |= ast_custom_function_register(&realtime_store_function);
@@ -335,11 +323,9 @@ index 6d7338c..8e0d2c6 100644
  	res |= ast_custom_function_register(&realtimefield_function);
  	res |= ast_custom_function_register(&realtimehash_function);
  	return res;
-diff --git a/funcs/func_shell.c b/funcs/func_shell.c
-index bad10b3..e403efc 100644
 --- a/funcs/func_shell.c
 +++ b/funcs/func_shell.c
-@@ -88,12 +88,17 @@ static int shell_helper(struct ast_channel *chan, const char *cmd, char *data,
+@@ -75,12 +75,17 @@ static int shell_helper(struct ast_chann
  		</syntax>
  		<description>
  			<para>Returns the value from a system command</para>
@@ -363,7 +349,7 @@ index bad10b3..e403efc 100644
  		</description>
   
  	</function>
-@@ -109,7 +115,7 @@ static int unload_module(void)
+@@ -97,7 +102,7 @@ static int unload_module(void)
  
  static int load_module(void)
  {
@@ -372,11 +358,9 @@ index bad10b3..e403efc 100644
  }
  
  AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Returns the output of a shell command");
-diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
-index 52710e8..2531413 100644
 --- a/include/asterisk/pbx.h
 +++ b/include/asterisk/pbx.h
-@@ -1149,16 +1149,44 @@ struct ast_custom_function* ast_custom_function_find(const char *name);
+@@ -1023,16 +1023,44 @@ struct ast_custom_function* ast_custom_f
  int ast_custom_function_unregister(struct ast_custom_function *acf);
  
  /*!
@@ -421,7 +405,7 @@ index 52710e8..2531413 100644
   * \brief Retrieve the number of active calls
   */
  int ast_active_calls(void);
-@@ -1271,6 +1299,32 @@ unsigned int ast_hashtab_hash_contexts(const void *obj);
+@@ -1129,6 +1157,32 @@ int ast_hashtab_compare_contexts(const v
  unsigned int ast_hashtab_hash_contexts(const void *obj);
  /*! @} */
  
@@ -454,11 +438,9 @@ index 52710e8..2531413 100644
  #if defined(__cplusplus) || defined(c_plusplus)
  }
  #endif
-diff --git a/main/asterisk.c b/main/asterisk.c
-index fe4088b..c563b70 100644
 --- a/main/asterisk.c
 +++ b/main/asterisk.c
-@@ -3036,6 +3036,8 @@ static void ast_readconfig(void)
+@@ -2828,6 +2828,8 @@ static void ast_readconfig(void)
  		unsigned int dbdir:1;
  		unsigned int keydir:1;
  	} found = { 0, 0 };
@@ -467,7 +449,7 @@ index fe4088b..c563b70 100644
  
  	if (ast_opt_override_config) {
  		cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
-@@ -3245,8 +3247,11 @@ static void ast_readconfig(void)
+@@ -3033,8 +3035,11 @@ static void ast_readconfig(void)
  			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIDE_CONSOLE_CONNECT);
  		} else if (!strcasecmp(v->name, "sendfullybooted")) {
  			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_SEND_FULLYBOOTED);
@@ -479,11 +461,9 @@ index fe4088b..c563b70 100644
  	for (v = ast_variable_browse(cfg, "compat"); v; v = v->next) {
  		float version;
  		if (sscanf(v->value, "%30f", &version) != 1) {
-diff --git a/main/pbx.c b/main/pbx.c
-index 996ac25..e370ae7 100644
 --- a/main/pbx.c
 +++ b/main/pbx.c
-@@ -821,6 +821,17 @@ static struct ast_taskprocessor *device_state_tps;
+@@ -759,6 +759,17 @@ static struct ast_taskprocessor *device_
  
  AST_THREADSTORAGE(switch_data);
  AST_THREADSTORAGE(extensionstate_buf);
@@ -501,7 +481,7 @@ index 996ac25..e370ae7 100644
  
  /*!
     \brief ast_exten: An extension
-@@ -1175,6 +1186,19 @@ static int totalcalls;
+@@ -1079,6 +1090,19 @@ static int totalcalls;
  
  static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
  
@@ -521,7 +501,7 @@ index 996ac25..e370ae7 100644
  /*! \brief Declaration of builtin applications */
  static struct pbx_builtin {
  	char name[AST_MAX_APP];
-@@ -3748,6 +3772,7 @@ struct ast_custom_function *ast_custom_function_find(const char *name)
+@@ -3216,6 +3240,7 @@ struct ast_custom_function *ast_custom_f
  int ast_custom_function_unregister(struct ast_custom_function *acf)
  {
  	struct ast_custom_function *cur;
@@ -529,7 +509,7 @@ index 996ac25..e370ae7 100644
  
  	if (!acf) {
  		return -1;
-@@ -3764,9 +3789,64 @@ int ast_custom_function_unregister(struct ast_custom_function *acf)
+@@ -3230,9 +3255,64 @@ int ast_custom_function_unregister(struc
  	}
  	AST_RWLIST_UNLOCK(&acf_root);
  
@@ -594,7 +574,7 @@ index 996ac25..e370ae7 100644
  /*! \internal
   *  \brief Retrieve the XML documentation of a specified ast_custom_function,
   *         and populate ast_custom_function string fields.
-@@ -3868,6 +3948,50 @@ int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_m
+@@ -3332,6 +3412,50 @@ int __ast_custom_function_register(struc
  	return 0;
  }
  
@@ -645,7 +625,7 @@ index 996ac25..e370ae7 100644
  /*! \brief return a pointer to the arguments of the function,
   * and terminates the function name with '\\0'
   */
-@@ -3889,6 +4013,124 @@ static char *func_args(char *function)
+@@ -3353,6 +3477,124 @@ static char *func_args(char *function)
  	return args;
  }
  
@@ -770,7 +750,7 @@ index 996ac25..e370ae7 100644
  int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
  {
  	char *copy = ast_strdupa(function);
-@@ -3901,6 +4143,8 @@ int ast_func_read(struct ast_channel *chan, const char *function, char *workspac
+@@ -3363,6 +3605,8 @@ int ast_func_read(struct ast_channel *ch
  		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
  	else if (!acfptr->read)
  		ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
@@ -779,7 +759,7 @@ index 996ac25..e370ae7 100644
  	else {
  		int res;
  		struct ast_module_user *u = NULL;
-@@ -3977,11 +4223,13 @@ int ast_func_write(struct ast_channel *chan, const char *function, const char *v
+@@ -3382,11 +3626,13 @@ int ast_func_write(struct ast_channel *c
  	char *args = func_args(copy);
  	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
  
@@ -796,11 +776,9 @@ index 996ac25..e370ae7 100644
  		int res;
  		struct ast_module_user *u = NULL;
  		if (acfptr->mod)
-diff --git a/main/tcptls.c b/main/tcptls.c
-index c25f63f..32931b9 100644
 --- a/main/tcptls.c
 +++ b/main/tcptls.c
-@@ -48,6 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+@@ -44,6 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
  #include "asterisk/options.h"
  #include "asterisk/manager.h"
  #include "asterisk/astobj2.h"
@@ -808,7 +786,7 @@ index c25f63f..32931b9 100644
  
  /*! \brief
   * replacement read/write functions for SSL support.
-@@ -161,6 +162,16 @@ static void *handle_tcptls_connection(void *data)
+@@ -135,6 +136,16 @@ static void *handle_tcptls_connection(vo
  	char err[256];
  #endif
  
@@ -825,6 +803,3 @@ index c25f63f..32931b9 100644
  	/*
  	* open a FILE * as appropriate.
  	*/
--- 
-1.7.10.4
-
diff --git a/debian/patches/ASTERISK-20658 b/debian/patches/ASTERISK-20658
index e0093a3..fb0b0a6 100644
--- a/debian/patches/ASTERISK-20658
+++ b/debian/patches/ASTERISK-20658
@@ -24,11 +24,9 @@ patches:
  main/config.c         |   11 +++++++++++
  2 files changed, 28 insertions(+)
 
-diff --git a/funcs/func_realtime.c b/funcs/func_realtime.c
-index a1b6d20..6d7338c 100644
 --- a/funcs/func_realtime.c
 +++ b/funcs/func_realtime.c
-@@ -219,6 +219,13 @@ static int function_realtime_read(struct ast_channel *chan, const char *cmd, cha
+@@ -185,6 +185,13 @@ static int function_realtime_read(struct
  	/* add space for delimiters and final '\0' */
  	resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
  
@@ -42,7 +40,7 @@ index a1b6d20..6d7338c 100644
  	out = ast_str_alloca(resultslen);
  	for (var = head; var; var = var->next)
  		ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
-@@ -439,6 +446,16 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
+@@ -398,6 +405,16 @@ static int function_realtime_readdestroy
  	/* add space for delimiters and final '\0' */
  	resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
  
@@ -59,11 +57,9 @@ index a1b6d20..6d7338c 100644
  	out = ast_str_alloca(resultslen);
  	for (var = head; var; var = var->next) {
  		ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
-diff --git a/main/config.c b/main/config.c
-index 4a71c1f..f90cfc1 100644
 --- a/main/config.c
 +++ b/main/config.c
-@@ -1524,6 +1524,17 @@ static struct ast_config *config_text_file_load(const char *database, const char
+@@ -1325,6 +1325,17 @@ static struct ast_config *config_text_fi
  		while (!feof(f)) {
  			lineno++;
  			if (fgets(buf, sizeof(buf), f)) {
@@ -81,6 +77,3 @@ index 4a71c1f..f90cfc1 100644
  				if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
  					CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
  					ast_str_reset(lline_buffer);        /* erase the lline buffer */
--- 
-1.7.10.4
-
diff --git a/doc/asterisk.8 b/doc/asterisk.8
index ef90884..331d732 100644
--- a/doc/asterisk.8
+++ b/doc/asterisk.8
@@ -9,10 +9,10 @@
 asterisk \- All-purpose telephony server.
 .SH SYNOPSIS
 
-\fBasterisk\fR [ \fB-tThfdvVqpRgciIn\fR ] [ \fB-C \fIfile\fB\fR ] [ \fB-U \fIuser\fB\fR ] [ \fB-G \fIgroup\fB\fR ] [ \fB-x \fIcommand\fB\fR ] [ \fB-M \fIvalue\fB\fR ]
+\fBasterisk\fR [ \fB\-tThfdvVqpRgciIn\fR ] [ \fB\-C \fIfile\fB\fR ] [ \fB\-U \fIuser\fB\fR ] [ \fB\-G \fIgroup\fB\fR ] [ \fB\-x \fIcommand\fB\fR ] [ \fB\-M \fIvalue\fB\fR ]
 
 
-\fBasterisk -r\fR [ \fB-v\fR ] [ \fB-x \fIcommand\fB\fR ] [ \fB-s \fIsocket\fB\fR ]
+\fBasterisk \-r\fR [ \fB\-v\fR ] [ \fB\-x \fIcommand\fB\fR ] [ \fB\-s \fIsocket\fB\fR ]
 
 .SH "DESCRIPTION"
 .PP
@@ -32,7 +32,7 @@ Virtually all aspects of the operation of asterisk's configuration files
 can be found in the sample configuration files.  The format for those files
 is generally beyond the scope of this man page.
 .PP
-When running with \fB-c\fR, \fB-r\fR or \fB-R\fR
+When running with \fB\-c\fR, \fB\-r\fR or \fB\-R\fR
 options, Asterisk supplies a powerful command line, including command
 completion, which may be used to monitors its status, perform a variety
 of administrative actions and even explore the applications that are
@@ -41,161 +41,161 @@ currently loaded into the system.
 Asterisk is a trademark of Digium, Inc.
 .SH "OPTIONS"
 .TP
-\fB-C \fIfile\fB\fR
+\fB\-C \fIfile\fB\fR
 Use \fIfile\fR as master configuration file
 instead of the default, /etc/asterisk/asterisk.conf
 .TP
-\fB-c\fR
+\fB\-c\fR
 Provide a control console on the calling terminal.
 Specifying this option implies \fB-f\fR and will cause
 asterisk to no longer fork or detach from the controlling terminal.
 .TP
-\fB-d\fR
+\fB\-d\fR
 Enable extra debugging statements.
 
 Note: This always sets the debug level in the asterisk process,
 even if it is running in the background. This will affect the size
 of your log files.
 .TP
-\fB-f\fR
+\fB\-f\fR
 Do not fork or detach from controlling terminal.
 .TP
-\fB-g\fR
+\fB\-g\fR
 Remove resource limit on core size, thus forcing Asterisk to dump
 core in the unlikely event of a segmentation fault or abort signal.
 \fBNOTE:\fR in some cases this may be incompatible
-with the \fB-U\fR or \fB-G\fR flags.
+with the \fB\-U\fR or \fB\-G\fR flags.
 .TP
-\fB-G \fIgroup\fB\fR
+\fB\-G \fIgroup\fB\fR
 Run as group \fIgroup\fR instead of the
 calling group.  \fBNOTE:\fR this requires substantial work
 to be sure that Asterisk's environment has permission to write
 the files required for its operation, including logs, its comm
 socket, the asterisk database, etc.
 .TP
-\fB-h\fR
+\fB\-h\fR
 Provide brief summary of command line arguments and terminate.
 .TP
-\fB-i\fR
+\fB\-i\fR
 Prompt user to initialize any encrypted private keys for IAX2
 secure authentication during startup.
 .TP
-\fB-L \fIloadaverage\fB\fR
+\fB\-L \fIloadaverage\fB\fR
 Limits the maximum load average before rejecting new calls.  This can
 be useful to prevent a system from being brought down by terminating
 too many simultaneous calls.
 .TP
-\fB-m\fR
-Disable log and verbose output to remote (-r) consoles.
+\fB\-m\fR
+Disable log and verbose output to remote (\-r) consoles.
 .TP
 \fB-M \fIvalue\fB\fR
 Limits the maximum number of calls to the specified value.  This can
 be useful to prevent a system from being brought down by terminating
 too many simultaneous calls.
 .TP
-\fB-n\fR
+\fB\-n\fR
 Disable ANSI colors even on terminals capable of displaying them.
 .TP
-\fB-p\fR
+\fB\-p\fR
 If supported by the operating system (and executing as root),
 attempt to run with realtime priority for increased performance and
 responsiveness within the Asterisk process, at the expense of other
 programs running on the same machine.
 .TP
-\fB-q\fR
+\fB\-q\fR
 Reduce default console output when running in conjunction with
-console mode (\fB-c\fR).
+console mode (\fB\-c\fR).
 .TP
-\fB-r\fR
+\fB\-r\fR
 Instead of running a new Asterisk process, attempt to connect
 to a running Asterisk process and provide a console interface
 for controlling it.
 .TP
-\fB-R\fR
-Much like \fB-r\fR\&.  Instead of running a new Asterisk process, attempt to connect
+\fB\-R\fR
+Much like \fB\-r\fR\&.  Instead of running a new Asterisk process, attempt to connect
 to a running Asterisk process and provide a console interface
 for controlling it. Additionally, if connection to the Asterisk 
 process is lost, attempt to reconnect for as long as 30 seconds.
 .TP
-\fB-s \fIsocket\fB\fR
+\fB\-s \fIsocket\fB\fR
 Allows to specify the socket file to be used to connect to the
 Asterisk console. Used in conjunction with \fB-r\fR or \fB-R\fR.
 .TP
-\fB-I\fR
+\fB\-I\fR
 Enable internal timing if DAHDI timer is available
 The default behaviour is that outbound packets are phase locked
 to inbound packets. Enabling this switch causes them to be
 locked to the internal DAHDI timer instead.
 .TP
-\fB-t\fR
+\fB\-t\fR
 When recording files, write them first into a temporary holding directory, 
 then move them into the final location when done.
 .TP
-\fB-T\fR
+\fB\-T\fR
 Add timestamp to all non-command related output going to the console
 when running with verbose and/or logging to the console.
 .TP
-\fB-U \fIuser\fB\fR
+\fB\-U \fIuser\fB\fR
 Run as user \fIuser\fR instead of the
 calling user.  \fBNOTE:\fR this requires substantial work
 to be sure that Asterisk's environment has permission to write
 the files required for its operation, including logs, its comm
 socket, the asterisk database, etc.
 .TP
-\fB-v\fR
+\fB\-v\fR
 Increase the level of verboseness on the console.  The more times
 \fB-v\fR is specified, the more verbose the output is.
-Specifying this option implies \fB-f\fR and will cause
+Specifying this option implies \fB\-f\fR and will cause
 asterisk to no longer fork or detach from the controlling terminal.
-This option may also be used in conjunction with \fB-r\fR
-and \fB-R\fR\&.
+This option may also be used in conjunction with \fB\-r\fR
+and \fB\-R\fR\&.
 
 Note: This always sets the verbose level in the asterisk process,
 even if it is running in the background. This will affect the size
 of your log files.
 .TP
-\fB-V\fR
+\fB\-V\fR
 Display version information and exit immediately.
 .TP
-\fB-W\fR
+\fB\-W\fR
 Change the terminal colors to compensate for a light background,
 rather than a dark background, as is the default.
 .TP
-\fB-x \fIcommand\fB\fR
+\fB\-x \fIcommand\fB\fR
 Connect to a running Asterisk process and execute a command on
 a command line, passing any output through to standard out and
 then terminating when the command execution completes.  Implies
-\fB-r\fR when \fB-R\fR is not explicitly
+\fB\-r\fR when \fB\-R\fR is not explicitly
 supplied.
 .SH "EXAMPLES"
 .PP
-\fBasterisk\fR - Begin Asterisk as a daemon
+\fBasterisk\fR \- Begin Asterisk as a daemon
 .PP
-\fBasterisk -vvvgc\fR - Run on controlling terminal
+\fBasterisk \-vvvgc\fR \- Run on controlling terminal
 .PP
-\fBasterisk -rx "core show channels"\fR - Display channels on running server
+\fBasterisk \-rx "core show channels"\fR \- Display channels on running server
 .SH "BUGS"
 .PP
 Bug reports and feature requests may be filed at http://bugs.digium.com
 .SH "SEE ALSO"
 .PP
-*CLI> \fBhelp\fR - Help on Asterisk CLI
+*CLI> \fBhelp\fR \- Help on Asterisk CLI
 .PP
-*CLI> \fBcore show applications\fR - Show loaded dialplan applications
+*CLI> \fBcore show applications\fR \- Show loaded dialplan applications
 .PP
-*CLI> \fBcore show functions\fR - Show loaded dialplan functions
+*CLI> \fBcore show functions\fR \- Show loaded dialplan functions
 .PP
-*CLI> \fBdialplan show\fR - Show current dialplan
+*CLI> \fBdialplan show\fR \- Show current dialplan
 .PP
-http://www.asterisk.org - The Asterisk Home Page
+http://www.asterisk.org \- The Asterisk Home Page
 .PP
-http://www.asteriskdocs.org - The Asterisk Documentation Project
+http://www.asteriskdocs.org \- The Asterisk Documentation Project
 .PP
-http://www.voip-info.org/wiki-Asterisk - The Asterisk Wiki
+http://www.voip\-info.org/wiki-Asterisk \- The Asterisk Wiki
 .PP
-http://www.digium.com/ - Asterisk sponsor and hardware supplier
+http://www.digium.com/ \- Asterisk sponsor and hardware supplier
 .PP
-http://www.markocam.com/ - Asterisk author's web cam
+http://www.markocam.com/ \- Asterisk author's web cam
 .SH "AUTHOR"
 .PP
 Mark Spencer <markster at digium.com>
diff --git a/funcs/func_db.c b/funcs/func_db.c
index 5e54314..7760b0b 100644
--- a/funcs/func_db.c
+++ b/funcs/func_db.c
@@ -93,6 +93,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 154542 $")
 			<para>This function will retrieve a value from the Asterisk database
 			and then remove that key from the database. <variable>DB_RESULT</variable>
 			will be set to the key's value if it exists.</para>
+			<note>
+				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+				is set to <literal>no</literal>, this function can only be read from the
+				dialplan, and not directly from external protocols. It can, however, be
+				executed as a write operation (<literal>DB_DELETE(family, key)=ignored</literal>)</para>
+			</note>
 		</description>
 		<see-also>
 			<ref type="application">DBdel</ref>
@@ -238,10 +244,22 @@ static int function_db_delete(struct ast_channel *chan, const char *cmd,
 	return 0;
 }
 
+/*!
+ * \brief Wrapper to execute DB_DELETE from a write operation. Allows execution
+ * even if live_dangerously is disabled.
+ */
+static int function_db_delete_write(struct ast_channel *chan, const char *cmd, char *parse,
+	const char *value)
+{
+	/* Throwaway to hold the result from the read */
+	char buf[128];
+	return function_db_delete(chan, cmd, parse, buf, sizeof(buf));
+}
 
 static struct ast_custom_function db_delete_function = {
 	.name = "DB_DELETE",
 	.read = function_db_delete,
+	.write = function_db_delete_write,
 };
 
 static int unload_module(void)
@@ -261,7 +279,7 @@ static int load_module(void)
 
 	res |= ast_custom_function_register(&db_function);
 	res |= ast_custom_function_register(&db_exists_function);
-	res |= ast_custom_function_register(&db_delete_function);
+	res |= ast_custom_function_register_escalating(&db_delete_function, AST_CFE_READ);
 
 	return res;
 }
diff --git a/funcs/func_devstate.c b/funcs/func_devstate.c
index 3df7b6a..6f79c13 100644
--- a/funcs/func_devstate.c
+++ b/funcs/func_devstate.c
@@ -128,7 +128,7 @@ static int devstate_write(struct ast_channel *chan, const char *cmd, char *data,
 
 	ast_db_put(astdb_family, data, value);
 
-	ast_devstate_changed(state_val, "Custom:%s", data);
+	ast_devstate_changed(state_val, AST_DEVSTATE_CACHABLE, "Custom:%s", data);
 
 	return 0;
 }
@@ -290,7 +290,7 @@ static char *handle_cli_devstate_change(struct ast_cli_entry *e, int cmd, struct
 
 	ast_db_put(astdb_family, dev, state);
 
-	ast_devstate_changed(state_val, "Custom:%s", dev);
+	ast_devstate_changed(state_val, AST_DEVSTATE_CACHABLE, "Custom:%s", dev);
 
 	return CLI_SUCCESS;
 }
@@ -336,7 +336,7 @@ static int load_module(void)
 		if (dev_name <= (const char *) 1)
 			continue;
 		ast_devstate_changed(ast_devstate_val(db_entry->data),
-			"Custom:%s\n", dev_name);
+			AST_DEVSTATE_CACHABLE, "Custom:%s\n", dev_name);
 	}
 	ast_db_freetree(db_tree);
 	db_tree = NULL;
diff --git a/funcs/func_env.c b/funcs/func_env.c
index 69261fe..ffb6e5d 100644
--- a/funcs/func_env.c
+++ b/funcs/func_env.c
@@ -65,6 +65,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 182278 $")
 			<parameter name="filename" required="true" />
 		</syntax>
 		<description>
+			<note>
+				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+				is set to <literal>no</literal>, this function can only be executed from the
+				dialplan, and not directly from external protocols.</para>
+			</note>
 		</description>
 	</function>
 	<function name="FILE" language="en_US">
@@ -83,6 +88,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 182278 $")
 			</parameter>
 		</syntax>
 		<description>
+			<note>
+				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+				is set to <literal>no</literal>, this function can only be executed from the
+				dialplan, and not directly from external protocols.</para>
+			</note>
 		</description>
 	</function>
  ***/
@@ -259,8 +269,8 @@ static int load_module(void)
 	int res = 0;
 
 	res |= ast_custom_function_register(&env_function);
-	res |= ast_custom_function_register(&stat_function);
-	res |= ast_custom_function_register(&file_function);
+	res |= ast_custom_function_register_escalating(&stat_function, AST_CFE_READ);
+	res |= ast_custom_function_register_escalating(&file_function, AST_CFE_READ);
 
 	return res;
 }
diff --git a/funcs/func_lock.c b/funcs/func_lock.c
index b65bb19..b582a8a 100644
--- a/funcs/func_lock.c
+++ b/funcs/func_lock.c
@@ -55,6 +55,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 232015 $")
 			Returns <literal>1</literal> if the lock was obtained or <literal>0</literal> on error.</para>
 			<note><para>To avoid the possibility of a deadlock, LOCK will only attempt to
 			obtain the lock for 3 seconds if the channel already has another lock.</para></note>
+			<note>
+				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+				is set to <literal>no</literal>, this function can only be executed from the
+				dialplan, and not directly from external protocols.</para>
+			</note>
 		</description>
 	</function>
 	<function name="TRYLOCK" language="en_US">
@@ -68,6 +73,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 232015 $")
 			<para>Attempts to grab a named lock exclusively, and prevents other channels
 			from obtaining the same lock.  Returns <literal>1</literal> if the lock was 
 			available or <literal>0</literal> otherwise.</para>
+			<note>
+				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+				is set to <literal>no</literal>, this function can only be executed from the
+				dialplan, and not directly from external protocols.</para>
+			</note>
 		</description>
 	</function>
 	<function name="UNLOCK" language="en_US">
@@ -82,6 +92,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 232015 $")
 			had a lock or <literal>0</literal> otherwise.</para>
 			<note><para>It is generally unnecessary to unlock in a hangup routine, as any locks 
 			held are automatically freed when the channel is destroyed.</para></note>
+			<note>
+				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+				is set to <literal>no</literal>, this function can only be executed from the
+				dialplan, and not directly from external protocols.</para>
+			</note>
 		</description>
 	</function>
  ***/
@@ -484,9 +499,9 @@ static int unload_module(void)
 
 static int load_module(void)
 {
-	int res = ast_custom_function_register(&lock_function);
-	res |= ast_custom_function_register(&trylock_function);
-	res |= ast_custom_function_register(&unlock_function);
+	int res = ast_custom_function_register_escalating(&lock_function, AST_CFE_READ);
+	res |= ast_custom_function_register_escalating(&trylock_function, AST_CFE_READ);
+	res |= ast_custom_function_register_escalating(&unlock_function, AST_CFE_READ);
 	ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL);
 	return res;
 }
diff --git a/funcs/func_realtime.c b/funcs/func_realtime.c
index 2746e61..fe9ef67 100644
--- a/funcs/func_realtime.c
+++ b/funcs/func_realtime.c
@@ -99,6 +99,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 206811 $")
 		<description>
 			<para>This function acts in the same way as REALTIME(....) does, except that
 			it destroys the matched record in the RT engine.</para>
+			<note>
+				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+				is set to <literal>no</literal>, this function can only be read from the
+				dialplan, and not directly from external protocols. It can, however, be
+				executed as a write operation (<literal>REALTIME_DESTROY(family, fieldmatch)=ignored</literal>)</para>
+			</note>
 		</description>
 	</function>
 	<function name="REALTIME_FIELD" language="en_US">
@@ -185,6 +191,13 @@ static int function_realtime_read(struct ast_channel *chan, const char *cmd, cha
 	/* add space for delimiters and final '\0' */
 	resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
 
+	if (resultslen > len) {
+		ast_log(LOG_WARNING, "Failed to fetch. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
+		return -1;
+	}
+
+	/* len is going to be sensible, so we don't need to check for stack
+	 * overflows here. */
 	out = ast_str_alloca(resultslen);
 	for (var = head; var; var = var->next)
 		ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
@@ -391,18 +404,32 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
 		return -1;
 	}
 
-	resultslen = 0;
-	n = 0;
-	for (var = head; var; n++, var = var->next)
-		resultslen += strlen(var->name) + strlen(var->value);
-	/* add space for delimiters and final '\0' */
-	resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
+	if (len > 0) {
+		resultslen = 0;
+		n = 0;
+		for (var = head; var; n++, var = var->next) {
+			resultslen += strlen(var->name) + strlen(var->value);
+		}
+		/* add space for delimiters and final '\0' */
+		resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
+
+		if (resultslen > len) {
+			/* Unfortunately this does mean that we cannot destroy
+			 * the row anymore. But OTOH, we're not destroying
+			 * someones data without giving him the chance to look
+			 * at it. */
+			ast_log(LOG_WARNING, "Failed to fetch/destroy. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
+			return -1;
+		}
 
-	out = ast_str_alloca(resultslen);
-	for (var = head; var; var = var->next) {
-		ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
+		/* len is going to be sensible, so we don't need to check for
+		 * stack overflows here. */
+		out = ast_str_alloca(resultslen);
+		for (var = head; var; var = var->next) {
+			ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
+		}
+		ast_copy_string(buf, ast_str_buffer(out), len);
 	}
-	ast_copy_string(buf, ast_str_buffer(out), len);
 
 	ast_destroy_realtime(args.family, args.fieldmatch, args.value, SENTINEL);
 	ast_variables_destroy(head);
@@ -413,6 +440,15 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
 	return 0;
 }
 
+/*!
+ * \brief Wrapper to execute REALTIME_DESTROY from a write operation. Allows
+ * execution even if live_dangerously is disabled.
+ */
+static int function_realtime_writedestroy(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+	return function_realtime_readdestroy(chan, cmd, data, NULL, 0);
+}
+
 struct ast_custom_function realtime_function = {
 	.name = "REALTIME",
 	.read = function_realtime_read,
@@ -438,6 +474,7 @@ struct ast_custom_function realtime_store_function = {
 struct ast_custom_function realtime_destroy_function = {
 	.name = "REALTIME_DESTROY",
 	.read = function_realtime_readdestroy,
+	.write = function_realtime_writedestroy,
 };
 
 static int unload_module(void)
@@ -456,7 +493,7 @@ static int load_module(void)
 	int res = 0;
 	res |= ast_custom_function_register(&realtime_function);
 	res |= ast_custom_function_register(&realtime_store_function);
-	res |= ast_custom_function_register(&realtime_destroy_function);
+	res |= ast_custom_function_register_escalating(&realtime_destroy_function, AST_CFE_READ);
 	res |= ast_custom_function_register(&realtimefield_function);
 	res |= ast_custom_function_register(&realtimehash_function);
 	return res;
diff --git a/funcs/func_shell.c b/funcs/func_shell.c
index 888ba46..6c50f69 100644
--- a/funcs/func_shell.c
+++ b/funcs/func_shell.c
@@ -75,12 +75,17 @@ static int shell_helper(struct ast_channel *chan, const char *cmd, char *data,
 		</syntax>
 		<description>
 			<para>Returns the value from a system command</para>
-			<para>Example:  <literal>Set(foo=${SHELL(echo \bar\)})</literal></para>
-			<note><para>When using the SHELL() dialplan function, your \SHELL\ is /bin/sh,
-			which may differ as to the underlying shell, depending upon your production
-			platform.  Also keep in mind that if you are using a common path, you should
-			be mindful of race conditions that could result from two calls running
-			SHELL() simultaneously.</para></note>
+			<para>Example:  <literal>Set(foo=${SHELL(echo bar)})</literal></para>
+			<note>
+				<para>The command supplied to this function will be executed by the
+				system's shell, typically specified in the SHELL environment variable. There
+				are many different system shells available with somewhat different behaviors,
+				so the output generated by this function may vary between platforms.</para>
+
+				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+				is set to <literal>no</literal>, this function can only be executed from the
+				dialplan, and not directly from external protocols.</para>
+			</note>
 		</description>
  
 	</function>
@@ -97,7 +102,7 @@ static int unload_module(void)
 
 static int load_module(void)
 {
-	return ast_custom_function_register(&shell_function);
+	return ast_custom_function_register_escalating(&shell_function, AST_CFE_READ);
 }
 
 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Returns the output of a shell command");
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index f502299..25767b1 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -569,6 +569,12 @@ enum {
 	 *  some non-traditional dialplans (like AGI) to continue to function.
 	 */
 	AST_FLAG_DISABLE_WORKAROUNDS = (1 << 20),
+	/*! Disable device state event caching.  This allows allows channel
+	 * drivers to selectively prevent device state events from being cached
+	 * by certain channels such as anonymous calls which have no persistent
+	 * represenatation that can be tracked.
+	 */
+	AST_FLAG_DISABLE_DEVSTATE_CACHE = (1 << 21),
 };
 
 /*! \brief ast_bridge_config flags */
diff --git a/include/asterisk/devicestate.h b/include/asterisk/devicestate.h
index 4c51687..72ec547 100644
--- a/include/asterisk/devicestate.h
+++ b/include/asterisk/devicestate.h
@@ -61,6 +61,14 @@ enum ast_device_state {
 	AST_DEVICE_TOTAL,        /*/ Total num of device states, used for testing */
 };
 
+/*! \brief Device State Cachability
+ *  \note This is used to define the cachability of a device state when set.
+ */
+enum ast_devstate_cache {
+	AST_DEVSTATE_NOT_CACHABLE,  /*!< This device state is not cachable */
+	AST_DEVSTATE_CACHABLE,      /*!< This device state is cachable */
+};
+
 /*! \brief Devicestate provider call back */
 typedef enum ast_device_state (*ast_devstate_prov_cb_type)(const char *data);
 
@@ -129,6 +137,7 @@ enum ast_device_state ast_device_state(const char *device);
  * \brief Tells Asterisk the State for Device is changed
  *
  * \param state the new state of the device
+ * \param cachable whether this device state is cachable
  * \param fmt device name like a dial string with format parameters
  *
  * The new state of the device will be sent off to any subscribers
@@ -138,13 +147,14 @@ enum ast_device_state ast_device_state(const char *device);
  * \retval 0 on success
  * \retval -1 on failure
  */
-int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...)
-	__attribute__((format(printf, 2, 3)));
+int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt, ...)
+	__attribute__((format(printf, 3, 4)));
 
 /*!
  * \brief Tells Asterisk the State for Device is changed
  *
  * \param state the new state of the device
+ * \param cachable whether this device state is cachable
  * \param device device name like a dial string with format parameters
  *
  * The new state of the device will be sent off to any subscribers
@@ -154,7 +164,7 @@ int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...)
  * \retval 0 on success
  * \retval -1 on failure
  */
-int ast_devstate_changed_literal(enum ast_device_state state, const char *device);
+int ast_devstate_changed_literal(enum ast_device_state state, enum ast_devstate_cache cachable, const char *device);
 
 /*!
  * \brief Tells Asterisk the State for Device is changed.
diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h
index 3f1e3bd..d3a8286 100644
--- a/include/asterisk/event_defs.h
+++ b/include/asterisk/event_defs.h
@@ -120,9 +120,15 @@ enum ast_event_ie_type {
 	  * This IE indicates which server the event originated from
 	  */
 	 AST_EVENT_IE_EID      = 0x0A,
+	/*!
+	 * \brief Event non-cachability flag
+	 * Used by: All events
+	 * Payload type: UINT
+	 */
+	AST_EVENT_IE_CACHABLE            = 0x0B,
 };
 
-#define AST_EVENT_IE_MAX AST_EVENT_IE_EID
+#define AST_EVENT_IE_MAX AST_EVENT_IE_CACHABLE
 
 /*!
  * \brief Payload types for event information elements
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index e35cb96..c0d2e42 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -1023,16 +1023,44 @@ struct ast_custom_function* ast_custom_function_find(const char *name);
 int ast_custom_function_unregister(struct ast_custom_function *acf);
 
 /*!
+ * \brief Description of the ways in which a function may escalate privileges.
+ */
+enum ast_custom_function_escalation {
+	AST_CFE_NONE,
+	AST_CFE_READ,
+	AST_CFE_WRITE,
+	AST_CFE_BOTH,
+};
+
+/*!
  * \brief Register a custom function
  */
 #define ast_custom_function_register(acf) __ast_custom_function_register(acf, ast_module_info->self)
 
 /*!
+ * \brief Register a custom function which requires escalated privileges.
+ *
+ * Examples would be SHELL() (for which a read needs permission to execute
+ * arbitrary code) or FILE() (for which write needs permission to change files
+ * on the filesystem).
+ */
+#define ast_custom_function_register_escalating(acf, escalation) __ast_custom_function_register_escalating(acf, escalation, ast_module_info->self)
+
+/*!
  * \brief Register a custom function
  */
 int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod);
 
 /*! 
+ * \brief Register a custom function which requires escalated privileges.
+ *
+ * Examples would be SHELL() (for which a read needs permission to execute
+ * arbitrary code) or FILE() (for which write needs permission to change files
+ * on the filesystem).
+ */
+int __ast_custom_function_register_escalating(struct ast_custom_function *acf, enum ast_custom_function_escalation escalation, struct ast_module *mod);
+
+/*!
  * \brief Retrieve the number of active calls
  */
 int ast_active_calls(void);
@@ -1129,6 +1157,32 @@ int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b);
 unsigned int ast_hashtab_hash_contexts(const void *obj);
 /*! @} */
 
+/*!
+ * \brief Enable/disable the execution of 'dangerous' functions from external
+ * protocols (AMI, etc.).
+ *
+ * These dialplan functions (such as \c SHELL) provide an opportunity for
+ * privilege escalation. They are okay to invoke from the dialplan, but external
+ * protocols with permission controls should not normally invoke them.
+ *
+ * This function can globally enable/disable the execution of dangerous
+ * functions from external protocols.
+ *
+ * \param new_live_dangerously If true, enable the execution of escalating
+ * functions from external protocols.
+ */
+void pbx_live_dangerously(int new_live_dangerously);
+
+/*!
+ * \brief Inhibit (in the current thread) the execution of dialplan functions
+ * which cause privilege escalations. If pbx_live_dangerously() has been
+ * called, this function has no effect.
+ *
+ * \return 0 if successfuly marked current thread.
+ * \return Non-zero if marking current thread failed.
+ */
+int ast_thread_inhibit_escalations(void);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
diff --git a/main/Makefile b/main/Makefile
index 24337cf..d9efdcd 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -156,13 +156,6 @@ AST_EMBED_LDFLAGS:=$(foreach dep,$(EMBED_LDFLAGS),$(value $(dep)))
 AST_EMBED_LIBS:=$(foreach dep,$(EMBED_LIBS),$(value $(dep)))
 OBJS:=$(sort $(OBJS))
 
-ifneq ($(wildcard ../channels/h323/Makefile.ast),)
-  include ../channels/h323/Makefile.ast
-else
-  H323LDFLAGS=
-  H323LDLIBS=
-endif
-
 ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
 MAIN_TGT:=asterisk.dll
 asterisk: cygload
@@ -180,11 +173,7 @@ endif
 $(MAIN_TGT): $(OBJS) editline/libedit.a db1-ast/libdb1.a $(AST_EMBED_LDSCRIPTS)
 	@$(CC) -c -o buildinfo.o $(_ASTCFLAGS) buildinfo.c $(ASTCFLAGS)
 	$(ECHO_PREFIX) echo "   [LD] $^ -> $@"
-ifneq ($(findstring chan_h323,$(MENUSELECT_CHANNELS)),)
 	$(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(_ASTLDFLAGS) $(ASTLDFLAGS) $^ buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(GMIMELDFLAGS)
-else
-	$(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(_ASTLDFLAGS) $(ASTLDFLAGS) $(H323LDFLAGS) $^ buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(H323LDLIBS) $(GMIMELDFLAGS)
-endif
 	$(CMD_PREFIX) $(ASTTOPDIR)/build_tools/strip_nonapi $@ || rm $@
 
 clean::
diff --git a/main/asterisk.c b/main/asterisk.c
index ebc057b..5b9947d 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -483,6 +483,13 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
 	ast_cli(a->fd, "  Module directory:            %s\n", ast_config_AST_MODULE_DIR);
 	ast_cli(a->fd, "  Spool directory:             %s\n", ast_config_AST_SPOOL_DIR);
 	ast_cli(a->fd, "  Log directory:               %s\n", ast_config_AST_LOG_DIR);
+	ast_cli(a->fd, "  Run/Sockets directory:       %s\n", ast_config_AST_RUN_DIR);
+	ast_cli(a->fd, "  PID file:                    %s\n", ast_config_AST_PID);
+	ast_cli(a->fd, "  VarLib directory:            %s\n", ast_config_AST_VAR_DIR);
+	ast_cli(a->fd, "  Data directory:              %s\n", ast_config_AST_DATA_DIR);
+	ast_cli(a->fd, "  ASTDB:                       %s\n", ast_config_AST_DB);
+	ast_cli(a->fd, "  IAX2 Keys directory:         %s\n", ast_config_AST_KEY_DIR);
+	ast_cli(a->fd, "  AGI Scripts directory:       %s\n", ast_config_AST_AGI_DIR);
 	ast_cli(a->fd, "\n\n");
 	return CLI_SUCCESS;
 }
@@ -2821,6 +2828,8 @@ static void ast_readconfig(void)
 		unsigned int dbdir:1;
 		unsigned int keydir:1;
 	} found = { 0, 0 };
+	/* Default to true for backward compatibility */
+	int live_dangerously = 1;
 
 	if (ast_opt_override_config) {
 		cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
@@ -3026,8 +3035,11 @@ static void ast_readconfig(void)
 			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIDE_CONSOLE_CONNECT);
 		} else if (!strcasecmp(v->name, "sendfullybooted")) {
 			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_SEND_FULLYBOOTED);
+		} else if (!strcasecmp(v->name, "live_dangerously")) {
+			live_dangerously = ast_true(v->value);
 		}
 	}
+	pbx_live_dangerously(live_dangerously);
 	for (v = ast_variable_browse(cfg, "compat"); v; v = v->next) {
 		float version;
 		if (sscanf(v->value, "%30f", &version) != 1) {
diff --git a/main/bridging.c b/main/bridging.c
index c6b92c1..d71e7bb 100644
--- a/main/bridging.c
+++ b/main/bridging.c
@@ -626,11 +626,16 @@ static int smart_bridge_operation(struct ast_bridge *bridge, struct ast_bridge_c
 		if (new_technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD) {
 			ast_debug(1, "Telling current bridge thread for bridge %p to refresh\n", bridge);
 			bridge->refresh = 1;
+			bridge_poke(bridge);
 		} else {
+			pthread_t bridge_thread = bridge->thread;
 			ast_debug(1, "Telling current bridge thread for bridge %p to stop\n", bridge);
 			bridge->stop = 1;
+			bridge_poke(bridge);
+			ao2_unlock(bridge);
+			pthread_join(bridge_thread, NULL);
+			ao2_lock(bridge);
 		}
-		bridge_poke(bridge);
 	}
 
 	/* Since we are soon going to pass this bridge to a new technology we need to NULL out the bridge_pvt pointer but don't worry as it still exists in temp_bridge, ditto for the old technology */
diff --git a/main/channel.c b/main/channel.c
index e35f5d7..0490c96 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -1451,6 +1451,7 @@ void ast_channel_free(struct ast_channel *chan)
 #ifdef HAVE_EPOLL
 	int i;
 #endif
+	int flags;
 	struct ast_var_t *vardata;
 	struct ast_frame *f;
 	struct varshead *headp;
@@ -1493,6 +1494,7 @@ void ast_channel_free(struct ast_channel *chan)
 		sched_context_destroy(chan->sched);
 
 	ast_copy_string(name, chan->name, sizeof(name));
+	flags = chan->flags;
 	if ((dashptr = strrchr(name, '-'))) {
 		*dashptr = '\0';
 	}
@@ -1561,7 +1563,7 @@ void ast_channel_free(struct ast_channel *chan)
 	/* Queue an unknown state, because, while we know that this particular
 	 * instance is dead, we don't know the state of all other possible
 	 * instances. */
-	ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, name);
+	ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, (flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), name);
 }
 
 struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, const char *uid)
@@ -4877,7 +4879,7 @@ int ast_setstate(struct ast_channel *chan, enum ast_channel_state state)
 	/* We have to pass AST_DEVICE_UNKNOWN here because it is entirely possible that the channel driver
 	 * for this channel is using the callback method for device state. If we pass in an actual state here
 	 * we override what they are saying the state is and things go amuck. */
-	ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, name);
+	ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, (chan->flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), name);
 
 	/* setstate used to conditionally report Newchannel; this is no more */
 	manager_event(EVENT_FLAG_CALL,
diff --git a/main/config.c b/main/config.c
index d1ea6ea..0642eca 100644
--- a/main/config.c
+++ b/main/config.c
@@ -1023,12 +1023,13 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 		if (*c) {
 			*c = '\0';
 			/* Find real argument */
-			c = ast_skip_blanks(c + 1);
+			c = ast_strip(c + 1);
 			if (!(*c)) {
 				c = NULL;
 			}
-		} else 
+		} else {
 			c = NULL;
+		}
 		if (!strcasecmp(cur, "include")) {
 			do_include = 1;
 		} else if (!strcasecmp(cur, "exec")) {
@@ -1052,20 +1053,12 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 
 		cur = c;
 		/* Strip off leading and trailing "'s and <>'s */
-		if (*c == '"') {
-			/* Dequote */
-			while (*c) {
-				if (*c == '"') {
-					strcpy(c, c + 1); /* SAFE */
-					c--;
-				} else if (*c == '\\') {
-					strcpy(c, c + 1); /* SAFE */
-				}
-				c++;
-			}
-		} else if (*c == '<') {
-			/* C-style include */
-			if (*(c + strlen(c) - 1) == '>') {
+               if ((*c == '"') || (*c == '<')) {
+                       char quote_char = *c;
+                       if (quote_char == '<')
+                               quote_char = '>';
+
+                       if (*(c + strlen(c) - 1) == quote_char) {
 				cur++;
 				*(c + strlen(c) - 1) = '\0';
 			}
@@ -1332,6 +1325,17 @@ static struct ast_config *config_text_file_load(const char *database, const char
 		while (!feof(f)) {
 			lineno++;
 			if (fgets(buf, sizeof(buf), f)) {
+				/* Skip lines that are too long */
+				if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
+					ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
+					while (fgets(buf, sizeof(buf), f)) {
+						if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
+							break;
+						}
+					}
+					continue;
+				}
+
 				if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
 					CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
 					ast_str_reset(lline_buffer);        /* erase the lline buffer */
diff --git a/main/devicestate.c b/main/devicestate.c
index 5074507..83bc183 100644
--- a/main/devicestate.c
+++ b/main/devicestate.c
@@ -170,6 +170,7 @@ static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov);
 
 struct state_change {
 	AST_LIST_ENTRY(state_change) list;
+	enum ast_devstate_cache cachable;
 	char device[1];
 };
 
@@ -187,6 +188,7 @@ struct devstate_change {
 	AST_LIST_ENTRY(devstate_change) entry;
 	uint32_t state;
 	struct ast_eid eid;
+	enum ast_devstate_cache cachable;
 	char device[1];
 };
 
@@ -426,7 +428,7 @@ static int getproviderstate(const char *provider, const char *address)
 	return res;
 }
 
-static void devstate_event(const char *device, enum ast_device_state state)
+static void devstate_event(const char *device, enum ast_device_state state, int cachable)
 {
 	struct ast_event *event;
 	enum ast_event_type event_type;
@@ -442,18 +444,23 @@ static void devstate_event(const char *device, enum ast_device_state state)
 	ast_debug(3, "device '%s' state '%d'\n", device, state);
 
 	if (!(event = ast_event_new(event_type,
-			AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
-			AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
-			AST_EVENT_IE_END))) {
+				    AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
+				    AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
+				    AST_EVENT_IE_CACHABLE, AST_EVENT_IE_PLTYPE_UINT, cachable,
+				    AST_EVENT_IE_END))) {
 		return;
 	}
 
-	ast_event_queue_and_cache(event);
+	if (cachable) {
+		ast_event_queue_and_cache(event);
+	} else {
+		ast_event_queue(event);
+	}
 }
 
 /*! Called by the state change thread to find out what the state is, and then
  *  to queue up the state change event */
-static void do_state_change(const char *device)
+static void do_state_change(const char *device, int cachable)
 {
 	enum ast_device_state state;
 
@@ -461,10 +468,10 @@ static void do_state_change(const char *device)
 
 	ast_debug(3, "Changing state for %s - state %d (%s)\n", device, state, ast_devstate2str(state));
 
-	devstate_event(device, state);
+	devstate_event(device, state, cachable);
 }
 
-int ast_devstate_changed_literal(enum ast_device_state state, const char *device)
+int ast_devstate_changed_literal(enum ast_device_state state, enum ast_devstate_cache cachable, const char *device)
 {
 	struct state_change *change;
 
@@ -485,14 +492,15 @@ int ast_devstate_changed_literal(enum ast_device_state state, const char *device
 	 */
 
 	if (state != AST_DEVICE_UNKNOWN) {
-		devstate_event(device, state);
+		devstate_event(device, state, cachable);
 	} else if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
 		/* we could not allocate a change struct, or */
 		/* there is no background thread, so process the change now */
-		do_state_change(device);
+		do_state_change(device, cachable);
 	} else {
 		/* queue the change */
 		strcpy(change->device, device);
+		change->cachable = cachable;
 		AST_LIST_LOCK(&state_changes);
 		AST_LIST_INSERT_TAIL(&state_changes, change, list);
 		ast_cond_signal(&change_pending);
@@ -504,10 +512,10 @@ int ast_devstate_changed_literal(enum ast_device_state state, const char *device
 
 int ast_device_state_changed_literal(const char *dev)
 {
-	return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, dev);
+	return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, dev);
 }
 
-int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...) 
+int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt, ...)
 {
 	char buf[AST_MAX_EXTENSION];
 	va_list ap;
@@ -516,7 +524,7 @@ int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...)
 	vsnprintf(buf, sizeof(buf), fmt, ap);
 	va_end(ap);
 
-	return ast_devstate_changed_literal(state, buf);
+	return ast_devstate_changed_literal(state, cachable, buf);
 }
 
 int ast_device_state_changed(const char *fmt, ...) 
@@ -528,7 +536,7 @@ int ast_device_state_changed(const char *fmt, ...)
 	vsnprintf(buf, sizeof(buf), fmt, ap);
 	va_end(ap);
 
-	return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf);
+	return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, buf);
 }
 
 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
@@ -548,7 +556,7 @@ static void *do_devstate_changes(void *data)
 		/* Process each state change */
 		while ((current = next)) {
 			next = AST_LIST_NEXT(current, list);
-			do_state_change(current->device);
+			do_state_change(current->device, current->cachable);
 			ast_free(current);
 		}
 	}
@@ -592,7 +600,7 @@ static void devstate_cache_cb(const struct ast_event *event, void *data)
 	collection->num_states++;
 }
 
-static void process_collection(const char *device, struct change_collection *collection)
+static void process_collection(const char *device, enum ast_devstate_cache cachable, struct change_collection *collection)
 {
 	int i;
 	struct ast_devstate_aggregate agg;
@@ -642,7 +650,11 @@ static void process_collection(const char *device, struct change_collection *col
 		return;
 	}
 
-	ast_event_queue_and_cache(event);
+	if (cachable) {
+		ast_event_queue_and_cache(event);
+	} else {
+		ast_event_queue(event);
+	}
 }
 
 static void handle_devstate_change(struct devstate_change *sc)
@@ -668,7 +680,7 @@ static void handle_devstate_change(struct devstate_change *sc)
 	/* Populate the collection of device states from the cache */
 	ast_event_dump_cache(tmp_sub);
 
-	process_collection(sc->device, &collection);
+	process_collection(sc->device, sc->cachable, &collection);
 
 	ast_event_sub_destroy(tmp_sub);
 }
@@ -697,10 +709,12 @@ static void devstate_change_collector_cb(const struct ast_event *event, void *da
 	const char *device;
 	const struct ast_eid *eid;
 	uint32_t state;
+	enum ast_devstate_cache cachable = AST_DEVSTATE_CACHABLE;
 
 	device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
 	eid = ast_event_get_ie_raw(event, AST_EVENT_IE_EID);
 	state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+	cachable = ast_event_get_ie_uint(event, AST_EVENT_IE_CACHABLE);
 
 	if (ast_strlen_zero(device) || !eid) {
 		ast_log(LOG_ERROR, "Invalid device state change event received\n");
@@ -713,6 +727,7 @@ static void devstate_change_collector_cb(const struct ast_event *event, void *da
 	strcpy(sc->device, device);
 	sc->eid = *eid;
 	sc->state = state;
+	sc->cachable = cachable;
 
 	ast_mutex_lock(&devstate_collector.lock);
 	AST_LIST_INSERT_TAIL(&devstate_collector.devstate_change_q, sc, entry);
diff --git a/main/event.c b/main/event.c
index 55256e1..fd02c23 100644
--- a/main/event.c
+++ b/main/event.c
@@ -216,6 +216,7 @@ static struct ie_map {
 	{ AST_EVENT_IE_STATE,     AST_EVENT_IE_PLTYPE_UINT, "State" },
 	{ AST_EVENT_IE_CONTEXT,   AST_EVENT_IE_PLTYPE_STR,  "Context" },
 	{ AST_EVENT_IE_EID,       AST_EVENT_IE_PLTYPE_RAW,  "EntityID" },
+	{ AST_EVENT_IE_CACHABLE,  AST_EVENT_IE_PLTYPE_UINT, "Cachable" },
 };
 
 const char *ast_event_get_type_name(const struct ast_event *event)
diff --git a/main/features.c b/main/features.c
index dd92b1b..7f0280c 100644
--- a/main/features.c
+++ b/main/features.c
@@ -513,7 +513,7 @@ static void notify_metermaids(const char *exten, char *context, enum ast_device_
 	ast_debug(4, "Notification of state change to metermaids %s@%s\n to state '%s'", 
 		exten, context, ast_devstate2str(state));
 
-	ast_devstate_changed(state, "park:%s@%s", exten, context);
+	ast_devstate_changed(state, AST_DEVSTATE_CACHABLE, "park:%s@%s", exten, context);
 }
 
 /*! \brief metermaids callback from devicestate.c */
@@ -2703,10 +2703,19 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
 				break;
 			case AST_CONTROL_OPTION:
 				aoh = f->data.ptr;
-				/* Forward option Requests */
+				/* Forward option Requests, but only ones we know are safe
+				 * These are ONLY sent by chan_iax2 and I'm not convinced that
+				 * they are useful. I haven't deleted them entirely because I
+				 * just am not sure of the ramifications of removing them. */
 				if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
-					ast_channel_setoption(other, ntohs(aoh->option), aoh->data, 
-						f->datalen - sizeof(struct ast_option_header), 0);
+				   	switch (ntohs(aoh->option)) {
+					case AST_OPTION_TONE_VERIFY:
+					case AST_OPTION_TDD:
+					case AST_OPTION_RELAXDTMF:
+					case AST_OPTION_AUDIO_MODE:
+						ast_channel_setoption(other, ntohs(aoh->option), aoh->data, 
+							f->datalen - sizeof(struct ast_option_header), 0);
+					}
 				}
 				break;
 			}
diff --git a/main/http.c b/main/http.c
index d992896..c80c901 100644
--- a/main/http.c
+++ b/main/http.c
@@ -53,12 +53,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211580 $")
 #include "asterisk/astobj2.h"
 
 #define MAX_PREFIX 80
+#define DEFAULT_SESSION_LIMIT 100
+#define MAX_HEAD_SIZE 8192
 
 /* See http.h for more information about the SSL implementation */
 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
 #define	DO_SSL	/* comment in/out if you want to support ssl */
 #endif
 
+static int session_limit = DEFAULT_SESSION_LIMIT;
+static int session_count = 0;
+
 static struct ast_tls_config http_tls_cfg;
 
 static void *httpd_helper_thread(void *arg);
@@ -660,6 +665,7 @@ static void *httpd_helper_thread(void *data)
 {
 	char buf[4096];
 	char cookie[4096];
+	int header_len = 0;
 	struct ast_tcptls_session_instance *ser = data;
 	struct ast_variable *vars=NULL, *headers = NULL;
 	char *uri, *title=NULL;
@@ -668,6 +674,10 @@ static void *httpd_helper_thread(void *data)
 	unsigned int static_content = 0;
 	struct ast_variable *tail = headers;
 
+	if (ast_atomic_fetchadd_int(&session_count, +1) >= session_limit) {
+		goto done;
+	}
+
 	if (!fgets(buf, sizeof(buf), ser->f)) {
 		goto done;
 	}
@@ -688,12 +698,15 @@ static void *httpd_helper_thread(void *data)
 	}
 
 	/* process "Cookie: " lines */
-	while (fgets(cookie, sizeof(cookie), ser->f)) {
+	while (header_len <= MAX_HEAD_SIZE && fgets(cookie, sizeof(cookie), ser->f)) {
+		int len;
 		/* Trim trailing characters */
 		ast_trim_blanks(cookie);
-		if (ast_strlen_zero(cookie)) {
+		len = strlen(cookie); /* FIXME: not real length. Blanks removed */
+		if (len == 0) {
 			break;
 		}
+		header_len += len;
 		if (!strncasecmp(cookie, "Cookie: ", 8)) {
 			vars = parse_cookies(cookie);
 		} else {
@@ -778,6 +791,7 @@ static void *httpd_helper_thread(void *data)
 	}
 
 done:
+	ast_atomic_fetchadd_int(&session_count, -1);
 	fclose(ser->f);
 	ao2_ref(ser, -1);
 	ser = NULL;
@@ -927,6 +941,12 @@ static int __ast_http_load(int reload)
 				}
 			} else if (!strcasecmp(v->name, "redirect")) {
 				add_redirect(v->value);
+			} else if (!strcasecmp(v->name, "sessionlimit")) {
+				if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
+							&session_limit, DEFAULT_SESSION_LIMIT, 1, INT_MAX)) {
+					ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
+							v->name, v->value, v->lineno);
+				}
 			} else {
 				ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
 			}
diff --git a/main/loader.c b/main/loader.c
index d2412e0..441f38d 100644
--- a/main/loader.c
+++ b/main/loader.c
@@ -383,6 +383,12 @@ static struct ast_module *load_dynamic_module(const char *resource_in, unsigned
 	if (missing_so)
 		strcat(resource_being_loaded->resource, ".so");
 
+	/* libopenh323 is buggy and segfaults on dlclose() when opened with
+	 * RTLD_LAZY. Workaround this until it gets fixed */
+	if (!strcasecmp(resource_being_loaded->resource, "chan_h323.so") ||
+	    !strcasecmp(resource_being_loaded->resource, "chan_oh323.so"))
+		lib = dlopen(fn, RTLD_NOW | RTLD_LOCAL);
+
 	if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_LOCAL))) {
 		ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
 		ast_free(resource_being_loaded);
diff --git a/main/manager.c b/main/manager.c
index 43c8a9a..7d22638 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -119,16 +119,29 @@ struct eventqent {
 
 static AST_LIST_HEAD_STATIC(all_events, eventqent);
 
-static int displayconnects = 1;
+static const int DEFAULT_ENABLED			= 0;	/*!< Default setting for manager to be enabled */
+static const int DEFAULT_WEBENABLED			= 0;	/*!< Default setting for the web interface to be enabled */
+static const int DEFAULT_BLOCKSOCKETS		= 0;	/*!< Default setting for block-sockets */
+static const int DEFAULT_DISPLAYCONNECTS	= 1;	/*!< Default setting for displaying manager connections */
+static const int DEFAULT_TIMESTAMPEVENTS	= 0;	/*!< Default setting for timestampevents */	
+static const int DEFAULT_HTTPTIMEOUT 		= 60;	/*!< Default manager http timeout */
+static const int DEFAULT_BROKENEVENTSACTION	= 0;	/*!< Default setting for brokeneventsaction */
+static const int DEFAULT_AUTHTIMEOUT		= 30;	/*!< Default setting for authtimeout */
+static const int DEFAULT_AUTHLIMIT		= 50;	/*!< Default setting for authlimit */
+
+static int displayconnects;
 static int allowmultiplelogin = 1;
 static int timestampevents;
-static int httptimeout = 60;
-static int broken_events_action = 0;
+static int httptimeout;
+static int broken_events_action;
 static int manager_enabled = 0;
 static int webmanager_enabled = 0;
+static int authtimeout;
+static int authlimit;
 
 static int block_sockets;
 static int num_sessions;
+static int unauth_sessions = 0;
 
 static int manager_debug;	/*!< enable some debugging code in the manager */
 
@@ -206,6 +219,7 @@ struct mansession_session {
 	int send_events;	/*!<  XXX what ? */
 	struct eventqent *last_ev;	/*!< last event processed. */
 	int writetimeout;	/*!< Timeout for ast_carefulwrite() */
+	time_t authstart;
 	int pending_event;         /*!< Pending events indicator in case when waiting_thread is NULL */
 	AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
 	AST_LIST_ENTRY(mansession_session) list;
@@ -220,6 +234,7 @@ struct mansession {
 	struct mansession_session *session;
 	FILE *f;
 	int fd;
+	int write_error:1;
 };
 
 #define NEW_EVENT(m)	(AST_LIST_NEXT(m->session->last_ev, eq_next))
@@ -386,6 +401,19 @@ static struct permalias {
 	{ 0, "none" },
 };
 
+/*! \brief Checks to see if a string which can be used to evaluate functions should be rejected */
+static int check_user_can_execute_function(const char *evaluating, int writepermlist)
+{
+	if (!(writepermlist & EVENT_FLAG_SYSTEM)
+		&& (
+			strstr(evaluating, "SHELL") ||       /* NoOp(${SHELL(rm -rf /)})  */
+			strstr(evaluating, "EVAL")           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
+		)) {
+		return 0;
+	}
+	return 1;
+}
+
 /*! \brief Convert authority code to a list of options */
 static char *authority_to_str(int authority, struct ast_str **res)
 {
@@ -944,11 +972,15 @@ struct ast_variable *astman_get_variables(const struct message *m)
  */
 static int send_string(struct mansession *s, char *string)
 {
-	if (s->f) {
-		return ast_careful_fwrite(s->f, s->fd, string, strlen(string), s->session->writetimeout);
-	} else {
-		return ast_careful_fwrite(s->session->f, s->session->fd, string, strlen(string), s->session->writetimeout);
+	FILE *f = s->f ? s->f : s->session->f;
+	int fd = s->f ? s->fd : s->session->fd;
+	int res;
+
+	if ((res = ast_careful_fwrite(f, fd, string, strlen(string), s->session->writetimeout))) {
+		s->write_error = 1;
 	}
+
+	return res;
 }
 
 /*!
@@ -1767,6 +1799,7 @@ static int action_login(struct mansession *s, const struct message *m)
 		return -1;
 	}
 	s->session->authenticated = 1;
+	ast_atomic_fetchadd_int(&unauth_sessions, -1);
 	if (manager_displayconnects(s->session))
 		ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
 	ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
@@ -1882,6 +1915,12 @@ static int action_getvar(struct mansession *s, const struct message *m)
 		return 0;
 	}
 
+	/* We don't want users with insufficient permissions using certain functions. */
+	if (!(check_user_can_execute_function(varname, s->session->writeperm))) {
+		astman_send_error(s, m, "GetVar Access Forbidden: Variable");
+		return 0;
+	}
+
 	if (!ast_strlen_zero(name)) {
 		c = ast_get_channel_by_name_locked(name);
 		if (!c) {
@@ -1949,6 +1988,11 @@ static int action_status(struct mansession *s, const struct message *m)
 	else
 		idText[0] = '\0';
 
+	if (!(check_user_can_execute_function(variables, s->session->writeperm))) {
+		astman_send_error(s, m, "Status Access Forbidden: Variables");
+		return 0;
+	}
+
 	if (all)
 		c = ast_channel_walk_locked(NULL);
 	else {
@@ -2477,6 +2521,30 @@ static int action_originate(struct mansession *s, const struct message *m)
 		format = 0;
 		ast_parse_allow_disallow(NULL, &format, codecs, 1);
 	}
+	if (!ast_strlen_zero(app)) {
+		int bad_appdata = 0;
+		/* To run the System application (or anything else that goes to
+		 * shell), you must have the additional System privilege */
+		if (!(s->session->writeperm & EVENT_FLAG_SYSTEM)
+			&& (
+				strcasestr(app, "system") ||      /* System(rm -rf /)
+				                                     TrySystem(rm -rf /)       */
+				strcasestr(app, "exec") ||        /* Exec(System(rm -rf /))
+				                                     TryExec(System(rm -rf /)) */
+				strcasestr(app, "agi") ||         /* AGI(/bin/rm,-rf /)
+				                                     EAGI(/bin/rm,-rf /)       */
+				strcasestr(app, "mixmonitor") ||  /* MixMonitor(blah,,rm -rf)  */
+				strcasestr(app, "externalivr") || /* ExternalIVR(rm -rf)       */
+				(strstr(appdata, "SHELL") && (bad_appdata = 1)) ||       /* NoOp(${SHELL(rm -rf /)})  */
+				(strstr(appdata, "EVAL") && (bad_appdata = 1))           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
+				)) {
+			char error_buf[64];
+			snprintf(error_buf, sizeof(error_buf), "Originate Access Forbidden: %s", bad_appdata ? "Data" : "Application");
+			astman_send_error(s, m, error_buf);
+			return 0;
+		}
+	}
+
 	if (ast_true(async)) {
 		struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
 		if (!fast) {
@@ -2507,21 +2575,6 @@ static int action_originate(struct mansession *s, const struct message *m)
 			}
 		}
 	} else if (!ast_strlen_zero(app)) {
-		/* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
-		if (!(s->session->writeperm & EVENT_FLAG_SYSTEM)
-			&& (
-				strcasestr(app, "system") == 0 || /* System(rm -rf /)
-				                                     TrySystem(rm -rf /)       */
-				strcasestr(app, "exec") ||        /* Exec(System(rm -rf /))
-				                                     TryExec(System(rm -rf /)) */
-				strcasestr(app, "agi") ||         /* AGI(/bin/rm,-rf /)
-				                                     EAGI(/bin/rm,-rf /)       */
-				strstr(appdata, "SHELL") ||       /* NoOp(${SHELL(rm -rf /)})  */
-				strstr(appdata, "EVAL")           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
-				)) {
-			astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
-			return 0;
-		}
 		res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
 	} else {
 		if (exten && context && pi)
@@ -3094,6 +3147,8 @@ static int get_input(struct mansession *s, char *output)
 	int res, x;
 	int maxlen = sizeof(s->session->inbuf) - 1;
 	char *src = s->session->inbuf;
+	int timeout = -1;
+	time_t now;
 
 	/*
 	 * Look for \r\n within the buffer. If found, copy to the output
@@ -3121,6 +3176,20 @@ static int get_input(struct mansession *s, char *output)
 	}
 	res = 0;
 	while (res == 0) {
+		/* calculate a timeout if we are not authenticated */
+		if (!s->session->authenticated) {
+			if(time(&now) == -1) {
+				ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
+				return -1;
+			}
+
+			timeout = (authtimeout - (now - s->session->authstart)) * 1000;
+			if (timeout < 0) {
+				/* we have timed out */
+				return 0;
+			}
+		}
+
 		/* XXX do we really need this locking ? */
 		ast_mutex_lock(&s->session->__lock);
 		if (s->session->pending_event) {
@@ -3131,7 +3200,7 @@ static int get_input(struct mansession *s, char *output)
 		s->session->waiting_thread = pthread_self();
 		ast_mutex_unlock(&s->session->__lock);
 
-		res = ast_wait_for_input(s->session->fd, -1);	/* return 0 on timeout ? */
+		res = ast_wait_for_input(s->session->fd, timeout);
 
 		ast_mutex_lock(&s->session->__lock);
 		s->session->waiting_thread = AST_PTHREADT_NULL;
@@ -3164,6 +3233,7 @@ static int do_message(struct mansession *s)
 	struct message m = { 0 };
 	char header_buf[sizeof(s->session->inbuf)] = { '\0' };
 	int res;
+	time_t now;
 
 	for (;;) {
 		/* Check if any events are pending and do them if needed */
@@ -3171,6 +3241,17 @@ static int do_message(struct mansession *s)
 			return -1;
 		res = get_input(s, header_buf);
 		if (res == 0) {
+			if (!s->session->authenticated) {
+				if(time(&now) == -1) {
+					ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
+					return -1;
+				}
+
+				if (now - s->session->authstart > authtimeout) {
+					ast_log(LOG_EVENT, "Client from %s, failed to authenticate in %d seconds\n", ast_inet_ntoa(s->session->sin.sin_addr), authtimeout);
+					return -1;
+				}
+			}
 			continue;
 		} else if (res > 0) {
 			if (ast_strlen_zero(header_buf))
@@ -3194,13 +3275,22 @@ static int do_message(struct mansession *s)
 static void *session_do(void *data)
 {
 	struct ast_tcptls_session_instance *ser = data;
-	struct mansession_session *session = ast_calloc(1, sizeof(*session));
+	struct mansession_session *session = NULL;
 	struct mansession s = {.session = NULL, };
 	int flags;
 	int res;
 
-	if (session == NULL)
+	if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= authlimit) {
+		fclose(ser->f);
+		ast_atomic_fetchadd_int(&unauth_sessions, -1);
 		goto done;
+	}
+
+	if ((session = ast_calloc(1, sizeof(*session))) == NULL) {
+		fclose(ser->f);
+		ast_atomic_fetchadd_int(&unauth_sessions, -1);
+		goto done;
+	}
 
 	session->writetimeout = 100;
 	session->waiting_thread = AST_PTHREADT_NULL;
@@ -3230,9 +3320,16 @@ static void *session_do(void *data)
 	ast_atomic_fetchadd_int(&num_sessions, 1);
 	AST_LIST_UNLOCK(&sessions);
 
+	if(time(&session->authstart) == -1) {
+		ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
+		ast_atomic_fetchadd_int(&unauth_sessions, -1);
+		destroy_session(session);
+		goto done;
+	}
+
 	astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);	/* welcome prompt */
 	for (;;) {
-		if ((res = do_message(&s)) < 0)
+		if ((res = do_message(&s)) < 0 || s.write_error)
 			break;
 	}
 	/* session is over, explain why and terminate */
@@ -3241,6 +3338,7 @@ static void *session_do(void *data)
 			ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
 		ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
 	} else {
+		ast_atomic_fetchadd_int(&unauth_sessions, -1);
 			if (displayconnects)
 			ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
 		ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
@@ -4088,16 +4186,14 @@ static int __init_manager(int reload)
 	struct ast_config *ucfg = NULL, *cfg = NULL;
 	const char *val;
 	char *cat = NULL;
-	int newhttptimeout = 60;
+	int newhttptimeout = DEFAULT_HTTPTIMEOUT;
 	int have_sslbindaddr = 0;
 	struct hostent *hp;
 	struct ast_hostent ahp;
 	struct ast_manager_user *user = NULL;
 	struct ast_variable *var;
 	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
-
-	manager_enabled = 0;
-
+	
 	if (!registered) {
 		/* Register default actions */
 		ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
@@ -4142,8 +4238,16 @@ static int __init_manager(int reload)
 	if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
 		return 0;
 
-	displayconnects = 1;
-	broken_events_action = 0;
+	manager_enabled = DEFAULT_ENABLED;
+	webmanager_enabled = DEFAULT_WEBENABLED;
+	displayconnects = DEFAULT_DISPLAYCONNECTS;
+	broken_events_action = DEFAULT_BROKENEVENTSACTION;
+	block_sockets = DEFAULT_BLOCKSOCKETS;
+	timestampevents = DEFAULT_TIMESTAMPEVENTS;
+	httptimeout = DEFAULT_HTTPTIMEOUT;
+	authtimeout = DEFAULT_AUTHTIMEOUT;
+	authlimit = DEFAULT_AUTHLIMIT;
+
 	if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
 		ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid. Asterisk management interface (AMI) disabled.\n");
 		return 0;
@@ -4207,6 +4311,22 @@ static int __init_manager(int reload)
 			manager_debug = ast_true(val);
 		} else if (!strcasecmp(var->name, "httptimeout")) {
 			newhttptimeout = atoi(val);
+		} else if (!strcasecmp(var->name, "authtimeout")) {
+			int timeout = atoi(var->value);
+
+			if (timeout < 1) {
+				ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", var->value);
+			} else {
+				authtimeout = timeout;
+			}
+		} else if (!strcasecmp(var->name, "authlimit")) {
+			int limit = atoi(var->value);
+
+			if (limit < 1) {
+				ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", var->value);
+			} else {
+				authlimit = limit;
+			}
 		} else {
 			ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
 				var->name, val);
diff --git a/main/pbx.c b/main/pbx.c
index dedb656..61a9098 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -759,6 +759,17 @@ static struct ast_taskprocessor *device_state_tps;
 
 AST_THREADSTORAGE(switch_data);
 AST_THREADSTORAGE(extensionstate_buf);
+/*!
+ * \brief A thread local indicating whether the current thread can run
+ * 'dangerous' dialplan functions.
+ */
+AST_THREADSTORAGE(thread_inhibit_escalations_tl);
+
+/*!
+ * \brief Set to true (non-zero) to globally allow all dangerous dialplan
+ * functions to run.
+ */
+static int live_dangerously;
 
 /*!
    \brief ast_exten: An extension
@@ -1079,6 +1090,19 @@ static int totalcalls;
 
 static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
 
+/*!
+ * \brief Extra information for an \ref ast_custom_function holding privilege
+ * escalation information. Kept in a separate structure for ABI compatibility.
+ */
+struct ast_custom_escalating_function {
+	AST_RWLIST_ENTRY(ast_custom_escalating_function) list;
+	const struct ast_custom_function *acf;
+	unsigned int read_escalates:1;
+	unsigned int write_escalates:1;
+};
+
+static AST_RWLIST_HEAD_STATIC(escalation_root, ast_custom_escalating_function);
+
 /*! \brief Declaration of builtin applications */
 static struct pbx_builtin {
 	char name[AST_MAX_APP];
@@ -3216,6 +3240,7 @@ struct ast_custom_function *ast_custom_function_find(const char *name)
 int ast_custom_function_unregister(struct ast_custom_function *acf)
 {
 	struct ast_custom_function *cur;
+	struct ast_custom_escalating_function *cur_escalation;
 
 	if (!acf) {
 		return -1;
@@ -3230,9 +3255,64 @@ int ast_custom_function_unregister(struct ast_custom_function *acf)
 	}
 	AST_RWLIST_UNLOCK(&acf_root);
 
+	/* Remove from the escalation list */
+	AST_RWLIST_WRLOCK(&escalation_root);
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&escalation_root, cur_escalation, list) {
+		if (cur_escalation->acf == acf) {
+			AST_RWLIST_REMOVE_CURRENT(list);
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
+	AST_RWLIST_UNLOCK(&escalation_root);
+
 	return cur ? 0 : -1;
 }
 
+/*!
+ * \brief Returns true if given custom function escalates privileges on read.
+ *
+ * \param acf Custom function to query.
+ * \return True (non-zero) if reads escalate privileges.
+ * \return False (zero) if reads just read.
+ */
+static int read_escalates(const struct ast_custom_function *acf) {
+	int res = 0;
+	struct ast_custom_escalating_function *cur_escalation;
+
+	AST_RWLIST_RDLOCK(&escalation_root);
+	AST_RWLIST_TRAVERSE(&escalation_root, cur_escalation, list) {
+		if (cur_escalation->acf == acf) {
+			res = cur_escalation->read_escalates;
+			break;
+		}
+	}
+	AST_RWLIST_UNLOCK(&escalation_root);
+	return res;
+}
+
+/*!
+ * \brief Returns true if given custom function escalates privileges on write.
+ *
+ * \param acf Custom function to query.
+ * \return True (non-zero) if writes escalate privileges.
+ * \return False (zero) if writes just write.
+ */
+static int write_escalates(const struct ast_custom_function *acf) {
+	int res = 0;
+	struct ast_custom_escalating_function *cur_escalation;
+
+	AST_RWLIST_RDLOCK(&escalation_root);
+	AST_RWLIST_TRAVERSE(&escalation_root, cur_escalation, list) {
+		if (cur_escalation->acf == acf) {
+			res = cur_escalation->write_escalates;
+			break;
+		}
+	}
+	AST_RWLIST_UNLOCK(&escalation_root);
+	return res;
+}
+
 /*! \internal
  *  \brief Retrieve the XML documentation of a specified ast_custom_function,
  *         and populate ast_custom_function string fields.
@@ -3332,6 +3412,50 @@ int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_m
 	return 0;
 }
 
+int __ast_custom_function_register_escalating(struct ast_custom_function *acf, enum ast_custom_function_escalation escalation, struct ast_module *mod)
+{
+	struct ast_custom_escalating_function *acf_escalation = NULL;
+	int res;
+
+	res = __ast_custom_function_register(acf, mod);
+	if (res != 0) {
+		return -1;
+	}
+
+	if (escalation == AST_CFE_NONE) {
+		/* No escalations; no need to do anything else */
+		return 0;
+	}
+
+	acf_escalation = ast_calloc(1, sizeof(*acf_escalation));
+	if (!acf_escalation) {
+		ast_custom_function_unregister(acf);
+		return -1;
+	}
+
+	acf_escalation->acf = acf;
+	switch (escalation) {
+	case AST_CFE_NONE:
+		break;
+	case AST_CFE_READ:
+		acf_escalation->read_escalates = 1;
+		break;
+	case AST_CFE_WRITE:
+		acf_escalation->write_escalates = 1;
+		break;
+	case AST_CFE_BOTH:
+		acf_escalation->read_escalates = 1;
+		acf_escalation->write_escalates = 1;
+		break;
+	}
+
+	AST_RWLIST_WRLOCK(&escalation_root);
+	AST_RWLIST_INSERT_TAIL(&escalation_root, acf_escalation, list);
+	AST_RWLIST_UNLOCK(&escalation_root);
+
+	return 0;
+}
+
 /*! \brief return a pointer to the arguments of the function,
  * and terminates the function name with '\\0'
  */
@@ -3353,6 +3477,124 @@ static char *func_args(char *function)
 	return args;
 }
 
+void pbx_live_dangerously(int new_live_dangerously)
+{
+	if (new_live_dangerously && !live_dangerously) {
+		ast_log(LOG_WARNING, "Privilege escalation protection disabled!\n"
+			"See https://wiki.asterisk.org/wiki/x/1gKfAQ for more details.\n");
+	}
+
+	if (!new_live_dangerously && live_dangerously) {
+		ast_log(LOG_NOTICE, "Privilege escalation protection enabled.\n");
+	}
+	live_dangerously = new_live_dangerously;
+}
+
+int ast_thread_inhibit_escalations(void)
+{
+	int *thread_inhibit_escalations;
+
+	thread_inhibit_escalations = ast_threadstorage_get(
+		&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
+
+	if (thread_inhibit_escalations == NULL) {
+		ast_log(LOG_ERROR, "Error inhibiting privilege escalations for current thread\n");
+		return -1;
+	}
+
+	*thread_inhibit_escalations = 1;
+	return 0;
+}
+
+/*!
+ * \brief Indicates whether the current thread inhibits the execution of
+ * dangerous functions.
+ *
+ * \return True (non-zero) if dangerous function execution is inhibited.
+ * \return False (zero) if dangerous function execution is allowed.
+ */
+static int thread_inhibits_escalations(void)
+{
+	int *thread_inhibit_escalations;
+
+	thread_inhibit_escalations = ast_threadstorage_get(
+		&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
+
+	if (thread_inhibit_escalations == NULL) {
+		ast_log(LOG_ERROR, "Error checking thread's ability to run dangerous functions\n");
+		/* On error, assume that we are inhibiting */
+		return 1;
+	}
+
+	return *thread_inhibit_escalations;
+}
+
+/*!
+ * \brief Determines whether execution of a custom function's read function
+ * is allowed.
+ *
+ * \param acfptr Custom function to check
+ * \return True (non-zero) if reading is allowed.
+ * \return False (zero) if reading is not allowed.
+ */
+static int is_read_allowed(struct ast_custom_function *acfptr)
+{
+	if (!acfptr) {
+		return 1;
+	}
+
+	if (!read_escalates(acfptr)) {
+		return 1;
+	}
+
+	if (!thread_inhibits_escalations()) {
+		return 1;
+	}
+
+	if (live_dangerously) {
+		/* Global setting overrides the thread's preference */
+		ast_debug(2, "Reading %s from a dangerous context\n",
+			acfptr->name);
+		return 1;
+	}
+
+	/* We have no reason to allow this function to execute */
+	return 0;
+}
+
+/*!
+ * \brief Determines whether execution of a custom function's write function
+ * is allowed.
+ *
+ * \param acfptr Custom function to check
+ * \return True (non-zero) if writing is allowed.
+ * \return False (zero) if writing is not allowed.
+ */
+static int is_write_allowed(struct ast_custom_function *acfptr)
+{
+	if (!acfptr) {
+		return 1;
+	}
+
+	if (!write_escalates(acfptr)) {
+		return 1;
+	}
+
+	if (!thread_inhibits_escalations()) {
+		return 1;
+	}
+
+	if (live_dangerously) {
+		/* Global setting overrides the thread's preference */
+		ast_debug(2, "Writing %s from a dangerous context\n",
+			acfptr->name);
+		return 1;
+	}
+
+	/* We have no reason to allow this function to execute */
+	return 0;
+}
+
 int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
 {
 	char *copy = ast_strdupa(function);
@@ -3363,6 +3605,8 @@ int ast_func_read(struct ast_channel *chan, const char *function, char *workspac
 		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
 	else if (!acfptr->read)
 		ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
+	else if (!is_read_allowed(acfptr))
+		ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
 	else {
 		int res;
 		struct ast_module_user *u = NULL;
@@ -3382,11 +3626,13 @@ int ast_func_write(struct ast_channel *chan, const char *function, const char *v
 	char *args = func_args(copy);
 	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
 
-	if (acfptr == NULL)
+	if (acfptr == NULL) {
 		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
-	else if (!acfptr->write)
+	} else if (!acfptr->write) {
 		ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
-	else {
+	} else if (!is_write_allowed(acfptr)) {
+		ast_log(LOG_ERROR, "Dangerous function %s write blocked\n", copy);
+	} else {
 		int res;
 		struct ast_module_user *u = NULL;
 		if (acfptr->mod)
diff --git a/main/rtp.c b/main/rtp.c
index 8fee3a5..d5bd004 100644
--- a/main/rtp.c
+++ b/main/rtp.c
@@ -4601,7 +4601,7 @@ static char *rtp_do_debug_ip(struct ast_cli_args *a)
 	int port = 0;
 	char *p, *arg;
 
-	arg = a->argv[3];
+	arg = a->argv[4];
 	p = strstr(arg, ":");
 	if (p) {
 		*p = '\0';
@@ -4631,7 +4631,7 @@ static char *rtcp_do_debug_ip(struct ast_cli_args *a)
 	int port = 0;
 	char *p, *arg;
 
-	arg = a->argv[3];
+	arg = a->argv[4];
 	p = strstr(arg, ":");
 	if (p) {
 		*p = '\0';
diff --git a/main/tcptls.c b/main/tcptls.c
index d25b433..93c12c2 100644
--- a/main/tcptls.c
+++ b/main/tcptls.c
@@ -44,6 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 253620 $")
 #include "asterisk/options.h"
 #include "asterisk/manager.h"
 #include "asterisk/astobj2.h"
+#include "asterisk/pbx.h"
 
 /*! \brief
  * replacement read/write functions for SSL support.
@@ -135,12 +136,26 @@ static void *handle_tcptls_connection(void *data)
 	char err[256];
 #endif
 
+	/* TCP/TLS connections are associated with external protocols, and
+	 * should not be allowed to execute 'dangerous' functions. This may
+	 * need to be pushed down into the individual protocol handlers, but
+	 * this seems like a good general policy.
+	 */
+	if (ast_thread_inhibit_escalations()) {
+		ast_log(LOG_ERROR, "Failed to inhibit privilege escalations; killing connection\n");
+		return NULL;
+	}
+
 	/*
 	* open a FILE * as appropriate.
 	*/
 	if (!tcptls_session->parent->tls_cfg) {
-		tcptls_session->f = fdopen(tcptls_session->fd, "w+");
-		setvbuf(tcptls_session->f, NULL, _IONBF, 0);
+		if ((tcptls_session->f = fdopen(tcptls_session->fd, "w+"))) {
+			if(setvbuf(tcptls_session->f, NULL, _IONBF, 0)) {
+				fclose(tcptls_session->f);
+				tcptls_session->f = NULL;
+			}
+		}
 	}
 #ifdef DO_SSL
 	else if ( (tcptls_session->ssl = SSL_new(tcptls_session->parent->tls_cfg->ssl_ctx)) ) {
diff --git a/main/udptl.c b/main/udptl.c
index b02189a..7a777af 100644
--- a/main/udptl.c
+++ b/main/udptl.c
@@ -217,38 +217,29 @@ static int decode_length(uint8_t *buf, unsigned int limit, unsigned int *len, un
 	}
 	*pvalue = (buf[*len] & 0x3F) << 14;
 	(*len)++;
-	/* Indicate we have a fragment */
+	/* We have a fragment.  Currently we don't process fragments. */
+	ast_debug(1, "UDPTL packet with length greater than 16K received, decoding will fail\n");
 	return 1;
 }
 /*- End of function --------------------------------------------------------*/
 
 static int decode_open_type(uint8_t *buf, unsigned int limit, unsigned int *len, const uint8_t **p_object, unsigned int *p_num_octets)
 {
-	unsigned int octet_cnt;
-	unsigned int octet_idx;
-	unsigned int i;
-	int length; /* a negative length indicates the limit has been reached in decode_length. */
-	const uint8_t **pbuf;
+	unsigned int octet_cnt = 0;
 
-	for (octet_idx = 0, *p_num_octets = 0; ; octet_idx += octet_cnt) {
-		octet_cnt = 0;
-		if ((length = decode_length(buf, limit, len, &octet_cnt)) < 0)
-			return -1;
-		if (octet_cnt > 0) {
-			*p_num_octets += octet_cnt;
+	if (decode_length(buf, limit, len, &octet_cnt) != 0)
+		return -1;
 
-			pbuf = &p_object[octet_idx];
-			i = 0;
-			/* Make sure the buffer contains at least the number of bits requested */
-			if ((*len + octet_cnt) > limit)
-				return -1;
+	if (octet_cnt > 0) {
+		/* Make sure the buffer contains at least the number of bits requested */
+		if ((*len + octet_cnt) > limit)
+			return -1;
 
-			*pbuf = &buf[*len];
-			*len += octet_cnt;
-		}
-		if (length == 0)
-			break;
+		*p_num_octets = octet_cnt;
+		*p_object = &buf[*len];
+		*len += octet_cnt;
 	}
+
 	return 0;
 }
 /*- End of function --------------------------------------------------------*/
@@ -335,8 +326,8 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, unsigned int len)
 	const uint8_t *data;
 	unsigned int ifp_len;
 	int repaired[16];
-	const uint8_t *bufs[16];
-	unsigned int lengths[16];
+	const uint8_t *bufs[ARRAY_LEN(s->f) - 1];
+	unsigned int lengths[ARRAY_LEN(s->f) - 1];
 	int span;
 	int entries;
 	int ifp_no;
@@ -366,13 +357,13 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, unsigned int len)
 			do {
 				if ((stat2 = decode_length(buf, len, &ptr, &count)) < 0)
 					return -1;
-				for (i = 0; i < count; i++) {
+				for (i = 0; i < count && total_count + i < ARRAY_LEN(bufs); i++) {
 					if ((stat1 = decode_open_type(buf, len, &ptr, &bufs[total_count + i], &lengths[total_count + i])) != 0)
 						return -1;
 				}
-				total_count += count;
+				total_count += i;
 			}
-			while (stat2 > 0);
+			while (stat2 > 0 && total_count < ARRAY_LEN(bufs));
 			/* Step through in reverse order, so we go oldest to newest */
 			for (i = total_count; i > 0; i--) {
 				if (seq_no - i >= s->rx_seq_no) {
@@ -435,6 +426,9 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, unsigned int len)
 		if (ptr + 1 > len)
 			return -1;
 		entries = buf[ptr++];
+		if (entries > MAX_FEC_ENTRIES) {
+			return -1;
+		}
 		s->rx[x].fec_entries = entries;
 
 		/* Decode the elements */
diff --git a/main/utils.c b/main/utils.c
index a767223..ee1e882 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -385,28 +385,27 @@ char *ast_uri_encode(const char *string, char *outbuf, int buflen, int doreserve
 	char *reserved = ";/?:@&=+$,# ";	/* Reserved chars */
 
  	const char *ptr  = string;	/* Start with the string */
-	char *out = NULL;
-	char *buf = NULL;
+	char *out = outbuf;
 
-	ast_copy_string(outbuf, string, buflen);
-
-	/* If there's no characters to convert, just go through and don't do anything */
-	while (*ptr) {
+	/* If there's no characters to convert, just go through and copy the string */
+	while (*ptr && out - outbuf < buflen - 1) {
 		if ((*ptr < 32) || (doreserved && strchr(reserved, *ptr))) {
-			/* Oops, we need to start working here */
-			if (!buf) {
-				buf = outbuf;
-				out = buf + (ptr - string) ;	/* Set output ptr */
+			if (out - outbuf >= buflen - 3) {
+				break;
 			}
+
 			out += sprintf(out, "%%%02x", (unsigned char) *ptr);
-		} else if (buf) {
-			*out = *ptr;	/* Continue copying the string */
+		} else {
+			*out = *ptr;	/* copy the character */
 			out++;
-		} 
+		}
 		ptr++;
 	}
-	if (buf)
+
+	if (buflen) {
 		*out = '\0';
+	}
+
 	return outbuf;
 }
 
diff --git a/res/res_agi.c b/res/res_agi.c
index 2782be8..69d31d6 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -440,7 +440,7 @@ static const char mandescr_asyncagi[] =
 "Variables:\n"
 "  *Channel: Channel that is currently in Async AGI\n"
 "  *Command: Application to execute\n"
-"   CommandID: comand id. This will be sent back in CommandID header of AsyncAGI exec event notification\n"
+"   CommandID: command id. This will be sent back in CommandID header of AsyncAGI exec event notification\n"
 "\n";
 
 static struct agi_cmd *get_agi_cmd(struct ast_channel *chan)
@@ -2433,7 +2433,7 @@ static char usage_recordfile[] =
 " to the offset without exceeding the end of the file.  \"silence\" is the number\n"
 " of seconds of silence allowed before the function returns despite the\n"
 " lack of dtmf digits or reaching timeout.  Silence value must be\n"
-" preceeded by \"s=\" and is also optional.\n";
+" preceded by \"s=\" and is also optional.\n";
 
 static char usage_autohangup[] =
 " Usage: SET AUTOHANGUP <time>\n"
diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c
index f760f64..a116267 100644
--- a/res/res_musiconhold.c
+++ b/res/res_musiconhold.c
@@ -977,7 +977,7 @@ static int moh_scan_files(struct mohclass *class) {
 	int i;
 
 	if (class->dir[0] != '/') {
-		ast_copy_string(dir_path, ast_config_AST_VAR_DIR, sizeof(dir_path));
+		ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
 		strncat(dir_path, "/", sizeof(dir_path) - 1);
 		strncat(dir_path, class->dir, sizeof(dir_path) - 1);
 	} else {
diff --git a/sounds/sounds.xml b/sounds/sounds.xml
index 192edab..b3f4ae0 100644
--- a/sounds/sounds.xml
+++ b/sounds/sounds.xml
@@ -57,7 +57,6 @@
 	</category>
 	<category name="MENUSELECT_MOH" displayname="Music On Hold File Packages" positive_output="yes">
 		<member name="MOH-OPSOUND-WAV" displayname="opsound.org Music On Hold Files, WAV format" >
-			<defaultenabled>yes</defaultenabled>
 		</member>
 		<member name="MOH-OPSOUND-ULAW" displayname="opsound.org Music On Hold Files, mu-Law format" >
 		</member>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/asterisk.git



More information about the Pkg-voip-commits mailing list