[asterisk-commits] kmoore: trunk r369028 - in /trunk/channels: chan_sip.c sip/include/sip.h

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jun 15 12:13:24 CDT 2012


Author: kmoore
Date: Fri Jun 15 12:13:20 2012
New Revision: 369028

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=369028
Log:
Allow chan_sip to decline unwanted media streams

This change replaces the static array of four representable media
streams with an AST_LIST so that chan_sip can keep track of offered
media streams.  This allows chan_sip to deal with offers containing
multiple same-type streams and many other situations without rejecting
the SDP offer in its entirety, yet still generating a valid response.
This also covers cases where Asterisk can not comprehend the offer if
it is in the correct format.

Previously, chan_sip would reject SDP offers or entirely ignore
individual stream offers in an effort to be more compatible which
would often result in invalid SDP responses.

Review: https://reviewboard.asterisk.org/r/1988/

Modified:
    trunk/channels/chan_sip.c
    trunk/channels/sip/include/sip.h

Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=369028&r1=369027&r2=369028
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Fri Jun 15 12:13:20 2012
@@ -5850,6 +5850,16 @@
 	ast_free(mwi);
 }
 
+/*! \brief Destroy SDP media offer list */
+static void offered_media_list_destroy(struct sip_pvt *p)
+{
+	struct offered_media *offer;
+	while ((offer = AST_LIST_REMOVE_HEAD(&p->offered_media, next))) {
+		ast_free(offer->decline_m_line);
+		ast_free(offer);
+	}
+}
+
 /*! \brief Execute destruction of SIP dialog structure, release memory */
 void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
 {
@@ -5952,6 +5962,8 @@
 	while ((req = AST_LIST_REMOVE_HEAD(&p->request_queue, next))) {
 		ast_free(req);
 	}
+
+	offered_media_list_destroy(p);
 
 	if (p->chanvars) {
 		ast_variables_destroy(p->chanvars);
@@ -8033,6 +8045,7 @@
 	ast_string_field_set(p, engine, default_engine);
 
 	AST_LIST_HEAD_INIT_NOLOCK(&p->request_queue);
+	AST_LIST_HEAD_INIT_NOLOCK(&p->offered_media);
 
 	/* Add to active dialog list */
 
@@ -9119,6 +9132,18 @@
 	return ast_sockaddr_isnull(addr) || ast_sockaddr_is_any(addr);
 }
 
+/*! \brief Check the media stream list to see if the given type already exists */
+static int has_media_stream(struct sip_pvt *p, enum media_type m)
+{
+	struct offered_media *offer = NULL;
+	AST_LIST_TRAVERSE(&p->offered_media, offer, next) {
+		if (m == offer->type) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
 /*! \brief Process SIP SDP offer, select formats and activate media channels
 	If offer is rejected, we will not change any properties of the call
  	Return 0 on success, a negative value on errors.
@@ -9139,6 +9164,7 @@
 	const char *m = NULL;           /* SDP media offer */
 	const char *nextm = NULL;
 	int len = -1;
+	struct offered_media *offer;
 
 	/* Host information */
 	struct ast_sockaddr sessionsa;
@@ -9178,7 +9204,6 @@
 	int sendonly = -1;
 	int vsendonly = -1;
 	int numberofports;
-	int numberofmediastreams = 0;
 	int last_rtpmap_codec = 0;
 	int red_data_pt[10];		/* For T.140 RED */
 	int red_num_gen = 0;		/* For T.140 RED */
@@ -9209,7 +9234,7 @@
 	/* Update our last rtprx when we receive an SDP, too */
 	p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
 
-	memset(p->offered_media, 0, sizeof(p->offered_media));
+	offered_media_list_destroy(p);
 
 	/* Scan for the first media stream (m=) line to limit scanning of globals */
 	nextm = get_sdp_iterate(&next, req, "m");
@@ -9281,10 +9306,29 @@
 		iterator = next;
 		nextm = get_sdp_iterate(&next, req, "m");
 
+		if (!(offer = ast_calloc(1, sizeof(*offer)))) {
+			ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer list\n");
+			res = -1;
+			goto process_sdp_cleanup;
+		}
+		AST_LIST_INSERT_TAIL(&p->offered_media, offer, next);
+		offer->type = SDP_UNKNOWN;
+
 		/* Check for 'audio' media offer */
 		if (strncmp(m, "audio ", 6) == 0) {
 			if ((sscanf(m, "audio %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
 			    (sscanf(m, "audio %30u RTP/%4s %n", &x, protocol, &len) == 2 && len > 0)) {
+				codecs = m + len;
+				/* produce zero-port m-line since it may be needed later
+				 * length is "m=audio 0 RTP/" + protocol + " " + codecs + "\0" */
+				if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
+					res = -1;
+					goto process_sdp_cleanup;
+				}
+				/* guaranteed to be exactly the right length */
+				sprintf(offer->decline_m_line, "m=audio 0 RTP/%s %s", protocol, codecs);
+
 				if (x == 0) {
 					ast_log(LOG_WARNING, "Ignoring audio media offer because port number is zero\n");
 					continue;
@@ -9299,23 +9343,19 @@
 					secure_audio = 1;
 				} else if (strcmp(protocol, "AVP")) {
 					ast_log(LOG_WARNING, "Unknown RTP profile in audio offer: %s\n", m);
-					res = -1;
-					goto process_sdp_cleanup;
+					continue;
 				}
 
-				if (p->offered_media[SDP_AUDIO].order_offered) {
-					ast_log(LOG_WARNING, "Rejecting non-primary audio stream: %s\n", m);
-					res = -1;
-					goto process_sdp_cleanup;
+				if (has_media_stream(p, SDP_AUDIO)) {
+					ast_log(LOG_WARNING, "Declining non-primary audio stream: %s\n", m);
+					continue;
 				}
 
 				audio = TRUE;
-				p->offered_media[SDP_AUDIO].order_offered = ++numberofmediastreams;
+				offer->type = SDP_AUDIO;
 				portno = x;
 
 				/* Scan through the RTP payload types specified in a "m=" line: */
-				codecs = m + len;
-				ast_copy_string(p->offered_media[SDP_AUDIO].codecs, codecs, sizeof(p->offered_media[SDP_AUDIO].codecs));
 				for (; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
 					if (sscanf(codecs, "%30u%n", &codec, &len) != 1) {
 						ast_log(LOG_WARNING, "Invalid syntax in RTP audio format list: %s\n", codecs);
@@ -9338,38 +9378,45 @@
 		else if (strncmp(m, "video ", 6) == 0) {
 			if ((sscanf(m, "video %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
 			    (sscanf(m, "video %30u RTP/%4s %n", &x, protocol, &len) == 2 && len > 0)) {
+				codecs = m + len;
+				/* produce zero-port m-line since it may be needed later
+				 * length is "m=video 0 RTP/" + protocol + " " + codecs + "\0" */
+				if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
+					res = -1;
+					goto process_sdp_cleanup;
+				}
+				/* guaranteed to be exactly the right length */
+				sprintf(offer->decline_m_line, "m=video 0 RTP/%s %s", protocol, codecs);
+
 				if (x == 0) {
-					ast_log(LOG_WARNING, "Ignoring video media offer because port number is zero\n");
+					ast_log(LOG_WARNING, "Ignoring video stream offer because port number is zero\n");
 					continue;
 				}
 
 				/* Check number of ports offered for stream */
 				if (numberofports > 1) {
-					ast_log(LOG_WARNING, "%d ports offered for video media, not supported by Asterisk. Will try anyway...\n", numberofports);
+					ast_log(LOG_WARNING, "%d ports offered for video stream, not supported by Asterisk. Will try anyway...\n", numberofports);
 				}
 
 				if (!strcmp(protocol, "SAVP")) {
 					secure_video = 1;
 				} else if (strcmp(protocol, "AVP")) {
 					ast_log(LOG_WARNING, "Unknown RTP profile in video offer: %s\n", m);
-					res = -1;
-					goto process_sdp_cleanup;
+					continue;
 				}
 
-				if (p->offered_media[SDP_VIDEO].order_offered) {
-					ast_log(LOG_WARNING, "Rejecting non-primary video stream: %s\n", m);
-					res = -1;
-					goto process_sdp_cleanup;
+				if (has_media_stream(p, SDP_VIDEO)) {
+					ast_log(LOG_WARNING, "Declining non-primary video stream: %s\n", m);
+					continue;
 				}
 
 				video = TRUE;
 				p->novideo = FALSE;
-				p->offered_media[SDP_VIDEO].order_offered = ++numberofmediastreams;
+				offer->type = SDP_VIDEO;
 				vportno = x;
 
 				/* Scan through the RTP payload types specified in a "m=" line: */
-				codecs = m + len;
-				ast_copy_string(p->offered_media[SDP_VIDEO].codecs, codecs, sizeof(p->offered_media[SDP_VIDEO].codecs));
 				for (; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
 					if (sscanf(codecs, "%30u%n", &codec, &len) != 1) {
 						ast_log(LOG_WARNING, "Invalid syntax in RTP video format list: %s\n", codecs);
@@ -9391,30 +9438,38 @@
 		else if (strncmp(m, "text ", 5) == 0) {
 			if ((sscanf(m, "text %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
 			    (sscanf(m, "text %30u RTP/AVP %n", &x, &len) == 1 && len > 0)) {
-				if (x == 0) {
-					ast_log(LOG_WARNING, "Ignoring text media offer because port number is zero\n");
-					continue;
-				}
-
-				/* Check number of ports offered for stream */
-				if (numberofports > 1) {
-					ast_log(LOG_WARNING, "%d ports offered for text media, not supported by Asterisk. Will try anyway...\n", numberofports);
-				}
-
-				if (p->offered_media[SDP_TEXT].order_offered) {
-					ast_log(LOG_WARNING, "Rejecting non-primary text stream: %s\n", m);
+				codecs = m + len;
+				/* produce zero-port m-line since it may be needed later
+				 * length is "m=text 0 RTP/AVP " + codecs + "\0" */
+				if (!(offer->decline_m_line = ast_malloc(17 + strlen(codecs) + 1))) {
+					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
 					res = -1;
 					goto process_sdp_cleanup;
 				}
+				/* guaranteed to be exactly the right length */
+				sprintf(offer->decline_m_line, "m=text 0 RTP/AVP %s", codecs);
+
+				if (x == 0) {
+					ast_log(LOG_WARNING, "Ignoring text stream offer because port number is zero\n");
+					continue;
+				}
+
+				/* Check number of ports offered for stream */
+				if (numberofports > 1) {
+					ast_log(LOG_WARNING, "%d ports offered for text stream, not supported by Asterisk. Will try anyway...\n", numberofports);
+				}
+
+				if (has_media_stream(p, SDP_TEXT)) {
+					ast_log(LOG_WARNING, "Declining non-primary text stream: %s\n", m);
+					continue;
+				}
 
 				text = TRUE;
 				p->notext = FALSE;
-				p->offered_media[SDP_TEXT].order_offered = ++numberofmediastreams;
+				offer->type = SDP_TEXT;
 				tportno = x;
 
 				/* Scan through the RTP payload types specified in a "m=" line: */
-				codecs = m + len;
-				ast_copy_string(p->offered_media[SDP_TEXT].codecs, codecs, sizeof(p->offered_media[SDP_TEXT].codecs));
 				for (; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
 					if (sscanf(codecs, "%30u%n", &codec, &len) != 1) {
 						ast_log(LOG_WARNING, "Invalid syntax in RTP video format list: %s\n", codecs);
@@ -9427,7 +9482,7 @@
 					ast_rtp_codecs_payloads_set_m_type(&newtextrtp, NULL, codec);
 				}
 			} else {
-				ast_log(LOG_WARNING, "Rejecting text media offer due to invalid or unsupported syntax: %s\n", m);
+				ast_log(LOG_WARNING, "Rejecting text stream offer due to invalid or unsupported syntax: %s\n", m);
 				res = -1;
 				goto process_sdp_cleanup;
 			}
@@ -9436,20 +9491,29 @@
 		else if (strncmp(m, "image ", 6) == 0) {
 			if (((sscanf(m, "image %30u udptl t38%n", &x, &len) == 1 && len > 0) ||
 			     (sscanf(m, "image %30u UDPTL t38%n", &x, &len) == 1 && len > 0))) {
-				if (x == 0) {
-					ast_log(LOG_WARNING, "Ignoring image media offer because port number is zero\n");
-					continue;
-				}
-
-				if (initialize_udptl(p)) {
+				/* produce zero-port m-line since it may be needed later
+				 * length is "m=image 0 UDPTL t38" + "\0" */
+				if (!(offer->decline_m_line = ast_malloc(20))) {
+					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
 					res = -1;
 					goto process_sdp_cleanup;
 				}
-
-				if (p->offered_media[SDP_IMAGE].order_offered) {
-					ast_log(LOG_WARNING, "Rejecting non-primary image stream: %s\n", m);
-					res = -1;
-					goto process_sdp_cleanup;
+				/* guaranteed to be exactly the right length */
+				strcpy(offer->decline_m_line, "m=image 0 UDPTL t38");
+
+				if (x == 0) {
+					ast_log(LOG_WARNING, "Ignoring image stream offer because port number is zero\n");
+					continue;
+				}
+
+				if (initialize_udptl(p)) {
+					ast_log(LOG_WARNING, "Failed to initialize UDPTL, declining image stream\n");
+					continue;
+				}
+
+				if (has_media_stream(p, SDP_IMAGE)) {
+					ast_log(LOG_WARNING, "Declining non-primary image stream: %s\n", m);
+					continue;
 				}
 
 				image = TRUE;
@@ -9457,7 +9521,7 @@
 					ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid);
 				}
 
-				p->offered_media[SDP_IMAGE].order_offered = ++numberofmediastreams;
+				offer->type = SDP_IMAGE;
 				udptlportno = x;
 
 				if (p->t38.state != T38_ENABLED) {
@@ -9473,9 +9537,26 @@
 				goto process_sdp_cleanup;
 			}
 		} else {
-			ast_log(LOG_WARNING, "Unsupported top-level media type in offer: %s\n", m);
-			res = -1;
-			goto process_sdp_cleanup;
+			char type[20] = {0,};
+			char *typelen = strchr(m, ' ');
+			if (typelen && typelen - m < 20 &&
+			    ((sscanf(m, "%s %30u/%30u %n", type, &x, &numberofports, &len) == 2 && len > 0) ||
+			     (sscanf(m, "%s %30u %n", type, &x, &len) == 1 && len > 0))) {
+				/* produce zero-port m-line since it may be needed later
+				 * length is "m=" + type + " 0 " + remainder + "\0" */
+				if (!(offer->decline_m_line = ast_malloc(2 + strlen(type) + 3 + strlen(m + len) + 1))) {
+					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
+					res = -1;
+					goto process_sdp_cleanup;
+				}
+				/* guaranteed to be long enough */
+				sprintf(offer->decline_m_line, "m=%s 0 %s", type, m + len);
+				continue;
+			} else {
+				ast_log(LOG_WARNING, "Unsupported top-level media type in offer: %s\n", m);
+				res = -1;
+				goto process_sdp_cleanup;
+			}
 		}
 
 		/* Media stream specific parameters */
@@ -9852,6 +9933,9 @@
 	}
 
 process_sdp_cleanup:
+	if (res) {
+		offered_media_list_destroy(p);
+	}
 	ast_format_cap_destroy(peercapability);
 	ast_format_cap_destroy(vpeercapability);
 	ast_format_cap_destroy(tpeercapability);
@@ -11817,6 +11901,7 @@
 	struct ast_sockaddr udptldest = { {0,} };
 
 	/* SDP fields */
+	struct offered_media *offer;
 	char *version = 	"v=0\r\n";		/* Protocol version */
 	char subject[256];				/* Subject of the session */
 	char owner[256];				/* Session owner/creator */
@@ -11848,7 +11933,6 @@
 
 	char codecbuf[SIPBUFSIZE];
 	char buf[SIPBUFSIZE];
-	char dummy_answer[256];
 
 	/* Set the SDP session name */
 	snprintf(subject, sizeof(subject), "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
@@ -12153,14 +12237,10 @@
 	}
 	add_content(resp, session_time);
 	/* if this is a response to an invite, order our offers properly */
-	if (p->offered_media[SDP_AUDIO].order_offered ||
-		p->offered_media[SDP_VIDEO].order_offered ||
-		p->offered_media[SDP_TEXT].order_offered ||
-		p->offered_media[SDP_IMAGE].order_offered) {
-		int i;
-		/* we have up to 3 streams as limited by process_sdp */
-		for (i = 1; i <= 3; i++) {
-			if (p->offered_media[SDP_AUDIO].order_offered == i) {
+	if (!AST_LIST_EMPTY(&p->offered_media)) {
+		AST_LIST_TRAVERSE(&p->offered_media, offer, next) {
+			switch (offer->type) {
+			case SDP_AUDIO:
 				if (needaudio) {
 					add_content(resp, m_audio->str);
 					add_content(resp, a_audio->str);
@@ -12169,10 +12249,10 @@
 						add_content(resp, a_crypto);
 					}
 				} else {
-					snprintf(dummy_answer, sizeof(dummy_answer), "m=audio 0 RTP/AVP %s\r\n", p->offered_media[SDP_AUDIO].codecs);
-					add_content(resp, dummy_answer);
+					add_content(resp, offer->decline_m_line);
 				}
-			} else if (p->offered_media[SDP_VIDEO].order_offered == i) {
+				break;
+			case SDP_VIDEO:
 				if (needvideo) { /* only if video response is appropriate */
 					add_content(resp, m_video->str);
 					add_content(resp, a_video->str);
@@ -12181,10 +12261,10 @@
 						add_content(resp, v_a_crypto);
 					}
 				} else {
-					snprintf(dummy_answer, sizeof(dummy_answer), "m=video 0 RTP/AVP %s\r\n", p->offered_media[SDP_VIDEO].codecs);
-					add_content(resp, dummy_answer);
+					add_content(resp, offer->decline_m_line);
 				}
-			} else if (p->offered_media[SDP_TEXT].order_offered == i) {
+				break;
+			case SDP_TEXT:
 				if (needtext) { /* only if text response is appropriate */
 					add_content(resp, m_text->str);
 					add_content(resp, a_text->str);
@@ -12193,16 +12273,20 @@
 						add_content(resp, t_a_crypto);
 					}
 				} else {
-					snprintf(dummy_answer, sizeof(dummy_answer), "m=text 0 RTP/AVP %s\r\n", p->offered_media[SDP_TEXT].codecs);
-					add_content(resp, dummy_answer);
+					add_content(resp, offer->decline_m_line);
 				}
-			} else if (p->offered_media[SDP_IMAGE].order_offered == i) {
+				break;
+			case SDP_IMAGE:
 				if (add_t38) {
 					add_content(resp, m_modem->str);
 					add_content(resp, a_modem->str);
 				} else {
-					add_content(resp, "m=image 0 udptl t38\r\n");
+					add_content(resp, offer->decline_m_line);
 				}
+				break;
+			case SDP_UNKNOWN:
+				add_content(resp, offer->decline_m_line);
+				break;
 			}
 		}
 	} else {
@@ -12452,7 +12536,8 @@
 	if (p->do_history) {
 		append_history(p, "ReInv", "Re-invite sent");
 	}
-	memset(p->offered_media, 0, sizeof(p->offered_media));
+
+	offered_media_list_destroy(p);
 
 	try_suggested_sip_codec(p);
 	if (t38version) {
@@ -12913,7 +12998,7 @@
 		add_diversion_header(&req, p);
 	}
 	if (sdp) {
-		memset(p->offered_media, 0, sizeof(p->offered_media));
+		offered_media_list_destroy(p);
 		if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) {
 			ast_debug(1, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? ast_channel_name(p->owner) : "<none>");
 			add_sdp(&req, p, FALSE, FALSE, TRUE);

Modified: trunk/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sip/include/sip.h?view=diff&rev=369028&r1=369027&r2=369028
==============================================================================
--- trunk/channels/sip/include/sip.h (original)
+++ trunk/channels/sip/include/sip.h Fri Jun 15 12:13:20 2012
@@ -467,6 +467,7 @@
 	SDP_VIDEO,   /*!< RTP/AVP Video */
 	SDP_IMAGE,   /*!< Image udptl, not TCP or RTP */
 	SDP_TEXT,    /*!< RTP/AVP Realtime Text */
+	SDP_UNKNOWN, /*!< Unknown media type */
 };
 
 /*! \brief Authentication types - proxy or www authentication
@@ -968,10 +969,11 @@
 };
 
 /*! \brief Structure for remembering offered media in an INVITE, to make sure we reply
-	to all media streams. In theory. In practise, we try our best. */
+	to all media streams. */
 struct offered_media {
-	int order_offered;		/*!< Order the media was offered in. Not offered is 0 */
-	char codecs[128];
+	enum media_type type;		/*!< The type of media that was offered */
+	char *decline_m_line;		/*!< Used if the media type is unknown/unused or a media stream is declined */
+	AST_LIST_ENTRY(offered_media) next;
 };
 
 /*! Additional headers to send with MESSAGE method packet. */
@@ -1194,7 +1196,7 @@
 	 *
 	 * The large-scale changes would be a good idea for implementing during an SDP rewrite.
 	 */
-	struct offered_media offered_media[OFFERED_MEDIA_COUNT];
+	AST_LIST_HEAD_NOLOCK(, offered_media) offered_media;
 	struct ast_cc_config_params *cc_params;
 	struct sip_epa_entry *epa_entry;
 	int fromdomainport;                 /*!< Domain port to show in from field */




More information about the asterisk-commits mailing list