[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#704829: marked as done (unblock: asterisk/1:1.8.13.1~dfsg-2)



Your message dated Tue, 09 Apr 2013 12:41:30 +0100
with message-id <adf3d07e54902a6cd4c0302be8119ba9@mail.adsl.funky-badger.org>
and subject line Re: Bug#704829: unblock: asterisk/1:1.8.13.1~dfsg-2
has caused the Debian Bug report #704829,
regarding unblock: asterisk/1:1.8.13.1~dfsg-2
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
704829: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=704829
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package asterisk. It includes a number of fixes, mostly
two series of security fixes.

The extra bug fixes are:

1. A simple fix to add support for powerpcspe
2. Two bugs that were fixes with patches trivially backported from
   later 1.8.x upstream commits.

A debdiff is attached below.

unblock asterisk/1:1.8.13.1~dfsg-2


diff -Nru asterisk-1.8.13.1~dfsg/debian/changelog asterisk-1.8.13.1~dfsg/debian/changelog
--- asterisk-1.8.13.1~dfsg/debian/changelog	2012-09-01 05:37:51.000000000 +0300
+++ asterisk-1.8.13.1~dfsg/debian/changelog	2013-04-06 14:15:46.000000000 +0300
@@ -1,3 +1,25 @@
+asterisk (1:1.8.13.1~dfsg-2) unstable; urgency=high
+
+  * Patches backported from Asterisk 1.8.19.1 (Closes: #697230):
+    - Patch AST-2012-014 (CVE-2012-5976) - fixes Crashes due to large stack
+      allocations when using TCP.
+      The following two fixes were also pulled in order to easily apply it:
+      - Patch fix-sip-tcp-no-FILE - Switch to reading with a recv loop
+      - Patch fix-sip-tls-leak - Memory leak in the SIP TLS code
+    - Patch AST-2012-015 (CVE-2012-5977) - Denial of Service Through
+      Exploitation of Device State Caching
+  * Patch powerpcspe: Fix OSARCH for powerpcspe (Closes: #701505).
+  * README.Debian: document running the testsuite. 
+  * Patch fix_xmpp_19532: fix a crash of the XMPP code (Closes: #545272).
+  * Patches backported from Asterisk 1.8.20.2 (Closes: #704114):
+    - Patch AST-2013-002 (CVE-2012-2686): Prevent DoS in HTTP server with
+      a large POST.
+    - Patch AST-2013-003 (CVE-2012-2264): Prevent username disclosure in
+      SIP channel driver.
+  * Patch bluetooth_bind - fix breakage of chan_mobile (Closes: #614786).
+
+ -- Tzafrir Cohen <tzafrir@debian.org>  Sat, 06 Apr 2013 14:15:41 +0300
+
 asterisk (1:1.8.13.1~dfsg-1) unstable; urgency=low
 
   * New upstream release (Closes: #680470):
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/AST-2012-014 asterisk-1.8.13.1~dfsg/debian/patches/AST-2012-014
--- asterisk-1.8.13.1~dfsg/debian/patches/AST-2012-014	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/AST-2012-014	2013-01-08 02:20:03.000000000 +0200
@@ -0,0 +1,285 @@
+From: Matthew Jordan <mjordan@digium.com>
+Date: Wed, 2 Jan 2013 15:16:10 +0000
+Subject: Resolve crashes due to large stack allocations when using TCP
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=378269
+CVE: CVE-2012-5976
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-20658
+
+Asterisk had several places where messages received over various network
+transports may be copied in a single stack allocation. In the case of TCP,
+since multiple packets in a stream may be concatenated together, this can
+lead to large allocations that overflow the stack.
+
+This patch modifies those portions of Asterisk using TCP to either
+favor heap allocations or use an upper bound to ensure that the stack will not
+overflow:
+ * For SIP, the allocation now has an upper limit
+ * For HTTP, the allocation is now a heap allocation instead of a stack
+   allocation
+ * For XMPP (in res_jabber), the allocation has been eliminated since it was
+   unnecesary.
+
+Note that the HTTP portion of this issue was independently found by Brandon
+Edwards of Exodus Intelligence.
+
+Reported by: wdoekes, Brandon Edwards
+Tested by: mmichelson, wdoekes
+See also: http://downloads.asterisk.org/pub/security/AST-2012-014.html
+
+---
+ channels/chan_sip.c        |   58 +++++++++++++++++++++++++++++++-------------
+ channels/sip/include/sip.h |    1 +
+ main/http.c                |   20 ++++++++++++---
+ res/res_jabber.c           |    5 ++--
+ 4 files changed, 60 insertions(+), 24 deletions(-)
+
+--- a/channels/chan_sip.c
++++ b/channels/chan_sip.c
+@@ -2520,19 +2520,20 @@ static int sip_tls_read(struct sip_reque
+ 			int authenticated, time_t start, struct sip_threadinfo *me)
+ {
+ 	int res, content_length, after_poll = 1, need_poll = 1;
++	size_t datalen = ast_str_strlen(req->data);
+ 	char buf[1024] = "";
+ 	int timeout = -1;
+-
+-	/* Read in headers one line at a time */
+-	while (ast_str_strlen(req->data) < 4 || strncmp(REQ_OFFSET_TO_STR(req, data->used - 4), "\r\n\r\n", 4)) {
+-		if (!tcptls_session->client && !authenticated) {
+-			if ((timeout = sip_check_authtimeout(start)) < 0) {
+-				ast_debug(2, "SIP SSL server failed to determine authentication timeout\n");
++ 
++ 	/* Read in headers one line at a time */
++	while (datalen < 4 || strncmp(REQ_OFFSET_TO_STR(req, data->used - 4), "\r\n\r\n", 4)) {
++ 		if (!tcptls_session->client && !authenticated) {
++ 			if ((timeout = sip_check_authtimeout(start)) < 0) {
++				ast_debug(2, "SIP TLS server failed to determine authentication timeout\n");
+ 				return -1;
+ 			}
+ 
+ 			if (timeout == 0) {
+-				ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
++				ast_debug(2, "SIP TLS server timed out\n");
+ 				return -1;
+ 			}
+ 		} else {
+@@ -2547,11 +2548,11 @@ static int sip_tls_read(struct sip_reque
+ 			after_poll = 1;
+ 			res = ast_wait_for_input(tcptls_session->fd, timeout);
+ 			if (res < 0) {
+-				ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res);
++				ast_debug(2, "SIP TLS server :: ast_wait_for_input returned %d\n", res);
+ 				return -1;
+ 			} else if (res == 0) {
+ 				/* timeout */
+-				ast_debug(2, "SIP TCP server timed out\n");
++				ast_debug(2, "SIP TLS server timed out\n");
+ 				return -1;
+ 			}
+ 		}
+@@ -2572,6 +2573,13 @@ static int sip_tls_read(struct sip_reque
+ 			return -1;
+ 		}
+ 		ast_str_append(&req->data, 0, "%s", buf);
++
++		datalen = ast_str_strlen(req->data);
++		if (datalen > SIP_MAX_PACKET_SIZE) {
++			ast_log(LOG_WARNING, "Rejecting TLS packet from '%s' because way too large: %zu\n",
++				ast_sockaddr_stringify(&tcptls_session->remote_address), datalen);
++			return -1;
++		}
+ 	}
+ 	copy_request(reqcpy, req);
+ 	parse_request(reqcpy);
+@@ -2585,7 +2593,7 @@ static int sip_tls_read(struct sip_reque
+ 				}
+ 
+ 				if (timeout == 0) {
+-					ast_debug(2, "SIP SSL server timed out\n");
++					ast_debug(2, "SIP TLS server timed out\n");
+ 					return -1;
+ 				}
+ 			} else {
+@@ -2597,11 +2605,11 @@ static int sip_tls_read(struct sip_reque
+ 				after_poll = 1;
+ 				res = ast_wait_for_input(tcptls_session->fd, timeout);
+ 				if (res < 0) {
+-					ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res);
++					ast_debug(2, "SIP TLS server :: ast_wait_for_input returned %d\n", res);
+ 					return -1;
+ 				} else if (res == 0) {
+ 					/* timeout */
+-					ast_debug(2, "SIP TCP server timed out\n");
++					ast_debug(2, "SIP TLS server timed out\n");
+ 					return -1;
+ 				}
+ 			}
+@@ -2624,6 +2632,13 @@ static int sip_tls_read(struct sip_reque
+ 			}
+ 			content_length -= strlen(buf);
+ 			ast_str_append(&req->data, 0, "%s", buf);
++		
++			datalen = ast_str_strlen(req->data);
++			if (datalen > SIP_MAX_PACKET_SIZE) {
++				ast_log(LOG_WARNING, "Rejecting TLS packet from '%s' because way too large: %zu\n",
++					ast_sockaddr_stringify(&tcptls_session->remote_address), datalen);
++				return -1;
++			}
+ 		}
+ 	}
+ 	/*! \todo XXX If there's no Content-Length or if the content-length and what
+@@ -2801,6 +2816,8 @@ static int sip_tcp_read(struct sip_reque
+ 	enum message_integrity message_integrity = MESSAGE_FRAGMENT;
+ 
+ 	while (message_integrity == MESSAGE_FRAGMENT) {
++		size_t datalen;
++
+ 		if (ast_str_strlen(tcptls_session->overflow_buf) == 0) {
+ 			char readbuf[4097];
+ 			int timeout;
+@@ -2840,6 +2857,13 @@ static int sip_tcp_read(struct sip_reque
+ 			ast_str_append(&req->data, 0, "%s", ast_str_buffer(tcptls_session->overflow_buf));
+ 			ast_str_reset(tcptls_session->overflow_buf);
+ 		}
++		
++		datalen = ast_str_strlen(req->data);
++		if (datalen > SIP_MAX_PACKET_SIZE) {
++			ast_log(LOG_WARNING, "Rejecting TCP packet from '%s' because way too large: %zu\n",
++				ast_sockaddr_stringify(&tcptls_session->remote_address), datalen);
++			return -1;
++		}
+ 
+ 		message_integrity = check_message_integrity(&req->data, &tcptls_session->overflow_buf);
+ 	}
+@@ -2911,7 +2935,7 @@ static void *_sip_tcp_helper_thread(stru
+ 	}
+ 
+ 	me->threadid = pthread_self();
+-	ast_debug(2, "Starting thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
++	ast_debug(2, "Starting thread for %s server\n", tcptls_session->ssl ? "TLS" : "TCP");
+ 
+ 	/* set up pollfd to watch for reads on both the socket and the alert_pipe */
+ 	fds[0].fd = tcptls_session->fd;
+@@ -2945,7 +2969,7 @@ static void *_sip_tcp_helper_thread(stru
+ 			}
+ 
+ 			if (timeout == 0) {
+-				ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
++				ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "TLS": "TCP");
+ 				goto cleanup;
+ 			}
+ 		} else {
+@@ -2955,11 +2979,11 @@ static void *_sip_tcp_helper_thread(stru
+ 		if (ast_str_strlen(tcptls_session->overflow_buf) == 0) {
+ 			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);
++				ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "TLS": "TCP", res);
+ 				goto cleanup;
+ 			} else if (res == 0) {
+ 				/* timeout */
+-				ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
++				ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "TLS": "TCP");
+ 				goto cleanup;
+ 			}
+ 		}
+@@ -3041,7 +3065,7 @@ static void *_sip_tcp_helper_thread(stru
+ 		}
+ 	}
+ 
+-	ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
++	ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "TLS" : "TCP");
+ 
+ cleanup:
+ 	if (tcptls_session && !tcptls_session->client && !authenticated) {
+--- a/channels/sip/include/sip.h
++++ b/channels/sip/include/sip.h
+@@ -96,6 +96,7 @@
+ 
+ #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 */
+ 
+--- a/main/http.c
++++ b/main/http.c
+@@ -622,6 +622,7 @@ struct ast_variable *ast_http_get_post_v
+ 	int content_length = 0;
+ 	struct ast_variable *v, *post_vars=NULL, *prev = NULL;
+ 	char *buf, *var, *val;
++	int res;
+ 
+ 	for (v = headers; v; v = v->next) {
+ 		if (!strcasecmp(v->name, "Content-Type")) {
+@@ -634,21 +635,27 @@ struct ast_variable *ast_http_get_post_v
+ 
+ 	for (v = headers; v; v = v->next) {
+ 		if (!strcasecmp(v->name, "Content-Length")) {
+-			content_length = atoi(v->value) + 1;
++			content_length = atoi(v->value);
+ 			break;
+ 		}
+ 	}
+ 
+-	if (!content_length) {
++	if (content_length <= 0) {
+ 		return NULL;
+ 	}
+ 
+-	if (!(buf = alloca(content_length))) {
++	buf = ast_malloc(content_length + 1);
++	if (!buf) {
+ 		return NULL;
+ 	}
+-	if (!fgets(buf, content_length, ser->f)) {
+-		return NULL;
++
++	res = fread(buf, 1, content_length, ser->f);
++	if (res < content_length) {
++		/* Error, distinguishable by ferror() or feof(), but neither
++		 * is good. */
++		goto done;
+ 	}
++	buf[content_length] = '\0';
+ 
+ 	while ((val = strsep(&buf, "&"))) {
+ 		var = strsep(&val, "=");
+@@ -667,6 +674,9 @@ struct ast_variable *ast_http_get_post_v
+ 			prev = v;
+ 		}
+ 	}
++	
++done:
++	ast_free(buf);
+ 	return post_vars;
+ }
+ 
+--- a/res/res_jabber.c
++++ b/res/res_jabber.c
+@@ -768,7 +768,7 @@ static struct ast_custom_function jabber
+  */
+ static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
+ {
+-	char *aux = NULL, *parse = NULL;
++	char *parse = NULL;
+ 	int timeout;
+ 	int jidlen, resourcelen;
+ 	struct timeval start;
+@@ -885,7 +885,7 @@ static int acf_jabberreceive_read(struct
+ 				continue;
+ 			}
+ 			found = 1;
+-			aux = ast_strdupa(tmp->message);
++			ast_copy_string(buf, tmp->message, buflen);
+ 			AST_LIST_REMOVE_CURRENT(list);
+ 			aji_message_destroy(tmp);
+ 			break;
+@@ -910,7 +910,6 @@ static int acf_jabberreceive_read(struct
+ 		ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
+ 		return -1;
+ 	}
+-	ast_copy_string(buf, aux, buflen);
+ 
+ 	return 0;
+ }
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/AST-2012-015 asterisk-1.8.13.1~dfsg/debian/patches/AST-2012-015
--- asterisk-1.8.13.1~dfsg/debian/patches/AST-2012-015	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/AST-2012-015	2013-01-08 02:20:03.000000000 +0200
@@ -0,0 +1,1012 @@
+From: Matthew Jordan <mjordan@digium.com>
+Date: Wed, 2 Jan 2013 16:54:20 +0000
+Subject: Prevent exhaustion of system resources through exploitation of event cache
+CVE: CVE-2012-5977
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=378303
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-20175
+
+Asterisk maintains an internal cache for devices in the event subsystem. The
+device state cache holds the state of each device known to Asterisk, such that
+consumers of device state information can query for the last known state for
+a particular device, even if it is not part of an active call. The concept of
+a device in Asterisk can include entities that do not have a physical
+representation. One way that this occurred was when anonymous calls are allowed
+in Asterisk. A device was automatically created and stored in the cache for
+each anonymous call that occurred; this was possible in the SIP and IAX2
+channel drivers and through channel drivers that utilized the
+res_jabber/res_xmpp resource modules (Gtalk, Jingle, and Motif). These devices
+are never removed from the system, allowing anonymous calls to potentially
+exhaust a system's resources.
+
+This patch changes the event cache subsystem and device state management to
+no longer cache devices that are not associated with a physical entity.
+
+Reported by: Russell Bryant, Leif Madsen, Joshua Colp
+Tested by: kmoore
+See also: http://downloads.asterisk.org/pub/security/AST-2012-015.html
+
+---
+ apps/app_confbridge.c          |    4 +--
+ apps/app_meetme.c              |   16 +++++------
+ channels/chan_agent.c          |   12 ++++----
+ channels/chan_dahdi.c          |    7 +++--
+ channels/chan_iax2.c           |   31 +++++++++++---------
+ channels/chan_local.c          |    3 ++
+ channels/chan_sip.c            |   18 +++++++-----
+ channels/chan_skinny.c         |   16 +++++------
+ funcs/func_devstate.c          |    6 ++--
+ include/asterisk/channel.h     |    6 ++++
+ include/asterisk/devicestate.h |   16 +++++++++--
+ include/asterisk/event_defs.h  |    8 +++++-
+ main/channel.c                 |    5 ++--
+ main/devicestate.c             |   51 +++++++++++++++++++++------------
+ main/event.c                   |    1 +
+ main/features.c                |    2 +-
+ res/res_calendar.c             |    8 +++---
+ res/res_jabber.c               |   61 ++++++++++++++++++++++++++++++----------
+ 18 files changed, 177 insertions(+), 94 deletions(-)
+
+--- a/apps/app_confbridge.c
++++ b/apps/app_confbridge.c
+@@ -486,7 +486,7 @@ static struct conference_bridge *join_co
+ 
+ 	/* Set the device state for this conference */
+ 	if (conference_bridge->users == 1) {
+-		ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
++		ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name);
+ 	}
+ 
+ 	/* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
+@@ -568,7 +568,7 @@ static void  leave_conference_bridge(str
+ 		}
+ 	} else {
+ 		/* Set device state to "not in use" */
+-		ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name);
++		ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name);
+ 
+ 		ao2_unlink(conference_bridges, conference_bridge);
+ 	}
+--- a/apps/app_meetme.c
++++ b/apps/app_meetme.c
+@@ -2489,7 +2489,7 @@ static int conf_run(struct ast_channel *
+ 
+ 	/* 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);
+ 
+@@ -3783,7 +3783,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 */
+@@ -5199,8 +5199,8 @@ static void sla_change_trunk_state(const
+ 				|| 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;
+ 		}
+ 	}
+@@ -5698,8 +5698,8 @@ static void sla_handle_hold_event(struct
+ {
+ 	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);
+ 
+@@ -6208,8 +6208,8 @@ static int sla_station_exec(struct ast_c
+ 			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;
+--- a/channels/chan_agent.c
++++ b/channels/chan_agent.c
+@@ -611,7 +611,7 @@ static struct ast_frame *agent_read(stru
+ 		if (p->chan) {
+ 			p->chan->_bridge = NULL;
+ 			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 {
+@@ -866,7 +866,7 @@ static int agent_call(struct ast_channel
+ 	} 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) {
+@@ -985,7 +985,7 @@ static int agent_hangup(struct ast_chann
+ 	if (!p->loginstart) {
+ 		p->logincallerid[0] = '\0';
+ 	} 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->abouttograb) {
+@@ -2128,7 +2128,7 @@ static int login_exec(struct ast_channel
+ 						}
+ 						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) {
+@@ -2149,7 +2149,7 @@ static int login_exec(struct ast_channel
+ 								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) {
+ 										check_beep(p, 0);
+ 									} else {
+@@ -2209,7 +2209,7 @@ static int login_exec(struct ast_channel
+ 						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_cond_destroy(&p->app_complete_cond);
+--- a/channels/chan_dahdi.c
++++ b/channels/chan_dahdi.c
+@@ -3312,7 +3312,7 @@ static void dahdi_pri_update_span_devsta
+ 	}
+ 	if (pri->congestion_devstate != new_state) {
+ 		pri->congestion_devstate = new_state;
+-		ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/congestion", pri->span);
++		ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_NOT_CACHABLE, "DAHDI/I%d/congestion", pri->span);
+ 	}
+ #if defined(THRESHOLD_DEVSTATE_PLACEHOLDER)
+ 	/* Update the span threshold device state and report any change. */
+@@ -3328,7 +3328,7 @@ static void dahdi_pri_update_span_devsta
+ 	}
+ 	if (pri->threshold_devstate != new_state) {
+ 		pri->threshold_devstate = new_state;
+-		ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/threshold", pri->span);
++		ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_NOT_CACHABLE, "DAHDI/I%d/threshold", pri->span);
+ 	}
+ #endif	/* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */
+ }
+@@ -9757,7 +9757,8 @@ static struct ast_channel *dahdi_new(str
+ 	if (dashptr) {
+ 		*dashptr = '\0';
+ 	}
+-	ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, device_name);
++	tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE;
++	ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_NOT_CACHABLE, device_name);
+ 
+ 	for (v = i->vars ; v ; v = v->next)
+ 		pbx_builtin_setvar_helper(tmp, v->name, v->value);
+--- a/channels/chan_iax2.c
++++ b/channels/chan_iax2.c
+@@ -5728,7 +5728,7 @@ static int iax2_getpeertrunk(struct sock
+ }
+ 
+ /*! \brief  Create new call, interface with the PBX core */
+-static struct ast_channel *ast_iax2_new(int callno, int state, format_t capability, const char *linkedid)
++static struct ast_channel *ast_iax2_new(int callno, int state, format_t capability, const char *linkedid, unsigned int cachable)
+ {
+ 	struct ast_channel *tmp;
+ 	struct chan_iax2_pvt *i;
+@@ -5797,6 +5797,10 @@ static struct ast_channel *ast_iax2_new(
+ 	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)
+@@ -8085,7 +8089,7 @@ static int register_verify(int callno, s
+ 		/* 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;
+@@ -8639,7 +8643,7 @@ static void __expire_registry(const void
+ 	if (!ast_test_flag64(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);
+ 
+@@ -8693,7 +8697,7 @@ static void reg_source_db(struct iax2_pe
+ 		}
+ 	}
+ 
+-	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) {
+@@ -8770,14 +8774,14 @@ static int update_registry(struct sockad
+ 					    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_flag64(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 */
+@@ -10278,7 +10282,8 @@ static int socket_process(struct iax2_th
+ 		    (f.frametype == AST_FRAME_IAX)) {
+ 			if (ast_test_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART)) {
+ 				ast_clear_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART);
+-				if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat, NULL)) {
++				if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat, NULL,
++						  ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED))) {
+ 					ast_variables_destroy(ies.vars);
+ 					ast_mutex_unlock(&iaxsl[fr->callno]);
+ 					return 1;
+@@ -10911,13 +10916,13 @@ static int socket_process(struct iax2_th
+ 						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;
+@@ -11159,7 +11164,7 @@ static int socket_process(struct iax2_th
+ 											using_prefs);
+ 
+ 							ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
+-							if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, NULL)))
++							if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, NULL, 1)))
+ 								iax2_destroy(fr->callno);
+ 							else if (ies.vars) {
+ 								struct ast_datastore *variablestore;
+@@ -11230,7 +11235,7 @@ immediatedial:
+ 							ast_getformatname_multiple(tmp, sizeof(tmp), 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, NULL)))
++						if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat, NULL, 1)))
+ 							iax2_destroy(fr->callno);
+ 						else if (ies.vars) {
+ 							struct ast_datastore *variablestore;
+@@ -11982,7 +11987,7 @@ static void __iax2_poke_noanswer(const v
+ 	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]);
+@@ -12150,7 +12155,7 @@ static struct ast_channel *iax2_request(
+ 	if (cai.found)
+ 		ast_string_field_set(iaxs[callno], host, pds.peer);
+ 
+-	c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, requestor ? requestor->linkedid : NULL);
++	c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, requestor ? requestor->linkedid : NULL, cai.found);
+ 
+ 	ast_mutex_unlock(&iaxsl[callno]);
+ 
+--- a/channels/chan_local.c
++++ b/channels/chan_local.c
+@@ -1143,6 +1143,9 @@ static struct ast_channel *local_new(str
+ 	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);
+--- a/channels/chan_sip.c
++++ b/channels/chan_sip.c
+@@ -6312,7 +6312,7 @@ static int update_call_counter(struct si
+ 	}
+ 
+ 	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;
+@@ -7510,6 +7510,9 @@ static struct ast_channel *sip_new(struc
+ 	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];
+@@ -14021,7 +14024,7 @@ static int expire_register(const void *d
+ 
+ 	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
+@@ -14803,8 +14806,9 @@ static void sip_peer_hold(struct sip_pvt
+ 	ast_atomic_fetchadd_int(&p->relatedpeer->onHold, (hold ? +1 : -1));
+ 
+ 	/* Request device state update */
+-	ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", p->relatedpeer->name);
+-	
++	ast_devstate_changed(AST_DEVICE_UNKNOWN, (p->owner->flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE),
++			     "SIP/%s", p->relatedpeer->name);
++
+ 	return;
+ }
+ 
+@@ -15208,7 +15212,7 @@ static enum check_auth_result register_v
+ 		}
+ 	}
+ 	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) {
+@@ -21122,7 +21126,7 @@ static void handle_response_peerpoke(str
+ 
+ 		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);
+ 		}
+@@ -26540,7 +26544,7 @@ static int sip_poke_noanswer(const void
+ 	/* Don't send a devstate change if nothing changed. */
+ 	if (peer->lastms > -1) {
+ 		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 */
+--- a/channels/chan_skinny.c
++++ b/channels/chan_skinny.c
+@@ -1925,7 +1925,7 @@ static int skinny_register(struct skinny
+ 					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;
+ 			}
+@@ -1963,7 +1963,7 @@ static int skinny_unregister(struct skin
+ 				l->instance = 0;
+ 				manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Unregistered\r\n", l->name, d->name);
+ 				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);
+ 			}
+ 		}
+ 	}
+@@ -5321,7 +5321,7 @@ static int handle_stimulus_message(struc
+ 			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;
+ }
+@@ -5372,7 +5372,7 @@ static int handle_offhook_message(struct
+ 	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;
+@@ -5448,7 +5448,7 @@ static int handle_onhook_message(struct
+ 		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;
+@@ -5834,7 +5834,7 @@ static int handle_soft_key_event_message
+ 		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:
+@@ -6049,7 +6049,7 @@ static int handle_soft_key_event_message
+ 				transmit_callstate(d, l->instance, sub->callid, l->hookstate);
+ 			}
+ 
+-			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) {
+@@ -6073,7 +6073,7 @@ static int handle_soft_key_event_message
+ 				}
+ 			}
+ 			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;
+--- a/funcs/func_devstate.c
++++ b/funcs/func_devstate.c
+@@ -132,7 +132,7 @@ static int devstate_write(struct ast_cha
+ 
+ 	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;
+ }
+@@ -294,7 +294,7 @@ static char *handle_cli_devstate_change(
+ 
+ 	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;
+ }
+@@ -340,7 +340,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;
+--- a/include/asterisk/channel.h
++++ b/include/asterisk/channel.h
+@@ -936,6 +936,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 */
+--- 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(c
+  * \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(c
+  * \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
+  * \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.
+--- a/include/asterisk/event_defs.h
++++ b/include/asterisk/event_defs.h
+@@ -283,8 +283,14 @@ enum ast_event_ie_type {
+ 	AST_EVENT_IE_CHALLENGE           = 0x0032,
+ 	AST_EVENT_IE_RESPONSE            = 0x0033,
+ 	AST_EVENT_IE_EXPECTED_RESPONSE   = 0x0034,
++	/*!
++	 * \brief Event non-cachability flag
++	 * Used by: All events
++	 * Payload type: UINT
++	 */
++	AST_EVENT_IE_CACHABLE            = 0x0035,
+ 	/*! \brief Must be the last IE value +1 */
+-	AST_EVENT_IE_TOTAL               = 0x0035,
++	AST_EVENT_IE_TOTAL               = 0x0036,
+ };
+ 
+ /*!
+--- a/main/channel.c
++++ b/main/channel.c
+@@ -285,6 +285,7 @@ static void channel_data_add_flags(struc
+ 	ast_data_add_bool(tree, "BRIDGE_HANGUP_RUN", ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_RUN));
+ 	ast_data_add_bool(tree, "BRIDGE_HANGUP_DONT", ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT));
+ 	ast_data_add_bool(tree, "DISABLE_WORKAROUNDS", ast_test_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS));
++	ast_data_add_bool(tree, "DISABLE_DEVSTATE_CACHE", ast_test_flag(chan, AST_FLAG_DISABLE_DEVSTATE_CACHE));
+ }
+ 
+ #if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)
+@@ -2477,7 +2478,7 @@ static void ast_channel_destructor(void
+ 		 * instance is dead, we don't know the state of all other possible
+ 		 * instances.
+ 		 */
+-		ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, device_name);
++		ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, (chan->flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), device_name);
+ 	}
+ }
+ 
+@@ -6918,7 +6919,7 @@ int ast_setstate(struct ast_channel *cha
+ 	/* 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 */
+ 	ast_manager_event(chan, EVENT_FLAG_CALL, "Newstate",
+--- a/main/devicestate.c
++++ b/main/devicestate.c
+@@ -170,6 +170,7 @@ static AST_RWLIST_HEAD_STATIC(devstate_p
+ 
+ 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];
+ };
+ 
+@@ -422,7 +424,7 @@ static int getproviderstate(const char *
+ 	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;
+@@ -438,18 +440,23 @@ static void devstate_event(const char *d
+ 	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;
+ 
+@@ -457,10 +464,10 @@ static void do_state_change(const char *
+ 
+ 	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;
+ 
+@@ -481,14 +488,15 @@ int ast_devstate_changed_literal(enum as
+ 	 */
+ 
+ 	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);
+@@ -500,10 +508,10 @@ int ast_devstate_changed_literal(enum as
+ 
+ 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;
+@@ -512,7 +520,7 @@ int ast_devstate_changed(enum ast_device
+ 	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, ...) 
+@@ -524,7 +532,7 @@ int ast_device_state_changed(const char
+ 	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 */
+@@ -544,7 +552,7 @@ static void *do_devstate_changes(void *d
+ 		/* 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);
+ 		}
+ 	}
+@@ -588,7 +596,7 @@ static void devstate_cache_cb(const stru
+ 	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;
+@@ -639,7 +647,11 @@ static void process_collection(const cha
+ 		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)
+@@ -665,7 +677,7 @@ static void handle_devstate_change(struc
+ 	/* 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);
+ }
+@@ -694,10 +706,12 @@ static void devstate_change_collector_cb
+ 	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");
+@@ -710,6 +724,7 @@ static void devstate_change_collector_cb
+ 	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);
+--- a/main/event.c
++++ b/main/event.c
+@@ -264,6 +264,7 @@ static const struct ie_map {
+ 	[AST_EVENT_IE_CHALLENGE]           = { AST_EVENT_IE_PLTYPE_STR,  "Challenge" },
+ 	[AST_EVENT_IE_RESPONSE]            = { AST_EVENT_IE_PLTYPE_STR,  "Response" },
+ 	[AST_EVENT_IE_EXPECTED_RESPONSE]   = { AST_EVENT_IE_PLTYPE_STR,  "ExpectedResponse" },
++	[AST_EVENT_IE_CACHABLE]            = { AST_EVENT_IE_PLTYPE_UINT,  "Cachable" },
+ };
+ 
+ const char *ast_event_get_type_name(const struct ast_event *event)
+--- a/main/features.c
++++ b/main/features.c
+@@ -1061,7 +1061,7 @@ static void notify_metermaids(const char
+ 	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 */
+--- a/res/res_calendar.c
++++ b/res/res_calendar.c
+@@ -571,9 +571,9 @@ static struct ast_calendar_event *destro
+ 	 * but haven't hit the end event yet, go ahead and set the devicestate to the current busy status */
+ 	if (event->bs_start_sched < 0 && event->bs_end_sched >= 0) {
+ 		if (!calendar_is_busy(event->owner)) {
+-			ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Calendar:%s", event->owner->name);
++			ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
+ 		} else {
+-			ast_devstate_changed(AST_DEVICE_BUSY, "Calendar:%s", event->owner->name);
++			ast_devstate_changed(AST_DEVICE_BUSY, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
+ 		}
+ 	}
+ 
+@@ -814,9 +814,9 @@ static int calendar_devstate_change(cons
+ 	/* We can have overlapping events, so ignore the event->busy_state and check busy state
+ 	 * based on all events in the calendar */
+ 	if (!calendar_is_busy(event->owner)) {
+-		ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Calendar:%s", event->owner->name);
++		ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
+ 	} else {
+-		ast_devstate_changed(AST_DEVICE_BUSY, "Calendar:%s", event->owner->name);
++		ast_devstate_changed(AST_DEVICE_BUSY, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
+ 	}
+ 
+ 	event = ast_calendar_unref_event(event);
+--- a/res/res_jabber.c
++++ b/res/res_jabber.c
+@@ -350,7 +350,7 @@ static char *aji_cli_create_leafnode(str
+ static void aji_create_affiliations(struct aji_client *client, const char *node);
+ static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type);
+ static void aji_publish_device_state(struct aji_client *client, const char * device,
+-	const char *device_state);
++				     const char *device_state, unsigned int cachable);
+ static int aji_handle_pubsub_error(void *data, ikspak *pak);
+ static int aji_handle_pubsub_event(void *data, ikspak *pak);
+ static void aji_pubsub_subscribe(struct aji_client *client, const char *node);
+@@ -364,7 +364,7 @@ static void aji_publish_mwi(struct aji_c
+ static void aji_devstate_cb(const struct ast_event *ast_event, void *data);
+ static void aji_mwi_cb(const struct ast_event *ast_event, void *data);
+ static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
+-	const char *event_type);
++				       const char *event_type, unsigned int cachable);
+ /* No transports in this version */
+ /*
+ static int aji_create_transport(char *label, struct aji_client *client);
+@@ -3198,6 +3198,7 @@ static void aji_devstate_cb(const struct
+ {
+ 	const char *device;
+ 	const char *device_state;
++	unsigned int cachable;
+ 	struct aji_client *client;
+ 	if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID)))
+ 	{
+@@ -3209,7 +3210,8 @@ static void aji_devstate_cb(const struct
+ 	client = ASTOBJ_REF((struct aji_client *) data);
+ 	device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
+ 	device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
+-	aji_publish_device_state(client, device, device_state);
++	cachable = ast_event_get_ie_uint(ast_event, AST_EVENT_IE_CACHABLE);
++	aji_publish_device_state(client, device, device_state, cachable);
+ 	ASTOBJ_UNREF(client, ast_aji_client_destroy);
+ }
+ 
+@@ -3249,11 +3251,13 @@ static void aji_init_event_distribution(
+  */
+ static int aji_handle_pubsub_event(void *data, ikspak *pak)
+ {
+-	char *item_id, *device_state, *context;
++	char *item_id, *device_state, *context, *cachable_str;
+ 	int oldmsgs, newmsgs;
+ 	iks *item, *item_content;
+ 	struct ast_eid pubsub_eid;
+ 	struct ast_event *event;
++	unsigned int cachable = AST_DEVSTATE_CACHABLE;
++
+ 	item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
+ 	if (!item) {
+ 		ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
+@@ -3268,11 +3272,14 @@ static int aji_handle_pubsub_event(void
+ 	}
+ 	if (!strcasecmp(iks_name(item_content), "state")) {
+ 		device_state = iks_find_cdata(item, "state");
++		if ((cachable_str = iks_find_cdata(item, "cachable"))) {
++			sscanf(cachable_str, "%30d", &cachable);
++		}
+ 		if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
+-			AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
+-			AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
+-			AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
+-			AST_EVENT_IE_END))) {
++					    AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
++					    AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
++					    AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
++					    AST_EVENT_IE_END))) {
+ 			return IKS_FILTER_EAT;
+ 		}
+ 	} else if (!strcasecmp(iks_name(item_content), "mailbox")) {
+@@ -3292,7 +3299,13 @@ static int aji_handle_pubsub_event(void
+ 			iks_name(item_content));
+ 		return IKS_FILTER_EAT;
+ 	}
+-	ast_event_queue_and_cache(event);
++
++	if (cachable == AST_DEVSTATE_CACHABLE) {
++		ast_event_queue_and_cache(event);
++	} else {
++		ast_event_queue(event);
++	}
++
+ 	return IKS_FILTER_EAT;
+ }
+ 
+@@ -3367,7 +3380,7 @@ static void aji_pubsub_subscribe(struct
+  * \return iks *
+  */
+ static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
+-	const char *event_type)
++				       const char *event_type, unsigned int cachable)
+ {
+ 	iks *request = aji_pubsub_iq_create(client, "set");
+ 	iks *pubsub, *publish, *item;
+@@ -3381,8 +3394,24 @@ static iks* aji_build_publish_skeleton(s
+ 	}
+ 	item = iks_insert(publish, "item");
+ 	iks_insert_attrib(item, "id", node);
+-	return item;
+ 
++	if (cachable == AST_DEVSTATE_NOT_CACHABLE) {
++		iks *options, *x, *field_form_type, *field_persist;
++
++		options = iks_insert(pubsub, "publish-options");
++		x = iks_insert(options, "x");
++		iks_insert_attrib(x, "xmlns", "jabber:x:data");
++		iks_insert_attrib(x, "type", "submit");
++		field_form_type = iks_insert(x, "field");
++		iks_insert_attrib(field_form_type, "var", "FORM_TYPE");
++		iks_insert_attrib(field_form_type, "type", "hidden");
++		iks_insert_cdata(iks_insert(field_form_type, "value"), "http://jabber.org/protocol/pubsub#publish-options";, 0);
++		field_persist = iks_insert(x, "field");
++		iks_insert_attrib(field_persist, "var", "pubsub#persist_items");
++		iks_insert_cdata(iks_insert(field_persist, "value"), "0", 1);
++	}
++
++	return item;
+ }
+ 
+ /*!
+@@ -3393,11 +3422,11 @@ static iks* aji_build_publish_skeleton(s
+  * \return void
+  */
+ static void aji_publish_device_state(struct aji_client *client, const char *device,
+-	const char *device_state)
++				     const char *device_state, unsigned int cachable)
+ {
+-	iks *request = aji_build_publish_skeleton(client, device, "device_state");
++	iks *request = aji_build_publish_skeleton(client, device, "device_state", cachable);
+ 	iks *state;
+-	char eid_str[20];
++	char eid_str[20], cachable_str[2];
+ 	if (ast_test_flag(&pubsubflags, AJI_PUBSUB_AUTOCREATE)) {
+ 		if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
+ 			aji_create_pubsub_node(client, "leaf", device, "device_state");
+@@ -3409,6 +3438,8 @@ static void aji_publish_device_state(str
+ 	state = iks_insert(request, "state");
+ 	iks_insert_attrib(state, "xmlns", "http://asterisk.org";);
+ 	iks_insert_attrib(state, "eid", eid_str);
++	snprintf(cachable_str, sizeof(cachable_str), "%u", cachable);
++	iks_insert_attrib(state, "cachable", cachable_str);
+ 	iks_insert_cdata(state, device_state, strlen(device_state));
+ 	ast_aji_send(client, iks_root(request));
+ 	iks_delete(request);
+@@ -3428,7 +3459,7 @@ static void aji_publish_mwi(struct aji_c
+ 	char eid_str[20];
+ 	iks *mailbox_node, *request;
+ 	snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
+-	request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting");
++	request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting", 1);
+ 	ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
+ 	mailbox_node = iks_insert(request, "mailbox");
+ 	iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org";);
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-002 asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-002
--- asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-002	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-002	2013-03-28 09:44:44.000000000 +0200
@@ -0,0 +1,55 @@
+From: Matthew Jordan <mjordan@digium.com>
+Date: Wed, 27 Mar 2013 14:35:11 +0000
+Subject: AST-2013-002: Prevent denial of service in HTTP server
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=383976
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-20967
+CVE: CVE-2013-2686
+
+AST-2012-014, fixed in January of this year, contained a fix for Asterisk's
+HTTP server for a remotely-triggered crash. While the fix put in place fixed
+the possibility for the crash to be triggered, a denial of service vector still
+exists with that solution if an attacker sends one or more HTTP POST requests
+with very large Content-Length values. This patch resolves this by capping
+the Content-Length at 1024 bytes. Any attempt to send an HTTP POST with
+Content-Length greater than this cap will not result in any memory allocation.
+The POST will be responded to with an HTTP 413 "Request Entity Too Large"
+response.
+
+This issue was reported by Christoph Hebeisen of TELUS Security Labs
+
+See Also: http://downloads.asterisk.org/pub/security/AST-2013-002.html
+
+---
+ main/http.c |    9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/main/http.c b/main/http.c
+index 1b5f2b6..4b73acb 100644
+--- a/main/http.c
++++ b/main/http.c
+@@ -612,6 +612,8 @@ static void http_decode(char *s)
+ 	ast_uri_decode(s);
+ }
+ 
++#define MAX_POST_CONTENT 1025
++
+ /*
+  * get post variables from client Request Entity-Body, if content type is
+  * application/x-www-form-urlencoded
+@@ -644,6 +646,13 @@ struct ast_variable *ast_http_get_post_vars(
+ 		return NULL;
+ 	}
+ 
++	if (content_length > MAX_POST_CONTENT - 1) {
++		ast_log(LOG_WARNING, "Excessively long HTTP content. %d is greater than our max of %d\n",
++				content_length, MAX_POST_CONTENT);
++		ast_http_send(ser, AST_HTTP_POST, 413, "Request Entity Too Large", NULL, NULL, 0, 0);
++		return NULL;
++	}
++
+ 	buf = ast_malloc(content_length + 1);
+ 	if (!buf) {
+ 		return NULL;
+-- 
+1.7.10.4
+
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-003 asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-003
--- asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-003	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-003	2013-04-06 14:00:38.000000000 +0300
@@ -0,0 +1,370 @@
+From: Matthew Jordan <mjordan@digium.com>
+Date: Wed, 27 Mar 2013 14:53:13 +0000
+Subject: AST-2013-003: Prevent username disclosure in SIP channel driver
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-21013
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=383981
+CVE: CVE-2013-2264
+
+When authenticating a SIP request with alwaysauthreject enabled, allowguest
+disabled, and autocreatepeer disabled, Asterisk discloses whether a user
+exists for INVITE, SUBSCRIBE, and REGISTER transactions in multiple ways. The
+information is disclosed when:
+ * A "407 Proxy Authentication Required" response is sent instead of a
+   "401 Unauthorized" response
+ * The presence or absence of additional tags occurs at the end of "403
+   Forbidden" (such as "(Bad Auth)")
+ * A "401 Unauthorized" response is sent instead of "403 Forbidden" response
+   after a retransmission
+ * Retransmission are sent when a matching peer did not exist, but not when a
+   matching peer did exist.
+
+This patch resolves these various vectors by ensuring that the responses sent
+in all scenarios is the same, regardless of the presence of a matching peer.
+
+This issue was reported by Walter Doekes, OSSO B.V. A substantial portion of
+the testing and the solution to this problem was done by Walter as well - a
+huge thanks to his tireless efforts in finding all the ways in which this
+setting didn't work, providing automated tests, and working with Kinsey on
+getting this fixed.
+
+Patch slightly adapted due to irrelevant changes in r367362.
+
+See Also: http://downloads.asterisk.org/pub/security/AST-2013-003.html
+
+---
+ channels/chan_sip.c        |  128 ++++++++++++++++++++++++++++----------------
+ channels/sip/include/sip.h |    1 -
+ 2 files changed, 83 insertions(+), 46 deletions(-)
+
+--- a/channels/chan_sip.c
++++ b/channels/chan_sip.c
+@@ -1110,6 +1110,11 @@ static struct ao2_container *threadt;
+ static struct ao2_container *peers;
+ static struct ao2_container *peers_by_ip;
+ 
++/*! \brief  A bogus peer, to be used when authentication should fail */
++static struct sip_peer *bogus_peer;
++/*! \brief  We can recognise the bogus peer by this invalid MD5 hash */
++#define BOGUS_PEER_MD5SECRET "intentionally_invalid_md5_string"
++
+ /*! \brief  The register list: Other SIP proxies we register with and receive calls from */
+ static struct ast_register_list {
+ 	ASTOBJ_CONTAINER_COMPONENTS(struct sip_registry);
+@@ -1250,7 +1255,7 @@ static int transmit_response_with_unsupp
+ static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale);
+ static int transmit_provisional_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, int with_sdp);
+ static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
+-static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable);
++static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable);
+ static int transmit_request(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch);
+ static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch);
+ static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_type publish_type, const char * const explicit_uri);
+@@ -14621,6 +14626,7 @@ static enum check_auth_result check_auth
+ 	char a1_hash[256];
+ 	char resp_hash[256]="";
+ 	char *c;
++	int is_bogus_peer = 0;
+ 	int  wrongnonce = FALSE;
+ 	int  good_response;
+ 	const char *usednonce = p->randdata;
+@@ -14715,8 +14721,14 @@ static enum check_auth_result check_auth
+ 		}
+ 	}
+ 
++	/* We cannot rely on the bogus_peer having a bad md5 value. Someone could
++	 * use it to construct valid auth. */
++	if (md5secret && strcmp(md5secret, BOGUS_PEER_MD5SECRET) == 0) {
++		is_bogus_peer = 1;
++	}
++
+ 	/* Verify that digest username matches  the username we auth as */
+-	if (strcmp(username, keys[K_USER].s)) {
++	if (strcmp(username, keys[K_USER].s) && !is_bogus_peer) {
+ 		ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n",
+ 			username, keys[K_USER].s);
+ 		/* Oops, we're trying something here */
+@@ -14755,7 +14767,8 @@ static enum check_auth_result check_auth
+ 	}
+ 
+ 	good_response = keys[K_RESP].s &&
+-			!strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash));
++			!strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash)) &&
++			!is_bogus_peer; /* lastly, check that the peer isn't the fake peer */
+ 	if (wrongnonce) {
+ 		if (good_response) {
+ 			if (sipdebug)
+@@ -14899,7 +14912,7 @@ static int cb_extensionstate(char *conte
+ /*! \brief Send a fake 401 Unauthorized response when the administrator
+   wants to hide the names of local devices  from fishers
+  */
+-static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable)
++static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable)
+ {
+ 	/* We have to emulate EXACTLY what we'd get with a good peer
+ 	 * and a bad password, or else we leak information. */
+@@ -14938,13 +14951,13 @@ static void transmit_fake_auth_response(
+ 	}
+ 
+ 	if (!(buf = ast_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN))) {
+-		transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
++		__transmit_response(p, "403 Forbidden", &p->initreq, reliable);
+ 		return;
+ 	}
+ 
+ 	/* Make a copy of the response and parse it */
+ 	if (ast_str_set(&buf, 0, "%s", authtoken) == AST_DYNSTR_BUILD_FAILED) {
+-		transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
++		__transmit_response(p, "403 Forbidden", &p->initreq, reliable);
+ 		return;
+ 	}
+ 
+@@ -14982,7 +14995,7 @@ static void transmit_fake_auth_response(
+ 		/* Schedule auto destroy in 32 seconds */
+ 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ 	} else {
+-		transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
++		__transmit_response(p, "403 Forbidden", &p->initreq, reliable);
+ 	}
+ }
+ 
+@@ -15077,7 +15090,7 @@ static enum check_auth_result register_v
+ 	if (!AST_LIST_EMPTY(&domain_list)) {
+ 		if (!check_sip_domain(domain, NULL, 0)) {
+ 			if (sip_cfg.alwaysauthreject) {
+-				transmit_fake_auth_response(p, SIP_REGISTER, &p->initreq, XMIT_UNRELIABLE);
++				transmit_fake_auth_response(p, &p->initreq, XMIT_UNRELIABLE);
+ 			} else {
+ 				transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
+ 			}
+@@ -15104,6 +15117,13 @@ static enum check_auth_result register_v
+ 	}
+ 	peer = find_peer(name, NULL, TRUE, FINDPEERS, FALSE, 0);
+ 
++	/* If we don't want username disclosure, use the bogus_peer when a user
++	 * is not found. */
++	if (!peer && sip_cfg.alwaysauthreject && !sip_cfg.autocreatepeer) {
++		peer = bogus_peer;
++		ref_peer(peer, "register_verify: ref the bogus_peer");
++	}
++
+ 	if (!(peer && ast_apply_ha(peer->ha, addr))) {
+ 		/* Peer fails ACL check */
+ 		if (peer) {
+@@ -15183,7 +15203,7 @@ static enum check_auth_result register_v
+ 			switch (parse_register_contact(p, peer, req)) {
+ 			case PARSE_REGISTER_DENIED:
+ 				ast_log(LOG_WARNING, "Registration denied because of contact ACL\n");
+-				transmit_response_with_date(p, "403 Forbidden (ACL)", req);
++				transmit_response_with_date(p, "403 Forbidden", req);
+ 				peer->lastmsgssent = -1;
+ 				res = 0;
+ 				break;
+@@ -15218,7 +15238,7 @@ static enum check_auth_result register_v
+ 		switch (res) {
+ 		case AUTH_SECRET_FAILED:
+ 			/* Wrong password in authentication. Go away, don't try again until you fixed it */
+-			transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
++			transmit_response(p, "403 Forbidden", &p->initreq);
+ 			if (global_authfailureevents) {
+ 				const char *peer_addr = ast_strdupa(ast_sockaddr_stringify_addr(addr));
+ 				const char *peer_port = ast_strdupa(ast_sockaddr_stringify_port(addr));
+@@ -15241,7 +15261,7 @@ static enum check_auth_result register_v
+ 		case AUTH_PEER_NOT_DYNAMIC:
+ 		case AUTH_ACL_FAILED:
+ 			if (sip_cfg.alwaysauthreject) {
+-				transmit_fake_auth_response(p, SIP_REGISTER, &p->initreq, XMIT_UNRELIABLE);
++				transmit_fake_auth_response(p, &p->initreq, XMIT_UNRELIABLE);
+ 				if (global_authfailureevents) {
+ 					const char *peer_addr = ast_strdupa(ast_sockaddr_stringify_addr(addr));
+ 					const char *peer_port = ast_strdupa(ast_sockaddr_stringify_port(addr));
+@@ -16242,7 +16262,19 @@ static enum check_auth_result check_peer
+ 			ast_verbose("No matching peer for '%s' from '%s'\n",
+ 				of, ast_sockaddr_stringify(&p->recv));
+ 		}
+-		return AUTH_DONT_KNOW;
++
++		/* If you don't mind, we can return 404s for devices that do
++		 * not exist: username disclosure. If we allow guests, there
++		 * is no way around that. */
++		if (sip_cfg.allowguest || !sip_cfg.alwaysauthreject) {
++			return AUTH_DONT_KNOW;
++		}
++
++		/* If you do mind, we use a peer that will never authenticate.
++		 * This ensures that we follow the same code path as regular
++		 * auth: less chance for username disclosure. */
++		peer = bogus_peer;
++		ref_peer(peer, "ref_peer: check_peer_ok: must ref bogus_peer so unreffing it does not fail");
+ 	}
+ 
+ 	if (!ast_apply_ha(peer->ha, addr)) {
+@@ -16250,9 +16282,10 @@ static enum check_auth_result check_peer
+ 		unref_peer(peer, "unref_peer: check_peer_ok: from find_peer call, early return of AUTH_ACL_FAILED");
+ 		return AUTH_ACL_FAILED;
+ 	}
+-	if (debug)
++	if (debug && peer != bogus_peer) {
+ 		ast_verbose("Found peer '%s' for '%s' from %s\n",
+ 			peer->name, of, ast_sockaddr_stringify(&p->recv));
++	}
+ 
+ 	/* XXX what about p->prefs = peer->prefs; ? */
+ 	/* Set Frame packetization */
+@@ -16517,8 +16550,6 @@ static enum check_auth_result check_user
+ 		} else {
+ 			res = AUTH_RTP_FAILED;
+ 		}
+-	} else if (sip_cfg.alwaysauthreject) {
+-		res = AUTH_FAKE_AUTH; /* reject with fake authorization request */
+ 	} else {
+ 		res = AUTH_SECRET_FAILED; /* we don't want any guests, authentication will fail */
+ 	}
+@@ -22225,13 +22256,8 @@ static int handle_request_options(struct
+ 			return 0;
+ 		}
+ 		if (res < 0) { /* Something failed in authentication */
+-			if (res == AUTH_FAKE_AUTH) {
+-				ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From"));
+-				transmit_fake_auth_response(p, SIP_OPTIONS, req, XMIT_UNRELIABLE);
+-			} else {
+-				ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
+-				transmit_response(p, "403 Forbidden", req);
+-			}
++			ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
++			transmit_response(p, "403 Forbidden", req);
+ 			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ 			return 0;
+ 		}
+@@ -22896,13 +22922,8 @@ static int handle_request_invite(struct
+ 			goto request_invite_cleanup;
+ 		}
+ 		if (res < 0) { /* Something failed in authentication */
+-			if (res == AUTH_FAKE_AUTH) {
+-				ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From"));
+-				transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE);
+-			} else {
+-				ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
+-				transmit_response_reliable(p, "403 Forbidden", req);
+-			}
++			ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
++			transmit_response_reliable(p, "403 Forbidden", req);
+ 			p->invitestate = INV_COMPLETED;
+ 			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ 			res = 0;
+@@ -24712,18 +24733,13 @@ static int handle_request_publish(struct
+ 		return -1;
+ 	}
+ 
+-	auth_result = check_user(p, req, SIP_PUBLISH, uri, XMIT_RELIABLE, addr);
++	auth_result = check_user(p, req, SIP_PUBLISH, uri, XMIT_UNRELIABLE, addr);
+ 	if (auth_result == AUTH_CHALLENGE_SENT) {
+ 		p->lastinvite = seqno;
+ 		return 0;
+ 	} else if (auth_result < 0) {
+-		if (auth_result == AUTH_FAKE_AUTH) {
+-			ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From"));
+-			transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE);
+-		} else {
+-			ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
+-			transmit_response_reliable(p, "403 Forbidden", req);
+-		}
++		ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
++		transmit_response(p, "403 Forbidden", req);
+ 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ 		ast_string_field_set(p, theirtag, NULL);
+ 		return 0;
+@@ -24938,19 +24954,14 @@ static int handle_request_subscribe(stru
+ 	 * use if !req->ignore, because then we'll end up sending
+ 	 * a 200 OK if someone retransmits without sending auth */
+ 	if (p->subscribed == NONE || resubscribe) {
+-		res = check_user_full(p, req, SIP_SUBSCRIBE, e, 0, addr, &authpeer);
++		res = check_user_full(p, req, SIP_SUBSCRIBE, e, XMIT_UNRELIABLE, addr, &authpeer);
+ 
+ 		/* if an authentication response was sent, we are done here */
+ 		if (res == AUTH_CHALLENGE_SENT)	/* authpeer = NULL here */
+ 			return 0;
+ 		if (res != AUTH_SUCCESSFUL) {
+-			if (res == AUTH_FAKE_AUTH) {
+-				ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From"));
+-				transmit_fake_auth_response(p, SIP_SUBSCRIBE, req, XMIT_UNRELIABLE);
+-			} else {
+-				ast_log(LOG_NOTICE, "Failed to authenticate device %s for SUBSCRIBE\n", get_header(req, "From"));
+-				transmit_response_reliable(p, "403 Forbidden", req);
+-			}
++			ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
++			transmit_response(p, "403 Forbidden", req);
+ 
+ 			pvt_set_needdestroy(p, "authentication failed");
+ 			return 0;
+@@ -29988,6 +29999,7 @@ static int sip_do_reload(enum channelrel
+ /*! \brief Force reload of module from cli */
+ static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+ {
++	static struct sip_peer *tmp_peer, *new_peer;
+ 	
+ 	switch (cmd) {
+ 	case CLI_INIT:
+@@ -30010,6 +30022,18 @@ static char *sip_reload(struct ast_cli_e
+ 	ast_mutex_unlock(&sip_reload_lock);
+ 	restart_monitor();
+ 
++	tmp_peer = bogus_peer;
++	/* Create new bogus peer possibly with new global settings. */
++	if ((new_peer = temp_peer("(bogus_peer)"))) {
++		ast_string_field_set(new_peer, md5secret, BOGUS_PEER_MD5SECRET);
++		ast_clear_flag(&new_peer->flags[0], SIP_INSECURE);
++		bogus_peer = new_peer;
++		ao2_t_ref(tmp_peer, -1, "unref the old bogus_peer during reload");
++	} else {
++		ast_log(LOG_ERROR, "Could not update the fake authentication peer.\n");
++		/* You probably have bigger (memory?) issues to worry about though.. */
++	}
++
+ 	return CLI_SUCCESS;
+ }
+ 
+@@ -31130,6 +31154,17 @@ static int load_module(void)
+ 		return AST_MODULE_LOAD_DECLINE;
+ 	}
+ 
++	/* Initialize bogus peer. Can be done first after reload_config() */
++	if (!(bogus_peer = temp_peer("(bogus_peer)"))) {
++		ast_log(LOG_ERROR, "Unable to create bogus_peer for authentication\n");
++		io_context_destroy(io);
++		sched_context_destroy(sched);
++		return AST_MODULE_LOAD_FAILURE;
++	}
++	/* Make sure the auth will always fail. */
++	ast_string_field_set(bogus_peer, md5secret, BOGUS_PEER_MD5SECRET);
++	ast_clear_flag(&bogus_peer->flags[0], SIP_INSECURE);
++
+ 	/* Prepare the version that does not require DTMF BEGIN frames.
+ 	 * We need to use tricks such as memcpy and casts because the variable
+ 	 * has const fields.
+@@ -31140,6 +31175,7 @@ static int load_module(void)
+ 	/* Make sure we can register our sip channel type */
+ 	if (ast_channel_register(&sip_tech)) {
+ 		ast_log(LOG_ERROR, "Unable to register channel type 'SIP'\n");
++		ao2_t_ref(bogus_peer, -1, "unref the bogus_peer");
+ 		io_context_destroy(io);
+ 		sched_context_destroy(sched);
+ 		return AST_MODULE_LOAD_FAILURE;
+@@ -31378,6 +31414,8 @@ static int unload_module(void)
+ 		ast_debug(2, "TCP/TLS thread container did not become empty :(\n");
+ 	}
+ 
++	ao2_t_ref(bogus_peer, -1, "unref the bogus_peer");
++
+ 	ao2_t_ref(peers, -1, "unref the peers table");
+ 	ao2_t_ref(peers_by_ip, -1, "unref the peers_by_ip table");
+ 	ao2_t_ref(dialogs, -1, "unref the dialogs table");
+--- a/channels/sip/include/sip.h
++++ b/channels/sip/include/sip.h
+@@ -470,7 +470,6 @@ enum check_auth_result {
+ 	AUTH_SECRET_FAILED = -1,
+ 	AUTH_USERNAME_MISMATCH = -2,
+ 	AUTH_NOT_FOUND = -3,	/*!< returned by register_verify */
+-	AUTH_FAKE_AUTH = -4,
+ 	AUTH_UNKNOWN_DOMAIN = -5,
+ 	AUTH_PEER_NOT_DYNAMIC = -6,
+ 	AUTH_ACL_FAILED = -7,
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/bluetooth_bind asterisk-1.8.13.1~dfsg/debian/patches/bluetooth_bind
--- asterisk-1.8.13.1~dfsg/debian/patches/bluetooth_bind	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/bluetooth_bind	2013-04-06 14:12:50.000000000 +0300
@@ -0,0 +1,30 @@
+From: Matthew Jordan <mjordan@digium.com>
+Date: Thu, 17 Jan 2013 02:28:31 +0000
+Subject: Fix issue where chan_mobile fails to bind to first available port
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-16357
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=379342
+
+Per the bluez API, in order to bind to the first available port, the rc_channel
+field of the socket addressing structure used to bind the socket should be set
+to 0. Previously, Asterisk had set the rc_channel field set to 1, causing it
+to connect to whatever happens to be on port 1.
+
+We could probably not explicitly set rc_channel to 0 since we memset the struct
+earlier, but explicitly setting it will hopefully prevent someone from coming
+in and setting it to some explicit port in the future.
+
+---
+ addons/chan_mobile.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/addons/chan_mobile.c
++++ b/addons/chan_mobile.c
+@@ -1370,7 +1370,7 @@ static int rfcomm_connect(bdaddr_t src,
+ 	memset(&addr, 0, sizeof(addr));
+ 	addr.rc_family = AF_BLUETOOTH;
+ 	bacpy(&addr.rc_bdaddr, &src);
+-	addr.rc_channel = (uint8_t) 1;
++	addr.rc_channel = (uint8_t) 0;
+ 	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ 		ast_debug(1, "bind() failed (%d).\n", errno);
+ 		close(s);
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/fix-sip-tcp-no-FILE asterisk-1.8.13.1~dfsg/debian/patches/fix-sip-tcp-no-FILE
--- asterisk-1.8.13.1~dfsg/debian/patches/fix-sip-tcp-no-FILE	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/fix-sip-tcp-no-FILE	2013-01-08 02:20:03.000000000 +0200
@@ -0,0 +1,1096 @@
+From: Mark Michelson <mmichelson@digium.com>
+Date: Fri, 12 Oct 2012 15:57:40 +0000
+Subject: Do not use a FILE handle when doing SIP TCP reads.
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=374905
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-20212
+
+This is used to solve an issue where a poll on a file
+descriptor does not necessarily correspond to the readiness
+of a FILE handle to be read.
+
+This change makes it so that for TCP connections, we do a
+recv() on the file descriptor instead.
+
+Because TCP does not guarantee that an entire message or even
+just one single message will arrive during a read, a loop has
+been introduced to ensure that we only attempt to handle a
+single message at a time. The tcptls_session_instance structure
+has also had an overflow buffer added to it so that if more
+than one TCP message arrives in one go, there is a place to
+throw the excess.
+
+Huge thanks goes out to Walter Doekes for doing extensive review
+on this change and finding edge cases where code could fail.
+
+reported by Phil Ciccone
+
+Review: https://reviewboard.asterisk.org/r/2123
+
+---
+ channels/chan_sip.c       |  968 +++++++++++++++++++++++++++++++++++++++------
+ include/asterisk/tcptls.h |    6 +
+ main/tcptls.c             |    3 +
+ 3 files changed, 861 insertions(+), 116 deletions(-)
+
+--- a/channels/chan_sip.c
++++ b/channels/chan_sip.c
+@@ -2496,12 +2496,363 @@ static int sip_check_authtimeout(time_t
+ 	return timeout;
+ }
+ 
++/*!
++ * \brief Read a SIP request or response from a TLS connection
++ *
++ * Because TLS operations are hidden from view via a FILE handle, the
++ * logic for reading data is a bit complex, and we have to make periodic
++ * checks to be sure we aren't taking too long to perform the necessary
++ * action.
++ *
++ * \todo XXX This should be altered in the future not to use a FILE pointer
++ *
++ * \param req The request structure to fill in
++ * \param tcptls_session The TLS connection on which the data is being received
++ * \param authenticated A flag indicating whether authentication has occurred yet.
++ *        This is only relevant in a server role.
++ * \param start The time at which we started attempting to read data. Used in
++ *        determining if there has been a timeout.
++ * \param me Thread info. Used as a means of determining if the session needs to be stoppped.
++ * \retval -1 Failed to read data
++ * \retval 0 Succeeded in reading data
++ */
++static int sip_tls_read(struct sip_request *req, struct ast_tcptls_session_instance *tcptls_session, int authenticated, time_t start, struct sip_threadinfo *me)
++{
++	int res, content_length, after_poll = 1, need_poll = 1;
++	struct sip_request reqcpy = { 0, };
++	char buf[1024] = "";
++	int timeout = -1;
++
++	/* Read in headers one line at a time */
++	while (ast_str_strlen(req->data) < 4 || strncmp(REQ_OFFSET_TO_STR(req, data->used - 4), "\r\n\r\n", 4)) {
++		if (!tcptls_session->client && !authenticated) {
++			if ((timeout = sip_check_authtimeout(start)) < 0) {
++				ast_debug(2, "SIP SSL server failed to determine authentication timeout\n");
++				return -1;
++			}
++
++			if (timeout == 0) {
++				ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
++				return -1;
++			}
++		} else {
++			timeout = -1;
++		}
++
++		/* special polling behavior is required for TLS
++		 * sockets because of the buffering done in the
++		 * TLS layer */
++		if (need_poll) {
++			need_poll = 0;
++			after_poll = 1;
++			res = ast_wait_for_input(tcptls_session->fd, timeout);
++			if (res < 0) {
++				ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res);
++				return -1;
++			} else if (res == 0) {
++				/* timeout */
++				ast_debug(2, "SIP TCP server timed out\n");
++				return -1;
++			}
++		}
++
++		ast_mutex_lock(&tcptls_session->lock);
++		if (!fgets(buf, sizeof(buf), tcptls_session->f)) {
++			ast_mutex_unlock(&tcptls_session->lock);
++			if (after_poll) {
++				return -1;
++			} else {
++				need_poll = 1;
++				continue;
++			}
++		}
++		ast_mutex_unlock(&tcptls_session->lock);
++		after_poll = 0;
++		if (me->stop) {
++			return -1;
++		}
++		ast_str_append(&req->data, 0, "%s", buf);
++	}
++	copy_request(&reqcpy, req);
++	parse_request(&reqcpy);
++	/* In order to know how much to read, we need the content-length header */
++	if (sscanf(get_header(&reqcpy, "Content-Length"), "%30d", &content_length)) {
++		while (content_length > 0) {
++			size_t bytes_read;
++			if (!tcptls_session->client && !authenticated) {
++				if ((timeout = sip_check_authtimeout(start)) < 0) {
++					return -1;
++				}
++
++				if (timeout == 0) {
++					ast_debug(2, "SIP SSL server timed out\n");
++					return -1;
++				}
++			} else {
++				timeout = -1;
++			}
++
++			if (need_poll) {
++				need_poll = 0;
++				after_poll = 1;
++				res = ast_wait_for_input(tcptls_session->fd, timeout);
++				if (res < 0) {
++					ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res);
++					return -1;
++				} else if (res == 0) {
++					/* timeout */
++					ast_debug(2, "SIP TCP server timed out\n");
++					return -1;
++				}
++			}
++
++			ast_mutex_lock(&tcptls_session->lock);
++			if (!(bytes_read = fread(buf, 1, MIN(sizeof(buf) - 1, content_length), tcptls_session->f))) {
++				ast_mutex_unlock(&tcptls_session->lock);
++				if (after_poll) {
++					return -1;
++				} else {
++					need_poll = 1;
++					continue;
++				}
++			}
++			buf[bytes_read] = '\0';
++			ast_mutex_unlock(&tcptls_session->lock);
++			after_poll = 0;
++			if (me->stop) {
++				return -1;
++			}
++			content_length -= strlen(buf);
++			ast_str_append(&req->data, 0, "%s", buf);
++		}
++	}
++	/*! \todo XXX If there's no Content-Length or if the content-length and what
++					we receive is not the same - we should generate an error */
++	return 0;
++}
++
++/*!
++ * \brief Indication of a TCP message's integrity
++ */
++enum message_integrity {
++	/*!
++	 * The message has an error in it with
++	 * regards to its Content-Length header
++	 */
++	MESSAGE_INVALID,
++	/*!
++	 * The message is incomplete
++	 */
++	MESSAGE_FRAGMENT,
++	/*!
++	 * The data contains a complete message
++	 * plus a fragment of another.
++	 */
++	MESSAGE_FRAGMENT_COMPLETE,
++	/*!
++	 * The message is complete
++	 */
++	MESSAGE_COMPLETE,
++};
++
++/*!
++ * \brief
++ * Get the content length from an unparsed SIP message
++ *
++ * \param message The unparsed SIP message headers
++ * \return The value of the Content-Length header or -1 if message is invalid
++ */
++static int read_raw_content_length(const char *message)
++{
++	char *end_of_line;
++	char *content_length_str;
++	char *l_str;
++	int content_length;
++	char *msg;
++
++	if (sip_cfg.pedanticsipchecking) {
++		struct ast_str *msg_copy = ast_str_create(strlen(message));
++		if (!msg_copy) {
++			return -1;
++		}
++		ast_str_set(&msg_copy, 0, "%s", message);
++		lws2sws(msg_copy);
++		msg = ast_strdupa(ast_str_buffer(msg_copy));
++		ast_free(msg_copy);
++	} else {
++		msg = ast_strdupa(message);
++	}
++
++	/* Let's find a Content-Length header */
++	content_length_str = strcasestr(msg, "\nContent-Length:");
++	if (!content_length_str && !(l_str = strcasestr(msg, "\nl:"))) {
++		/* RFC 3261 18.3
++		 * "In the case of stream-oriented transports such as TCP, the Content-
++		 *  Length header field indicates the size of the body.  The Content-
++		 *  Length header field MUST be used with stream oriented transports."
++		 */
++		return -1;
++	}
++	if (content_length_str) {
++		content_length_str += sizeof("\nContent-Length:");
++	} else if (l_str) {
++		content_length_str = l_str + sizeof("\nl:");
++	} else {
++		return -1;
++	}
++
++	end_of_line = strchr(content_length_str, '\n');
++
++	if (!end_of_line) {
++		return -1;
++	}
++
++	if (sscanf(content_length_str, "%30d", &content_length) == 1) {
++		return content_length;
++	}
++
++	return -1;
++}
++
++/*!
++ * \brief Check that a message received over TCP is a full message
++ *
++ * This will take the information read in and then determine if
++ * 1) The message is a full SIP request
++ * 2) The message is a partial SIP request
++ * 3) The message contains a full SIP request along with another partial request
++ * \param data The unparsed incoming SIP message.
++ * \param request The resulting request with extra fragments removed.
++ * \param overflow If the message contains more than a full request, this is the remainder of the message
++ * \return The resulting integrity of the message
++ */
++static enum message_integrity check_message_integrity(struct ast_str **request, struct ast_str **overflow)
++{
++	char *message = ast_strdupa(ast_str_buffer(*request));
++	char *body;
++	int content_length;
++	int body_len;
++	int message_len = strlen(message);
++
++	/* Important pieces to search for in a SIP request are \r\n\r\n. This
++	 * marks either
++	 * 1) The division between the headers and body
++	 * 2) The end of the SIP request
++	 */
++	body = strstr(message, "\r\n\r\n");
++	if (!body) {
++		/* This is clearly a partial message since we haven't reached an end
++		 * yet.
++		 */
++		return MESSAGE_FRAGMENT;
++	}
++	body += sizeof("\r\n\r\n") - 1;
++	body_len = strlen(body);
++
++	body[-1] = '\0';
++	content_length = read_raw_content_length(message);
++	body[-1] = '\n';
++
++	if (content_length < 0) {
++		return MESSAGE_INVALID;
++	} else if (content_length == 0) {
++		/* We've definitely received an entire message. We need
++		 * to check if there's also a fragment of another message
++		 * in addition.
++		 */
++		if (body_len == 0) {
++			return MESSAGE_COMPLETE;
++		} else {
++			ast_str_truncate(*request, message_len - body_len);
++			ast_str_append(overflow, 0, "%s", body);
++			return MESSAGE_FRAGMENT_COMPLETE;
++		}
++	}
++	/* Positive content length. Let's see what sort of
++	 * message body we're dealing with.
++	 */
++	if (body_len < content_length) {
++		/* We don't have the full message body yet */
++		return MESSAGE_FRAGMENT;
++	} else if (body_len > content_length) {
++		/* We have the full message plus a fragment of a further
++		 * message
++		 */
++		ast_str_truncate(*request, message_len - (body_len - content_length));
++		ast_str_append(overflow, 0, "%s", body + content_length);
++		return MESSAGE_FRAGMENT_COMPLETE;
++	} else {
++		/* Yay! Full message with no extra content */
++		return MESSAGE_COMPLETE;
++	}
++}
++
++/*!
++ * \brief Read SIP request or response from a TCP connection
++ *
++ * \param req The request structure to be filled in
++ * \param tcptls_session The TCP connection from which to read
++ * \retval -1 Failed to read data
++ * \retval 0 Successfully read data
++ */
++static int sip_tcp_read(struct sip_request *req, struct ast_tcptls_session_instance *tcptls_session,
++		int authenticated, time_t start)
++{
++	enum message_integrity message_integrity = MESSAGE_FRAGMENT;
++
++	while (message_integrity == MESSAGE_FRAGMENT) {
++		if (ast_str_strlen(tcptls_session->overflow_buf) == 0) {
++			char readbuf[4097];
++			int timeout;
++			int res;
++			if (!tcptls_session->client && !authenticated) {
++				if ((timeout = sip_check_authtimeout(start)) < 0) {
++					return -1;
++				}
++
++				if (timeout == 0) {
++					ast_debug(2, "SIP TCP server timed out\n");
++					return -1;
++				}
++			} else {
++				timeout = -1;
++			}
++			res = ast_wait_for_input(tcptls_session->fd, timeout);
++			if (res < 0) {
++				ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res);
++				return -1;
++			} else if (res == 0) {
++				ast_debug(2, "SIP TCP server timed out\n");
++				return -1;
++			}
++
++			res = recv(tcptls_session->fd, readbuf, sizeof(readbuf) - 1, 0);
++			if (res < 0) {
++				ast_debug(2, "SIP TCP server error when receiving data\n");
++				return -1;
++			} else if (res == 0) {
++				ast_debug(2, "SIP TCP server has shut down\n");
++				return -1;
++			}
++			readbuf[res] = '\0';
++			ast_str_append(&req->data, 0, "%s", readbuf);
++		} else {
++			ast_str_append(&req->data, 0, "%s", ast_str_buffer(tcptls_session->overflow_buf));
++			ast_str_reset(tcptls_session->overflow_buf);
++		}
++
++		message_integrity = check_message_integrity(&req->data, &tcptls_session->overflow_buf);
++	}
++
++	return 0;
++}
++
+ /*! \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 ast_tcptls_session_instance *tcptls_session)
+ {
+-	int res, cl, timeout = -1, authenticated = 0, flags, after_poll = 0, need_poll = 1;
++	int res, timeout = -1, authenticated = 0, flags;
+ 	time_t start;
+ 	struct sip_request req = { 0, } , reqcpy = { 0, };
+ 	struct sip_threadinfo *me = NULL;
+@@ -2601,21 +2952,23 @@ static void *_sip_tcp_helper_thread(stru
+ 			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;
++		if (ast_str_strlen(tcptls_session->overflow_buf) == 0) {
++			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,
+-		 * and writes from alert_pipe fd */
+-		if (fds[0].revents) { /* there is data on the socket to be read */
+-			after_poll = 1;
+-
++		/* 
++		 * handle the socket event, check for both reads from the socket fd or TCP overflow buffer,
++		 * and writes from alert_pipe fd.
++		 */
++		if (fds[0].revents || (ast_str_strlen(tcptls_session->overflow_buf) > 0)) { /* there is data on the socket to be read */
+ 			fds[0].revents = 0;
+ 
+ 			/* clear request structure */
+@@ -2640,110 +2993,15 @@ static void *_sip_tcp_helper_thread(stru
+ 			}
+ 			req.socket.fd = tcptls_session->fd;
+ 
+-			/* Read in headers one line at a time */
+-			while (ast_str_strlen(req.data) < 4 || strncmp(REQ_OFFSET_TO_STR(&req, data->used - 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;
+-				}
+-
+-				/* special polling behavior is required for TLS
+-				 * sockets because of the buffering done in the
+-				 * TLS layer */
+-				if (!tcptls_session->ssl || need_poll) {
+-					need_poll = 0;
+-					after_poll = 1;
+-					res = ast_wait_for_input(tcptls_session->fd, timeout);
+-					if (res < 0) {
+-						ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res);
+-						goto cleanup;
+-					} else if (res == 0) {
+-						/* timeout */
+-						ast_debug(2, "SIP TCP server timed out\n");
+-						goto cleanup;
+-					}
+-				}
+-
+-				ast_mutex_lock(&tcptls_session->lock);
+-				if (!fgets(buf, sizeof(buf), tcptls_session->f)) {
+-					ast_mutex_unlock(&tcptls_session->lock);
+-					if (after_poll) {
+-						goto cleanup;
+-					} else {
+-						need_poll = 1;
+-						continue;
+-					}
+-				}
+-				ast_mutex_unlock(&tcptls_session->lock);
+-				after_poll = 0;
+-				if (me->stop) {
+-					 goto cleanup;
+-				}
+-				ast_str_append(&req.data, 0, "%s", buf);
+-			}
+-			copy_request(&reqcpy, &req);
+-			parse_request(&reqcpy);
+-			/* In order to know how much to read, we need the content-length header */
+-			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\n", tcptls_session->ssl ? "SSL": "TCP");
+-							goto cleanup;
+-						}
+-					} else {
+-						timeout = -1;
+-					}
+-
+-					if (!tcptls_session->ssl || need_poll) {
+-						need_poll = 0;
+-						after_poll = 1;
+-						res = ast_wait_for_input(tcptls_session->fd, timeout);
+-						if (res < 0) {
+-							ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res);
+-							goto cleanup;
+-						} else if (res == 0) {
+-							/* timeout */
+-							ast_debug(2, "SIP TCP server timed out\n");
+-							goto cleanup;
+-						}
+-					}
++			if (tcptls_session->ssl) {
++				res = sip_tls_read(&req, tcptls_session, authenticated, start, me);
++			} else {
++				res = sip_tcp_read(&req, tcptls_session, authenticated, start);
++			}
+ 
+-					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);
+-						if (after_poll) {
+-							goto cleanup;
+-						} else {
+-							need_poll = 1;
+-							continue;
+-						}
+-					}
+-					buf[bytes_read] = '\0';
+-					ast_mutex_unlock(&tcptls_session->lock);
+-					after_poll = 0;
+-					if (me->stop) {
+-						goto cleanup;
+-					}
+-					cl -= strlen(buf);
+-					ast_str_append(&req.data, 0, "%s", buf);
+-				}
++			if (res < 0) {
++				goto cleanup;
+ 			}
+-			/*! \todo XXX If there's no Content-Length or if the content-length and what
+-					we receive is not the same - we should generate an error */
+ 
+ 			req.socket.tcptls_session = tcptls_session;
+ 			handle_request_do(&req, &tcptls_session->remote_address);
+@@ -30164,6 +30422,482 @@ AST_TEST_DEFINE(test_sip_peers_get)
+ 	return AST_TEST_PASS;
+ }
+ 
++/*!
++ * \brief Imitation TCP reception loop
++ *
++ * This imitates the logic used by SIP's TCP code. Its purpose
++ * is to either
++ * 1) Combine fragments into a single message
++ * 2) Break up combined messages into single messages
++ *
++ * \param fragments The message fragments. This simulates the data received on a TCP socket.
++ * \param num_fragments This indicates the number of fragments to receive
++ * \param overflow This is a place to stash extra data if more than one message is received
++ *        in a single fragment
++ * \param[out] messages The parsed messages are placed in this array
++ * \param[out] num_messages The number of messages that were parsed
++ * \param test Used for printing messages
++ * \retval 0 Success
++ * \retval -1 Failure
++ */
++static int mock_tcp_loop(char *fragments[], size_t num_fragments,
++		struct ast_str **overflow, char **messages, int *num_messages, struct ast_test* test)
++{
++	struct ast_str *req_data;
++	int i = 0;
++	int res = 0;
++
++	req_data = ast_str_create(128);
++	ast_str_reset(*overflow);
++
++	while (i < num_fragments || ast_str_strlen(*overflow) > 0) {
++		enum message_integrity message_integrity = MESSAGE_FRAGMENT;
++		ast_str_reset(req_data);
++		while (message_integrity == MESSAGE_FRAGMENT) {
++			if (ast_str_strlen(*overflow) > 0) {
++				ast_str_append(&req_data, 0, "%s", ast_str_buffer(*overflow));
++				ast_str_reset(*overflow);
++			} else {
++				ast_str_append(&req_data, 0, "%s", fragments[i++]);
++			}
++			message_integrity = check_message_integrity(&req_data, overflow);
++		}
++		if (strcmp(ast_str_buffer(req_data), messages[*num_messages])) {
++			ast_test_status_update(test, "Mismatch in SIP messages.\n");
++			ast_test_status_update(test, "Expected message:\n%s", messages[*num_messages]);
++			ast_test_status_update(test, "Parsed message:\n%s", ast_str_buffer(req_data));
++			res = -1;
++			goto end;
++		} else {
++			ast_test_status_update(test, "Successfully read message:\n%s", ast_str_buffer(req_data));
++		}
++		(*num_messages)++;
++	}
++
++end:
++	ast_free(req_data);
++	return res;
++};
++
++AST_TEST_DEFINE(test_tcp_message_fragmentation)
++{
++	/* Normal single message in one fragment */
++	char *normal[] = {
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"Content-Length: 130\r\n"
++		"\r\n"
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++	};
++
++	/* Single message in two fragments.
++	 * Fragments combine to make "normal"
++	 */
++	char *fragmented[] = {
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: ",
++
++		"70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"Content-Length: 130\r\n"
++		"\r\n"
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++	};
++	/* Single message in two fragments, divided precisely at the body
++	 * Fragments combine to make "normal"
++	 */
++	char *fragmented_body[] = {
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"Content-Length: 130\r\n"
++		"\r\n",
++
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++	};
++
++	/* Single message in three fragments
++	 * Fragments combine to make "normal"
++	 */
++	char *multi_fragment[] = {
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n",
++
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"Content-Length: 130\r\n"
++		"\r\n"
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4",
++
++		" 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++	};
++
++	/* Two messages in a single fragment
++	 * Fragments split into "multi_message_divided"
++	 */
++	char *multi_message[] = {
++		"SIP/2.0 100 Trying\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: <sip:bob@example.org:5060>\r\n"
++		"Content-Length: 0\r\n"
++		"\r\n"
++		"SIP/2.0 180 Ringing\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: <sip:bob@example.org:5060>\r\n"
++		"Content-Length: 0\r\n"
++		"\r\n"
++	};
++	char *multi_message_divided[] = {
++		"SIP/2.0 100 Trying\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: <sip:bob@example.org:5060>\r\n"
++		"Content-Length: 0\r\n"
++		"\r\n",
++
++		"SIP/2.0 180 Ringing\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: <sip:bob@example.org:5060>\r\n"
++		"Content-Length: 0\r\n"
++		"\r\n"
++	};
++	/* Two messages with bodies combined into one fragment
++	 * Fragments split into "multi_message_body_divided"
++	 */
++	char *multi_message_body[] = {
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"Content-Length: 130\r\n"
++		"\r\n"
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 2 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"Content-Length: 130\r\n"
++		"\r\n"
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++	};
++	char *multi_message_body_divided[] = {
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"Content-Length: 130\r\n"
++		"\r\n"
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n",
++
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 2 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"Content-Length: 130\r\n"
++		"\r\n"
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++	};
++
++	/* Two messages that appear in two fragments. Fragment
++	 * boundaries do not align with message boundaries.
++	 * Fragments combine to make "multi_message_divided"
++	 */
++	char *multi_message_in_fragments[] = {
++		"SIP/2.0 100 Trying\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVI",
++
++		"TE\r\n"
++		"Contact: <sip:bob@example.org:5060>\r\n"
++		"Content-Length: 0\r\n"
++		"\r\n"
++		"SIP/2.0 180 Ringing\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: <sip:bob@example.org:5060>\r\n"
++		"Content-Length: 0\r\n"
++		"\r\n"
++	};
++
++	/* Message with compact content-length header
++	 * Same as "normal" but with compact content-length header
++	 */
++	char *compact[] = {
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"l: 130\r\n"
++		"\r\n"
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++	};
++
++	/* Message with faux content-length headers
++	 * Same as "normal" but with extra fake content-length headers
++	 */
++	char *faux[] = {
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"DisContent-Length: 0\r\n"
++		"MalContent-Length: 60\r\n"
++		"Content-Length: 130\r\n"
++		"\r\n"
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++	};
++
++	/* Message with folded Content-Length header
++	 * Message is "normal" with Content-Length spread across three lines
++	 *
++	 * This is the test that requires pedantic=yes in order to pass
++	 */
++	char *folded[] = {
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"Content-Length: \t\r\n"
++		"\t \r\n"
++		" 130\t \r\n"
++		"\r\n"
++		"v=0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++	};
++
++	/* Message with compact Content-length header in message and
++	 * full Content-Length header in the body. Ensure that the header
++	 * in the message is read and that the one in the body is ignored
++	 */
++	char *cl_in_body[] = {
++		"INVITE sip:bob@example.org SIP/2.0\r\n"
++		"Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
++		"From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
++		"To: <sip:bob@example.org:5060>\r\n"
++		"Call-ID: 12345\r\n"
++		"CSeq: 1 INVITE\r\n"
++		"Contact: sip:127.0.0.1:5061\r\n"
++		"Max-Forwards: 70\r\n"
++		"Content-Type: application/sdp\r\n"
++		"l: 149\r\n"
++		"\r\n"
++		"v=0\r\n"
++		"Content-Length: 0\r\n"
++		"o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
++		"s=-\r\n"
++		"c=IN IP4 127.0.0.1\r\n"
++		"t=0 0\r\n"
++		"m=audio 10000 RTP/AVP 0\r\n"
++		"a=rtpmap:0 PCMU/8000\r\n"
++	};
++
++	struct ast_str *overflow;
++	struct {
++		char **fragments;
++		char **expected;
++		int num_expected;
++		const char *description;
++	} tests[] = {
++		{ normal, normal, 1, "normal" },
++		{ fragmented, normal, 1, "fragmented" },
++		{ fragmented_body, normal, 1, "fragmented_body" },
++		{ multi_fragment, normal, 1, "multi_fragment" },
++		{ multi_message, multi_message_divided, 2, "multi_message" },
++		{ multi_message_body, multi_message_body_divided, 2, "multi_message_body" },
++		{ multi_message_in_fragments, multi_message_divided, 2, "multi_message_in_fragments" },
++		{ compact, compact, 1, "compact" },
++		{ faux, faux, 1, "faux" },
++		{ folded, folded, 1, "folded" },
++		{ cl_in_body, cl_in_body, 1, "cl_in_body" },
++	};
++	int i;
++	enum ast_test_result_state res = AST_TEST_PASS;
++
++	switch (cmd) {
++		case TEST_INIT:
++			info->name = "sip_tcp_message_fragmentation";
++			info->category = "/main/sip/transport";
++			info->summary = "SIP TCP message fragmentation test";
++			info->description =
++				"Tests reception of different TCP messages that have been fragmented or"
++				"run together. This test mimicks the code that TCP reception uses.";
++			return AST_TEST_NOT_RUN;
++		case TEST_EXECUTE:
++			break;
++	}
++	if (!sip_cfg.pedanticsipchecking) {
++		ast_log(LOG_WARNING, "Not running test. Pedantic SIP checking is not enabled, so it is guaranteed to fail\n");
++		return AST_TEST_NOT_RUN;
++	}
++
++	overflow = ast_str_create(128);
++	if (!overflow) {
++		return AST_TEST_FAIL;
++	}
++	for (i = 0; i < ARRAY_LEN(tests); ++i) {
++		int num_messages = 0;
++		if (mock_tcp_loop(tests[i].fragments, ARRAY_LEN(tests[i].fragments),
++					&overflow, tests[i].expected, &num_messages, test)) {
++			ast_test_status_update(test, "Failed to parse message '%s'\n", tests[i].description);
++			res = AST_TEST_FAIL;
++			break;
++		}
++		if (num_messages != tests[i].num_expected) {
++			ast_test_status_update(test, "Did not receive the expected number of messages. "
++					"Expected %d but received %d\n", tests[i].num_expected, num_messages);
++			res = AST_TEST_FAIL;
++			break;
++		}
++	}
++	ast_free(overflow);
++	return res;
++}
++
+ #endif
+ 
+ #define DATA_EXPORT_SIP_PEER(MEMBER)				\
+@@ -30386,6 +31120,7 @@ static int load_module(void)
+ #ifdef TEST_FRAMEWORK
+ 	AST_TEST_REGISTER(test_sip_peers_get);
+ 	AST_TEST_REGISTER(test_sip_mwi_subscribe_parse);
++	AST_TEST_REGISTER(test_tcp_message_fragmentation);
+ #endif
+ 
+ 	/* Register AstData providers */
+@@ -30498,6 +31233,7 @@ static int unload_module(void)
+ #ifdef TEST_FRAMEWORK
+ 	AST_TEST_UNREGISTER(test_sip_peers_get);
+ 	AST_TEST_UNREGISTER(test_sip_mwi_subscribe_parse);
++	AST_TEST_UNREGISTER(test_tcp_message_fragmentation);
+ #endif
+ 	/* Unregister all the AstData providers */
+ 	ast_data_unregister(NULL);
+--- a/include/asterisk/tcptls.h
++++ b/include/asterisk/tcptls.h
+@@ -149,6 +149,12 @@ struct ast_tcptls_session_instance {
+ 	struct ast_tcptls_session_args *parent;
+ 	/*! XXX Why do we still use this lock when this struct is allocated as an ao2 object which has its own lock? */
+ 	ast_mutex_t lock;
++	/* Sometimes, when an entity reads TCP data, multiple
++	 * logical messages might be read at the same time. In such
++	 * a circumstance, there needs to be a place to stash the
++	 * extra data.
++	 */
++	struct ast_str *overflow_buf;
+ };
+ 
+ #if defined(HAVE_FUNOPEN)
+--- a/main/tcptls.c
++++ b/main/tcptls.c
+@@ -130,6 +130,7 @@ HOOK_T ast_tcptls_server_write(struct as
+ static void session_instance_destructor(void *obj)
+ {
+ 	struct ast_tcptls_session_instance *i = obj;
++	ast_free(i->overflow_buf);
+ 	ast_mutex_destroy(&i->lock);
+ }
+ 
+@@ -280,6 +281,7 @@ void *ast_tcptls_server_root(void *data)
+ 		}
+ 
+ 		ast_mutex_init(&tcptls_session->lock);
++		tcptls_session->overflow_buf = ast_str_create(128);
+ 
+ 		flags = fcntl(fd, F_GETFL);
+ 		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+@@ -463,6 +465,7 @@ struct ast_tcptls_session_instance *ast_
+ 		goto error;
+ 
+ 	ast_mutex_init(&tcptls_session->lock);
++	tcptls_session->overflow_buf = ast_str_create(128);
+ 	tcptls_session->client = 1;
+ 	tcptls_session->fd = desc->accept_fd;
+ 	tcptls_session->parent = desc;
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/fix-sip-tls-leak asterisk-1.8.13.1~dfsg/debian/patches/fix-sip-tls-leak
--- asterisk-1.8.13.1~dfsg/debian/patches/fix-sip-tls-leak	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/fix-sip-tls-leak	2013-01-08 02:20:03.000000000 +0200
@@ -0,0 +1,61 @@
+From: Joshua Colp <jcolp@digium.com>
+Date: Wed, 5 Dec 2012 16:48:01 +0000
+Subject: Fix a SIP request memory leak with TLS connections.
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-20763
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=377257
+
+During the TLS re-work in chan_sip some TLS specific code was moved
+into a separate function. This function operates on a copy of the
+incoming SIP request. This copy was never deinitialized causing a
+memory leak for each request processed.
+
+This function is now given a SIP request structure which it can use
+to copy the incoming request into. This reduces the amount of memory
+allocations done since the internal allocated components are reused
+between packets and also ensures the SIP request structure is
+deinitialized when the TLS connection is torn down.
+
+Reported by: deti
+
+---
+ channels/chan_sip.c |   12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/channels/chan_sip.c
++++ b/channels/chan_sip.c
+@@ -2516,10 +2516,10 @@ static int sip_check_authtimeout(time_t
+  * \retval -1 Failed to read data
+  * \retval 0 Succeeded in reading data
+  */
+-static int sip_tls_read(struct sip_request *req, struct ast_tcptls_session_instance *tcptls_session, int authenticated, time_t start, struct sip_threadinfo *me)
++static int sip_tls_read(struct sip_request *req, struct sip_request *reqcpy, struct ast_tcptls_session_instance *tcptls_session,
++			int authenticated, time_t start, struct sip_threadinfo *me)
+ {
+ 	int res, content_length, after_poll = 1, need_poll = 1;
+-	struct sip_request reqcpy = { 0, };
+ 	char buf[1024] = "";
+ 	int timeout = -1;
+ 
+@@ -2573,10 +2573,10 @@ static int sip_tls_read(struct sip_reque
+ 		}
+ 		ast_str_append(&req->data, 0, "%s", buf);
+ 	}
+-	copy_request(&reqcpy, req);
+-	parse_request(&reqcpy);
++	copy_request(reqcpy, req);
++	parse_request(reqcpy);
+ 	/* In order to know how much to read, we need the content-length header */
+-	if (sscanf(get_header(&reqcpy, "Content-Length"), "%30d", &content_length)) {
++	if (sscanf(get_header(reqcpy, "Content-Length"), "%30d", &content_length)) {
+ 		while (content_length > 0) {
+ 			size_t bytes_read;
+ 			if (!tcptls_session->client && !authenticated) {
+@@ -2994,7 +2994,7 @@ static void *_sip_tcp_helper_thread(stru
+ 			req.socket.fd = tcptls_session->fd;
+ 
+ 			if (tcptls_session->ssl) {
+-				res = sip_tls_read(&req, tcptls_session, authenticated, start, me);
++				res = sip_tls_read(&req, &reqcpy, tcptls_session, authenticated, start, me);
+ 			} else {
+ 				res = sip_tcp_read(&req, tcptls_session, authenticated, start);
+ 			}
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/fix_xmpp_19532 asterisk-1.8.13.1~dfsg/debian/patches/fix_xmpp_19532
--- asterisk-1.8.13.1~dfsg/debian/patches/fix_xmpp_19532	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/fix_xmpp_19532	2013-03-27 22:56:55.000000000 +0200
@@ -0,0 +1,52 @@
+From 519d65096a6a5c6702f194c29da45140ce698c01 Mon Sep 17 00:00:00 2001
+From: Matthew Jordan <mjordan@digium.com>
+Date: Thu, 4 Oct 2012 02:09:43 +0000
+Subject: Check for presence of buddy in info/dinfo handlers
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-19532
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=374335
+
+The res_jabber resource module uses the ASTOBJ library for managing its ref
+counted objects.  After calling ASTOBJ_CONTAINER_FIND to locate a buddy object,
+the pointer to the object has to be checked to see if the buddy existed.
+Prior to this patch, the buddy object was not checked for NULL; with this patch
+in both aji_client_info_handler and aji_dinfo_handler the pointer is checked
+before used and, if no buddy object was found, the handlers return an error
+code.
+
+---
+ res/res_jabber.c |   12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/res/res_jabber.c b/res/res_jabber.c
+index 764ec6d..7d4eb66 100644
+--- a/res/res_jabber.c
++++ b/res/res_jabber.c
+@@ -2004,6 +2004,12 @@ static int aji_client_info_handler(void *data, ikspak *pak)
+ 	struct aji_resource *resource = NULL;
+ 	struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
+ 
++	if (!buddy) {
++		ast_log(LOG_NOTICE, "JABBER: Received client info from unknown buddy: %s.\n", pak->from->full);
++		ASTOBJ_UNREF(client, ast_aji_client_destroy);
++		return IKS_FILTER_EAT;
++	}
++
+ 	resource = aji_find_resource(buddy, pak->from->resource);
+ 	if (pak->subtype == IKS_TYPE_RESULT) {
+ 		if (!resource) {
+@@ -2071,6 +2077,12 @@ static int aji_dinfo_handler(void *data, ikspak *pak)
+ 	struct aji_resource *resource = NULL;
+ 	struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
+ 
++	if (!buddy) {
++		ast_log(LOG_NOTICE, "JABBER: Received client info from unknown buddy: %s.\n", pak->from->full);
++		ASTOBJ_UNREF(client, ast_aji_client_destroy);
++		return IKS_FILTER_EAT;
++	}
++
+ 	if (pak->subtype == IKS_TYPE_ERROR) {
+ 		ast_log(LOG_WARNING, "Received error from a client, turn on jabber debug!\n");
+ 		ASTOBJ_UNREF(client, ast_aji_client_destroy);
+-- 
+1.7.10.4
+
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/powerpcspe asterisk-1.8.13.1~dfsg/debian/patches/powerpcspe
--- asterisk-1.8.13.1~dfsg/debian/patches/powerpcspe	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/powerpcspe	2013-03-04 14:47:33.000000000 +0200
@@ -0,0 +1,32 @@
+From: Tzafrir Cohen <tzafrir@debian.org>
+Date: Sun, 24 Feb 2013 20:29:05 +0200
+Bug-Debian: http://bugs.debian.org/701505
+Subject: Consider linux-gnuspe as linux-gnu
+Origin: http://svnview.digium.com/svn/asterisk?view=revision&revision=382110
+
+The powerpcspe Linux port uses linux-gnuspe as the OS string.
+Our build system shouldn't really care for that, so just call
+it linux-gnu.
+
+Also: changing "gnueabi" to "gnueabi*" as is in later versions of Upstream.
+This also handles "gnueabihf".
+---
+ configure.ac |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index 5a01fc2..e723c0f 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -172,7 +172,7 @@ case "${host_os}" in
+      OSARCH=cygwin
+      PBX_WINARCH=1
+      ;;
+-     linux-gnueabi)
++     linux-gnueabi* | linux-gnuspe)
+      OSARCH=linux-gnu
+      ;;
+      *)
+-- 
+1.7.10.4
+
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/series asterisk-1.8.13.1~dfsg/debian/patches/series
--- asterisk-1.8.13.1~dfsg/debian/patches/series	2012-08-31 01:57:06.000000000 +0300
+++ asterisk-1.8.13.1~dfsg/debian/patches/series	2013-04-06 14:14:01.000000000 +0300
@@ -28,3 +28,13 @@
 
 AST-2012-012
 AST-2012-013
+# Needed for AST-2012-014:
+fix-sip-tcp-no-FILE
+fix-sip-tls-leak
+AST-2012-014
+AST-2012-015
+powerpcspe
+fix_xmpp_19532
+AST-2013-002
+AST-2013-003
+bluetooth_bind
diff -Nru asterisk-1.8.13.1~dfsg/debian/README.Debian asterisk-1.8.13.1~dfsg/debian/README.Debian
--- asterisk-1.8.13.1~dfsg/debian/README.Debian	2011-09-28 13:20:09.000000000 +0300
+++ asterisk-1.8.13.1~dfsg/debian/README.Debian	2013-03-28 14:46:13.000000000 +0200
@@ -303,6 +303,180 @@
 live/asterisk is a wrapper to that private copy of Asterisk.
 
 
-Enjoy your PBX!
+Test Suite
+==========
+# Required packages: sipp >= 3.2-2, python-starpy, python-yaml
+./debian/rules build
+sed -i -e 's|^ASTDATADIR = .*|ASTDATADIR = /usr/share/asterisk|' makeopts
+git clone <url> testsuite
+cd testsuite
+./run-local setup
+(for type in imap odbc; do echo "noload => app_voicemail_${type}storage.so"; done) >>astroot/etc/asterisk/modules.conf
+dget asterisk-core-sounds-en-gsm
+dpkg -x asterisk-core-sounds-en-gsm_*_all.deb astroot
+# ln -s en_US_f_Allison astroot/usr/share/asterisk/sounds/en # ASTERISK-21322
+mv astroot/usr/share/asterisk/sounds/en_US_f_Allison astroot/usr/share/asterisk/sounds/en
+./run-local run -v 1.8.13
+
+Printing all tests:
+  awk -F'"' '/<testcase /{print $2}' asterisk-test-suite-report.xml
+
+Failed ones:
+  awk -F'"' '/<testcase /{test=$2}; /<failure>/{print test}' asterisk-test-suite-report.xml 
+
+Total test: 123. Failures: 27.
+
+Tests:
+tests/example
+tests/manager/login
+tests/manager/action-events-response
+tests/manager/authlimit
+tests/manager/authtimeout
+tests/cdr/console_dial_sip_answer
+tests/cdr/console_dial_sip_busy
+tests/cdr/console_dial_sip_congestion
+tests/cdr/console_dial_sip_transfer
+tests/cdr/console_fork_after_busy_forward
+tests/cdr/console_fork_before_dial
+tests/cdr/cdr_fork_end_time
+tests/cdr/cdr_unanswered_yes
+tests/cdr/cdr_accountcode
+tests/cdr/cdr_userfield
+tests/cdr/nocdr
+tests/cdr/originate-cdr-disposition
+tests/cdr/batch_cdrs
+tests/channels/SIP/options
+tests/channels/SIP/info_dtmf
+tests/channels/SIP/tcpauthlimit
+tests/channels/SIP/tcpauthtimeout
+tests/channels/SIP/sip_register
+tests/channels/SIP/sip_channel_params
+tests/channels/SIP/sip_tls_call
+tests/channels/SIP/sip_tls_register
+tests/channels/SIP/sip_srtp
+tests/channels/SIP/noload_res_srtp
+tests/channels/SIP/noload_res_srtp_attempt_srtp
+tests/channels/SIP/secure_bridge_media
+tests/channels/SIP/message_disabled
+tests/channels/SIP/handle_response_address_incomplete
+tests/channels/SIP/realtime_sipregs
+tests/channels/SIP/realtime_nosipregs
+tests/channels/SIP/SDP_offer_answer
+tests/channels/SIP/sip_hold
+tests/channels/SIP/header_parsing
+tests/channels/SIP/use_contact_from_200
+tests/channels/SIP/sip_cause
+tests/channels/SIP/invite_no_totag
+tests/channels/SIP/rfc2833_dtmf_detect
+tests/channels/SIP/device_state_notification
+tests/channels/SIP/directrtpsetup
+tests/channels/SIP/session_timers/basic_uac_teardown
+tests/channels/SIP/session_timers/basic_uac_refresh
+tests/channels/SIP/session_timers/basic_uas_refresh
+tests/channels/SIP/session_timers/basic_uas_teardown
+tests/channels/SIP/session_timers/uas_minimum_se
+tests/channels/SIP/session_timers/uac_se_below_minse
+tests/channels/SIP/session_timers/uas_originate/no_minse_small_se
+tests/channels/SIP/session_timers/uas_originate/no_minse_medium_se
+tests/channels/SIP/session_timers/uas_originate/no_minse_large_se
+tests/channels/SIP/session_timers/uas_originate/small_minse_small_se
+tests/channels/SIP/session_timers/uas_originate/small_minse_medium_se
+tests/channels/SIP/session_timers/uas_originate/small_minse_large_se
+tests/channels/SIP/session_timers/uas_originate/medium_minse_medium_se
+tests/channels/SIP/session_timers/uas_originate/medium_minse_large_se
+tests/channels/SIP/session_timers/uas_originate/large_minse_large_se
+tests/channels/iax2/basic-call
+tests/udptl
+tests/udptl_v6
+tests/feature_blonde_transfer
+tests/feature_attended_transfer
+tests/callparking
+tests/fastagi/connect
+tests/fastagi/channel-status
+tests/fastagi/hangup
+tests/fastagi/stream-file
+tests/fastagi/control-stream-file
+tests/fastagi/database
+tests/fastagi/execute
+tests/fastagi/get-data
+tests/fastagi/record-file
+tests/fastagi/say-alpha
+tests/fastagi/say-date
+tests/fastagi/say-datetime
+tests/fastagi/say-time
+tests/fastagi/say-digits
+tests/fastagi/say-number
+tests/fastagi/say-phonetic
+tests/asyncagi/asyncagi_break
+tests/pbx/call-files
+tests/pbx/pbx_lua_background
+tests/fax/local_channel_t38_queryoption
+tests/apps/directory_operator_exit
+tests/apps/directory_context_operator_exit
+tests/apps/directory_attendant_exit
+tests/apps/bridge/bridge_baseline
+tests/apps/voicemail/leave_voicemail_nominal
+tests/apps/voicemail/leave_voicemail_priority
+tests/apps/voicemail/leave_voicemail_forwarding
+tests/apps/voicemail/leave_voicemail_forwarding_auto_urgent
+tests/apps/voicemail/func_vmcount
+tests/apps/voicemail/authenticate_nominal
+tests/apps/voicemail/authenticate_invalid_mailbox
+tests/apps/voicemail/authenticate_invalid_password
+tests/apps/voicemail/authenticate_extensions
+tests/apps/voicemail/check_voicemail_nominal
+tests/apps/voicemail/check_voicemail_envelope
+tests/apps/voicemail/check_voicemail_delete
+tests/apps/voicemail/check_voicemail_new_user
+tests/apps/voicemail/check_voicemail_new_user_hangup
+tests/apps/voicemail/check_voicemail_options_record_busy
+tests/apps/voicemail/check_voicemail_options_record_unavail
+tests/apps/voicemail/check_voicemail_options_record_name
+tests/apps/voicemail/check_voicemail_options_record_temp
+tests/apps/voicemail/check_voicemail_options_change_password
+tests/apps/voicemail/check_voicemail_forward
+tests/apps/voicemail/check_voicemail_forward_hangup
+tests/apps/voicemail/check_voicemail_forward_with_prepend
+tests/apps/voicemail/check_voicemail_callback
+tests/apps/voicemail/check_voicemail_dialout
+tests/apps/voicemail/check_voicemail_reply
+tests/apps/incomplete/sip_incomplete
+tests/apps/queues/queue_baseline
+tests/apps/queues/position_priority_maxlen
+tests/apps/queues/set_penalty
+tests/apps/directed_pickup
+tests/apps/mixmonitor
+tests/apps/control_playback/nominal
+tests/apps/playback/nominal
+tests/connected_line/macro
+tests/redirecting/macro
+
+Failed tests:
+tests/channels/SIP/sip_channel_params
+tests/channels/SIP/sip_tls_register
+tests/channels/SIP/noload_res_srtp_attempt_srtp
+tests/channels/SIP/sip_hold
+tests/channels/SIP/rfc2833_dtmf_detect
+tests/apps/voicemail/leave_voicemail_nominal
+tests/apps/voicemail/leave_voicemail_priority
+tests/apps/voicemail/leave_voicemail_forwarding
+tests/apps/voicemail/leave_voicemail_forwarding_auto_urgent
+tests/apps/voicemail/authenticate_nominal
+tests/apps/voicemail/check_voicemail_nominal
+tests/apps/voicemail/check_voicemail_envelope
+tests/apps/voicemail/check_voicemail_delete
+tests/apps/voicemail/check_voicemail_new_user
+tests/apps/voicemail/check_voicemail_new_user_hangup
+tests/apps/voicemail/check_voicemail_options_record_busy
+tests/apps/voicemail/check_voicemail_options_record_unavail
+tests/apps/voicemail/check_voicemail_options_record_name
+tests/apps/voicemail/check_voicemail_options_record_temp
+tests/apps/voicemail/check_voicemail_options_change_password
+tests/apps/voicemail/check_voicemail_forward
+tests/apps/voicemail/check_voicemail_forward_hangup
+tests/apps/voicemail/check_voicemail_forward_with_prepend
+tests/apps/voicemail/check_voicemail_callback
+tests/apps/voicemail/check_voicemail_dialout
+tests/apps/voicemail/check_voicemail_reply
+tests/apps/queues/queue_baseline
 
- -- Lionel Elie Mamane <lmamane@debian.org>, Fri, 29 Jul 2011 19:21:06 +0200

-- System Information:
Debian Release: 7.0
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'testing')
Architecture: amd64 (x86_64)

Kernel: Linux 3.2.0-4-amd64 (SMP w/2 CPU cores)
Locale: LANG=he_IL.utf8, LC_CTYPE=he_IL.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

--- End Message ---
--- Begin Message ---
On 09.04.2013 11:54, Adam D. Barratt wrote:
On 09.04.2013 11:34, Tzafrir Cohen wrote:
On Tue, Apr 09, 2013 at 01:30:01AM +0200, Tzafrir Cohen wrote:

Done. It turned out to be much smaller than the original one. At first
glance there isn't any other code path.


http://anonscm.debian.org/viewvc/pkg-voip/asterisk/trunk/debian/patches/AST-2012-014?revision=10137&view=markup

All other requested changed are commited to SVN. I'll rebuild -3
morning.

Debdiff attached. Upload?

Please go ahead; thanks.

-3 unblocked; thanks for the quick turnaround.

Regards,

Adam

--- End Message ---

Reply to: