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

Bug#704829: unblock: asterisk/1:1.8.13.1~dfsg-2



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


Reply to: