From 8fe9f1f7f1c37b2021483834f638972698d16ead Mon Sep 17 00:00:00 2001
From: Mark Michelson <mmichelson@digium.com>
Date: Tue, 9 Aug 2016 16:19:34 -0500
Subject: [PATCH] res_rtp_asterisk: Cache local RTCP address.

When an RTCP packet is sent or received, res_rtp_asterisk generates a
Stasis event that contains the RTCP report as well as the local and
remote addresses that the report pertains to.

The addresses are determined using ast_find_ourip(). For the local
address, this will typically result in a lookup of the hostname of the
server, and then a DNS lookup of that hostname. If you do not have the
host in /etc/hosts, then this results in a full DNS lookup, which can
potentially block for some time.

This is especially problematic when performing RTCP reads, since those
are done on the same thread responsible for reading and writing media.

This patch addresses the issue by performing a lookup of the local
address when RTCP is allocated. We then use this cached local address
for the Stasis events when necessary.

ASTERISK-26280 #close
Reported by Mark Michelson

Change-Id: I3dd61882c2e57036f09f0c390cf38f7c87e9b556
---
 res/res_rtp_asterisk.c | 69 ++++++++++++++++++------------------------
 1 file changed, 29 insertions(+), 40 deletions(-)

diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 6cf69ef0d7..a8d4a90775 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -407,6 +407,12 @@ struct ast_rtcp {
 #ifdef HAVE_OPENSSL_SRTP
 	struct dtls_details dtls; /*!< DTLS state information */
 #endif
+
+	/* Cached local address string allows us to generate
+	 * RTCP stasis messages without having to look up our
+	 * own address every time
+	 */
+	char *local_addr_str;
 };
 
 struct rtp_red {
@@ -2700,6 +2706,7 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
 		 * RTP instance while it's active.
 		 */
 		close(rtp->rtcp->s);
+		ast_free(rtp->rtcp->local_addr_str);
 		ast_free(rtp->rtcp);
 	}
 
@@ -3112,12 +3119,7 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
 	int rate = rtp_get_rate(rtp->f.subclass.format);
 	int ice;
 	int header_offset = 0;
-	char *str_remote_address;
-	char *str_local_address;
 	struct ast_sockaddr remote_address = { { 0, } };
-	struct ast_sockaddr local_address = { { 0, } };
-	struct ast_sockaddr real_remote_address = { { 0, } };
-	struct ast_sockaddr real_local_address = { { 0, } };
 	struct ast_rtp_rtcp_report_block *report_block = NULL;
 	RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,
 			ast_rtp_rtcp_report_alloc(rtp->themssrc ? 1 : 0),
@@ -3244,22 +3246,9 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
 		}
 	}
 
-	ast_rtp_instance_get_local_address(instance, &local_address);
-	if (!ast_find_ourip(&real_local_address, &local_address, 0)) {
-		str_local_address = ast_strdupa(ast_sockaddr_stringify(&real_local_address));
-	} else {
-		str_local_address = ast_strdupa(ast_sockaddr_stringify(&local_address));
-	}
-
-	if (!ast_find_ourip(&real_remote_address, &remote_address, 0)) {
-		str_remote_address = ast_strdupa(ast_sockaddr_stringify(&real_remote_address));
-	} else {
-		str_remote_address = ast_strdupa(ast_sockaddr_stringify(&remote_address));
-	}
-
 	message_blob = ast_json_pack("{s: s, s: s}",
-			"to", str_remote_address,
-			"from", str_local_address);
+			"to", ast_sockaddr_stringify(&remote_address),
+			"from", rtp->rtcp->local_addr_str);
 	ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_sent_type(),
 			rtcp_report,
 			message_blob);
@@ -4067,11 +4056,6 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
 	int report_counter = 0;
 	struct ast_rtp_rtcp_report_block *report_block;
 	struct ast_frame *f = &ast_null_frame;
-	char *str_local_address;
-	char *str_remote_address;
-	struct ast_sockaddr local_address = { { 0,} };
-	struct ast_sockaddr real_local_address = { { 0, } };
-	struct ast_sockaddr real_remote_address = { { 0, } };
 
 	/* Read in RTCP data from the socket */
 	if ((res = rtcp_recvfrom(instance, rtcpdata + AST_FRIENDLY_OFFSET,
@@ -4128,8 +4112,6 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
 
 	ast_debug(1, "Got RTCP report of %d bytes\n", res);
 
-	ast_rtp_instance_get_local_address(instance, &local_address);
-
 	while (position < packetwords) {
 		int i, pt, rc;
 		unsigned int length;
@@ -4245,21 +4227,10 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
 			/* If and when we handle more than one report block, this should occur outside
 			 * this loop.
 			 */
-			if (!ast_find_ourip(&real_local_address, &local_address, 0)) {
-				str_local_address = ast_strdupa(ast_sockaddr_stringify(&real_local_address));
-			} else {
-				str_local_address = ast_strdupa(ast_sockaddr_stringify(&local_address));
-			}
-
-			if (!ast_find_ourip(&real_remote_address, &addr, 0)) {
-				str_remote_address = ast_strdupa(ast_sockaddr_stringify(&real_remote_address));
-			} else {
-				str_remote_address = ast_strdupa(ast_sockaddr_stringify(&addr));
-			}
 
 			message_blob = ast_json_pack("{s: s, s: s, s: f}",
-					"from", str_remote_address,
-					"to", str_local_address,
+					"from", ast_sockaddr_stringify(&rtp->rtcp->them),
+					"to", rtp->rtcp->local_addr_str,
 					"rtt", rtp->rtcp->rtt);
 			ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_received_type(),
 					rtcp_report,
@@ -4822,6 +4793,8 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
 
 	if (property == AST_RTP_PROPERTY_RTCP) {
 		if (value) {
+			struct ast_sockaddr local_addr;
+
 			if (rtp->rtcp) {
 				ast_debug(1, "Ignoring duplicate RTCP property on RTP instance '%p'\n", instance);
 				return;
@@ -4836,6 +4809,19 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
 			ast_sockaddr_set_port(&rtp->rtcp->us,
 					      ast_sockaddr_port(&rtp->rtcp->us) + 1);
 
+			if (!ast_find_ourip(&local_addr, &rtp->rtcp->us, 0)) {
+				ast_sockaddr_set_port(&local_addr, ast_sockaddr_port(&rtp->rtcp->us));
+			} else {
+				ast_sockaddr_copy(&local_addr, &rtp->rtcp->us);
+			}
+
+			rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local_addr));
+			if (!rtp->rtcp->local_addr_str) {
+				ast_free(rtp->rtcp);
+				rtp->rtcp = NULL;
+				return;
+			}
+
 			if ((rtp->rtcp->s =
 			     create_new_socket("RTCP",
 					       ast_sockaddr_is_ipv4(&rtp->rtcp->us) ?
@@ -4843,6 +4829,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
 					       ast_sockaddr_is_ipv6(&rtp->rtcp->us) ?
 					       AF_INET6 : -1)) < 0) {
 				ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance);
+				ast_free(rtp->rtcp->local_addr_str);
 				ast_free(rtp->rtcp);
 				rtp->rtcp = NULL;
 				return;
@@ -4852,6 +4839,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
 			if (ast_bind(rtp->rtcp->s, &rtp->rtcp->us)) {
 				ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance);
 				close(rtp->rtcp->s);
+				ast_free(rtp->rtcp->local_addr_str);
 				ast_free(rtp->rtcp);
 				rtp->rtcp = NULL;
 				return;
@@ -4891,6 +4879,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
 					SSL_free(rtp->rtcp->dtls.ssl);
 				}
 #endif
+				ast_free(rtp->rtcp->local_addr_str);
 				ast_free(rtp->rtcp);
 				rtp->rtcp = NULL;
 			}