[asterisk-commits] loren: branch loren/sdp-parser r275547 - in /team/loren/sdp-parser/channels/s...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sun Jul 11 02:29:36 CDT 2010


Author: loren
Date: Sun Jul 11 02:29:33 2010
New Revision: 275547

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=275547
Log:
continuation of r275546

Added:
    team/loren/sdp-parser/channels/sip/include/sdp_parser.h   (with props)
    team/loren/sdp-parser/channels/sip/sdp_parser.c   (with props)

Added: team/loren/sdp-parser/channels/sip/include/sdp_parser.h
URL: http://svnview.digium.com/svn/asterisk/team/loren/sdp-parser/channels/sip/include/sdp_parser.h?view=auto&rev=275547
==============================================================================
--- team/loren/sdp-parser/channels/sip/include/sdp_parser.h (added)
+++ team/loren/sdp-parser/channels/sip/include/sdp_parser.h Sun Jul 11 02:29:33 2010
@@ -1,0 +1,31 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Parser and processor for the Session Description Protocol (SDP)
+ *
+ * \author Loren Sands-Ramshaw <lorensr at gmail.com>
+ */
+
+
+/*! \brief Process SIP SDP offer, select formats and activate RTP channels
+  \param p representation of SIP dialog
+  \param req SIP data from UDP socket
+  \param t38action T.38 state (T.38 is the protocol for fax over IP)
+  \return 0 on success, negative value on errors
+
+  If offer is rejected, we will not change any properties of the call
+  Must be called after find_sdp().
+*/
+static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action);

Propchange: team/loren/sdp-parser/channels/sip/include/sdp_parser.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/loren/sdp-parser/channels/sip/include/sdp_parser.h
------------------------------------------------------------------------------
    svn:keywords = loren 7/11/2010

Propchange: team/loren/sdp-parser/channels/sip/include/sdp_parser.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/loren/sdp-parser/channels/sip/sdp_parser.c
URL: http://svnview.digium.com/svn/asterisk/team/loren/sdp-parser/channels/sip/sdp_parser.c?view=auto&rev=275547
==============================================================================
--- team/loren/sdp-parser/channels/sip/sdp_parser.c (added)
+++ team/loren/sdp-parser/channels/sip/sdp_parser.c Sun Jul 11 02:29:33 2010
@@ -1,0 +1,1489 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Parser and processor for the Session Description Protocol (SDP)
+ *
+ * \author Loren Sands-Ramshaw <lorensr at gmail.com>
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
+ *
+ *
+ * ******** General TODO:s
+ * \todo TODO
+ *
+ */
+
+#include "sip/include/sip.h"
+
+static const char *get_sdp_iterate(int* start, struct sip_request *req, const char *name);
+static char get_sdp_line(int *start, int stop, struct sip_request *req, const char **value);
+static int find_sdp(struct sip_request *req);
+static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action);
+static int parse_sdp_originator(const char *o, struct sdp_parsed *parsed_sdp);
+static int process_sdp_originator(struct sdp_parsed *sdp, struct sip_pvt *p);
+static int parse_sdp_connection(const char *c, struct ast_hostent *ast_hp);
+static int parse_sdp_attribute_sendonly(const char *a, int *sendonly);
+static int parse_sdp_attribute_audio(const char *a, struct sdp_parsed *sdp, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec);
+static void process_sdp_attribute_audio(struct sdp_parsed *sdp, struct sip_pvt *p);
+static int parse_sdp_attribute_video(const char *a, struct sdp_parsed *sdp, struct ast_rtp_codecs *newvideortp, int *last_rtpmap_codec);
+static int process_sdp_attribute_video(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newvideortp, int *last_rtpmap_codec);
+static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, int *last_rtpmap_codec);
+static int process_sdp_a_image(const char *a, struct sip_pvt *p);
+
+
+struct sdp_raw {
+};
+
+struct sdp_parsed {
+     int64_t rua_version;
+
+     //TODO make into arrays
+     long int framing;
+     unsigned int has_rtpmap;//TODO replace
+     int rtmap_codec;
+     char mime_subtype[128];
+     unsigned int sample_rate;
+     struct ast_rtp_codecs newauditortp;//TODO
+
+     unsigned int should_process_image;
+
+     unsigned int has_media[OFFERED_MEDIA_COUNT];
+     char *codecs[OFFERED_MEDIA_COUNT];
+     int port_number[OFFERED_MEDIA_COUNT];
+     struct hostent *hosts[OFFERED_MEDIA_COUNT];
+
+     int red_data_pt[10];		/* For T.140 red */
+
+     /* SRTP */
+     unsigned int secure_audio;
+     unsigned int secure_video;
+};
+
+static const int SDP_PARSE_FAILURE = -1;
+static const int SDP_UPDATE_SIP_FAILURE = -2;
+static const int SDP_NO_RTP_ALLOCATED = -3;
+static const int MIN_TIME_DESCRIPTION_LINE_LEN = 1; //XXX is time left out entirely?
+
+/*! \brief Lookup 'name' in the SDP starting
+ * at the 'start' line. Returns the matching line, and 'start'
+ * is updated with the next line number.
+ */
+static const char *get_sdp_iterate(int *start, struct sip_request *req, const char *name)
+{
+	int len = strlen(name);
+
+	while (*start < (req->sdp_start + req->sdp_count)) {
+		const char *r = get_body_by_line(REQ_OFFSET_TO_STR(req, line[(*start)++]), name, len, '=');
+		if (r[0] != '\0')
+			return r;
+	}
+
+	/* if the line was not found, ensure that *start points past the SDP */
+	(*start)++;
+
+	return "";
+}
+
+/*! \brief Fetches the next valid SDP line between the 'start' line
+ * (inclusive) and the 'stop' line (exclusive). Returns the type
+ * ('a', 'c', ...) and matching line in reference 'start' is updated
+ * with the next line number.
+ */
+static char get_sdp_line(int *start, int stop, struct sip_request *req, const char **value)
+{
+	char type = '\0';
+	const char *line = NULL;
+
+	if (stop > (req->sdp_start + req->sdp_count)) {
+		stop = req->sdp_start + req->sdp_count;
+	}
+
+	while (*start < stop) {
+		line = REQ_OFFSET_TO_STR(req, line[(*start)++]);
+		if (line[1] == '=') {
+			type = line[0];
+			*value = ast_skip_blanks(line + 2);
+			break;
+		}
+	}
+
+	return type;
+}
+
+
+/*!
+ * \brief Determine whether the Content-Length header is invalid or zero
+ * \param req the SIP request
+ * \retval 1 if has zero length
+ * \retval 0 if has non-zero length
+ */
+static int has_zero_content_length(struct sip_request *req)
+{
+	unsigned int length;
+	const char *content_length = get_header(req, "Content-Length");
+
+	if (!ast_strlen_zero(content_length)) {
+		if (sscanf(content_length, "%30u", &length) != 1) {
+			ast_log(LOG_WARNING, "Invalid Content-Length: %s\n", content_length);
+			return TRUE;
+		}
+
+		/* Content-Length of zero means there can't possibly be an
+             SDP here, even if the Content-Type says there is */
+		if (length == 0) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+/*!
+ * \brief Find the text length of the boundary marker
+ * \param content_type Content-Type section of SIP header
+ * \param search current point of search in content_type
+ * \return text length
+ */
+static int find_boundary_text_length(const char *content_type, const char *search)
+{
+	if ((search = strcasestr(content_type, ";boundary=")))
+		return 10;
+	else if ((search = strcasestr(content_type, "; boundary=")))
+		return 11;
+
+	return 0;
+}
+
+/*!
+ * \brief search for the boundary marker, the empty line delimiting headers from
+ * sdp part and the end boundary if it exists
+ * \param req the SIP request to process
+ * \param boundary copy of the stripped content-type
+ * \retval 1 if SDP found
+ * \retval 0 if SDP not found
+ *
+ * Also updates req->sdp_start and req->sdp_count to indicate where the SDP
+ * lives in the message body.
+ */
+static int boundary_marker_search(struct sip_request *req, char *boundary)
+{
+	unsigned int x;
+	int found_application_sdp = FALSE;
+	int found_end_of_headers = FALSE;
+
+	/* search for the boundary marker, the empty line delimiting headers from
+	   sdp part and the end boundry if it exists */
+
+	for (x = 0; x < (req->lines); x++) {
+		const char *line = REQ_OFFSET_TO_STR(req, line[x]);
+		if (!strncasecmp(line, boundary, strlen(boundary))){
+			if (found_application_sdp && found_end_of_headers) {
+				req->sdp_count = (x - 1) - req->sdp_start;
+				return TRUE;
+			}
+			found_application_sdp = FALSE;
+		}
+		if (!strcasecmp(line, "Content-Type: application/sdp"))
+			found_application_sdp = TRUE;
+
+		if (ast_strlen_zero(line)) {
+			if (found_application_sdp && !found_end_of_headers){
+				req->sdp_start = x;
+				found_end_of_headers = TRUE;
+			}
+		}
+	}
+	if (found_application_sdp && found_end_of_headers) {
+		req->sdp_count = x - req->sdp_start;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/*!
+  \brief Continues to determine whether and where a SIP message contains an SDP in its body
+  \param req the SIP request to process
+  \retval 1 if SDP found
+  \retval 0 if SDP not found
+
+
+  Also updates req->sdp_start and req->sdp_count to indicate where the SDP
+  lives in the message body.
+*/
+static int find_sdp_boundaries(struct sip_request *req, const char *content_type)
+{
+	const char *search;
+	char *boundary;
+	unsigned int boundary_text_length;
+	int boundaryisquoted = FALSE;
+
+	/* if there is no boundary marker, it's invalid */
+	if (0 == (boundary_text_length = find_boundary_text_length(content_type, search))) {
+		return FALSE;
+	}
+
+	search += boundary_text_length;
+
+	if (ast_strlen_zero(search))
+		return 0;
+
+	/* If the boundary is quoted with ", remove quote */
+	if (*search == '\"')  {
+		search++;
+		boundaryisquoted = TRUE;
+	}
+
+	/* make a duplicate of the string, with two extra characters
+	   at the beginning */
+	boundary = ast_strdupa(search - 2);
+	boundary[0] = boundary[1] = '-';
+
+	/* Remove final quote */
+	if (boundaryisquoted)
+		boundary[strlen(boundary) - 1] = '\0';
+
+	return boundary_marker_search(req, boundary);
+}
+
+/*!
+  \brief Determine whether a SIP message contains an SDP in its body
+  \param req the SIP request to process
+  \retval 1 if SDP found
+  \retval 0 if SDP not found
+
+
+  Also updates req->sdp_start and req->sdp_count to indicate where the SDP
+  lives in the message body.
+*/
+static int find_sdp(struct sip_request *req)
+{
+	const char *content_type;
+
+	if (has_zero_content_length(req)) {
+		return FALSE;
+	}
+
+	content_type = get_header(req, "Content-Type");
+
+	/* if the body contains only SDP, this is easy */
+	if (!strncasecmp(content_type, "application/sdp", 15)) {
+		req->sdp_start = 0;
+		req->sdp_count = req->lines;
+		return req->lines ? TRUE : FALSE;
+	}
+
+	/* if it's not multipart/mixed, there cannot be an SDP */
+	if (strncasecmp(content_type, "multipart/mixed", 15)) {
+		return FALSE;
+	}
+
+	return find_sdp_boundaries(req, content_type);
+}
+
+
+static int get_ip_and_port_from_sdp(struct sip_request *req, const enum media_type media, struct sockaddr_in *sin)
+{
+	const char *m;
+	const char *c;
+	int miterator = req->sdp_start;
+	int citerator = req->sdp_start;
+	int x = 0;
+	int numberofports;
+	int len;
+	char host[258] = ""; /*Initialize to empty so we will know if we have any input */
+	struct ast_hostent audiohp;
+	struct hostent *hp;
+
+	c = get_sdp_iterate(&citerator, req, "c");
+	if (sscanf(c, "IN IP4 %256s", host) != 1) {
+		ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
+		/* Continue since there may be a valid host in a c= line specific to the audio stream */
+	}
+	/* We only want the m and c lines for audio */
+	for (m = get_sdp_iterate(&miterator, req, "m"); !ast_strlen_zero(m); m = get_sdp_iterate(&miterator, req, "m")) {
+		if ((media == SDP_AUDIO && ((sscanf(m, "audio %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
+                                      (sscanf(m, "audio %30u RTP/AVP %n", &x, &len) == 1 && len > 0))) ||
+              (media == SDP_VIDEO && ((sscanf(m, "video %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
+                                      (sscanf(m, "video %30u RTP/AVP %n", &x, &len) == 1 && len > 0)))) {
+			/* See if there's a c= line for this media stream.
+			 * XXX There is no guarantee that we'll be grabbing the c= line for this
+			 * particular media stream here. However, this is the same logic used in process_sdp.
+			 */
+			c = get_sdp_iterate(&citerator, req, "c");
+			if (!ast_strlen_zero(c)) {
+				sscanf(c, "IN IP4 %256s", host);
+			}
+			break;
+		}
+	}
+
+	if (ast_strlen_zero(host) || x == 0) {
+		ast_log(LOG_WARNING, "Failed to read an alternate host or port in SDP. Expect %s problems\n", media == SDP_AUDIO ? "audio" : "video");
+		return -1;
+	}
+
+	hp = ast_gethostbyname(host, &audiohp);
+	if (!hp) {
+		ast_log(LOG_WARNING, "Could not look up IP address of alternate hostname. Expect %s problems\n", media == SDP_AUDIO? "audio" : "video");
+		return -1;
+	}
+
+	memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
+	sin->sin_port = htons(x);
+	return 0;
+}
+
+static struct sdp_raw extract_sdp(struct sip_request *req)
+{
+     struct sdp_raw sdp;
+     return sdp; //TODO
+}
+
+static int parse_sdp(struct sdp_raw *sdp, struct sdp_parsed *parsed_sdp)
+{
+     parsed_sdp->has_rtpmap = FALSE;
+
+     /* Iterators for SDP parsing */
+	int start = req->sdp_start;
+	int next = start;
+	int iterator = start;
+
+	/* Temporary vars for SDP parsing */
+	char type = '\0';
+	const char *value = NULL;
+	const char *m = NULL;           /* SDP media offer */
+	const char *nextm = NULL;
+	int len = -1;
+
+	/* Host information */
+	struct ast_hostent sessionhp;
+     struct ast_hostent ast_hosts[OFFERED_MEDIA_COUNT];
+
+	const char *codecs;
+	int codec;
+
+	/* Others */
+	int sendonly = -1;
+	int vsendonly = -1;
+	int numberofports;
+	int numberofmediastreams = 0;
+	int last_rtpmap_codec = 0;
+	int red_num_gen = 0;		/* For T.140 red */
+	char red_fmtp[100] = "empty";	/* For T.140 red */
+	int debug = sip_debug_test_pvt(p);
+
+	/* START UNKNOWN */
+	char buf[SIPBUFSIZE];
+	/* END UNKNOWN */
+
+	/* SRTP */
+	parsed_sdp->secure_audio = FALSE;
+	parsed_sdp->secure_video = FALSE;
+
+     for (int i = 0; i < OFFERED_MEDIA_COUNT; i++) {
+          parsed_sdp->port_number[i] = -1;
+          hosts[i] = NULL;
+     }
+
+	/* MAKE sure that the codec structures are all cleared out */
+	ast_rtp_codecs_payloads_clear(&parsed_sdp->newaudiortp, NULL);
+	ast_rtp_codecs_payloads_clear(&newvideortp, NULL);
+	ast_rtp_codecs_payloads_clear(&newtextrtp, NULL);
+
+	if (p->vrtp) {
+		ast_rtp_codecs_payloads_clear(&newvideortp, NULL);
+	}
+
+	if (p->trtp) {
+		ast_rtp_codecs_payloads_clear(&newtextrtp, NULL);
+	}
+
+	/* Scan for the first media stream (m=) line to limit scanning of globals */
+	nextm = get_sdp_iterate(&next, req, "m");
+	if (ast_strlen_zero(nextm)) {
+		ast_log(LOG_WARNING, "Insufficient information for SDP (m= not found)\n");
+ 		return -1;
+ 	}
+
+     sdp->should_process_image = FALSE;
+
+	/* Scan session level SDP parameters (lines before first media stream) */
+	while ((type = get_sdp_line(&iterator, next - MIN_TIME_DESCRIPTION_LINE_LEN, req, &value)) != '\0') {
+		int processed = FALSE;
+		switch (type) {
+		case 'o':
+               if (!parse_sdp_originator(value, parsed_sdp)) {
+                    return -1;
+               }
+               break;
+		case 'c':
+			if (parse_sdp_connection(value, &sessionhp)) {
+				processed = TRUE;
+				parsed_sdp->hosts[SDP_AUDIO] = &sessionhp.hp;
+				parsed_sdp->hosts[SDP_VIDEO] = parsed_sdp->hosts[SDP_AUDIO];
+				parsed_sdp->hosts[SDP_TEXT] = parsed_sdp->hosts[SDP_AUDIO];
+				parsed_sdp->hosts[SDP_IMAGE] = parsed_sdp->hosts[SDP_AUDIO];
+			}
+			break;
+		case 'a':
+			if (parse_sdp_attribute_sendonly(value, &sendonly)) {
+				processed = TRUE;
+				vsendonly = sendonly;
+			}
+			else if (parse_sdp_attribute_audio(value, parsed_sdp, &parsed_sdp->newaudiortp, &last_rtpmap_codec)) {
+				processed = TRUE;
+               }
+			else if (parse_sdp_attribute_video(value, parsed_sdp, &newvideortp, &last_rtpmap_codec)) {
+				processed = TRUE;
+               }
+			else if (parse_sdp_attribute_text(value, parsed_sdp, &newtextrtp, red_fmtp, &red_num_gen, parsed_sdp->red_data_pt, &last_rtpmap_codec)) {
+				processed = TRUE;
+               }
+			else {
+                    sdp->image_line = value;
+                    sdp->should_process_image = TRUE;
+				processed = TRUE;
+               }
+			break;
+		}
+
+		if (option_debug > 2)
+			ast_log(LOG_DEBUG, "Processing session-level SDP %c=%s... %s\n", type, value, (processed == TRUE)? "OK." : "UNSUPPORTED.");
+ 	}
+
+     for(int i = 0; i < OFFERED_MEDIA_COUNT; i++) {
+          parsed_sdp->has_media[i] = FALSE;
+     }
+
+	/* Scan media stream (m=) specific parameters loop */
+	while (!ast_strlen_zero(nextm)) {
+		char protocol[5] = {0,};
+		int x;
+
+		numberofports = 1;
+		len = -1;
+		start = next;
+		m = nextm;
+		iterator = next;
+		nextm = get_sdp_iterate(&next, req, "m");
+          
+          //TODO extract to fn 
+          
+		/* Search for audio media definition */
+		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)) {
+			if (!strcmp(protocol, "SAVP")) {
+				parsed_sdp->secure_audio = TRUE;
+			} else if (strcmp(protocol, "AVP")) {
+				ast_log(LOG_WARNING, "unknown SDP media protocol in offer: %s\n", protocol);
+				continue;
+			}
+			parsed_sdp->has_media[SDP_AUDIO] = TRUE;
+			numberofmediastreams++;
+			parsed_sdp->port_number[SDP_AUDIO] = x;
+
+			/* Scan through the RTP payload types specified in a "m=" line: */
+			codecs = m + len;
+               parsed_sdp->codecs[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, "Error in codec string '%s'\n", codecs);
+					return -1;
+				}
+				if (debug)
+					ast_verbose("Found RTP audio format %d\n", codec);
+				
+				ast_rtp_codecs_payloads_set_m_type(&parsed_sdp->newaudiortp, NULL, codec);
+			}
+               /* Search for video media definition */
+		} else 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)) {
+			if (!strcmp(protocol, "SAVP")) {
+				parsed_sdp->secure_video = TRUE;
+			} else if (strcmp(protocol, "AVP")) {
+				ast_log(LOG_WARNING, "unknown SDP media protocol in offer: %s\n", protocol);
+				continue;
+			}
+			parsed_sdp->has_media[SDP_VIDEO] = TRUE;
+			numberofmediastreams++;
+			parsed_sdp->port_number[SDP_VIDEO] = x;
+
+			/* Scan through the RTP payload types specified in a "m=" line: */
+			codecs = m + len;
+               parsed_sdp->codecs[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, "Error in codec string '%s'\n", codecs);
+					return -1;
+				}
+				if (debug)
+					ast_verbose("Found RTP video format %d\n", codec);
+				ast_rtp_codecs_payloads_set_m_type(&newvideortp, NULL, codec);
+			}
+               /* Search for text media definition */
+		} else 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)) {
+			parsed_sdp->has_media[TEXT] = TRUE;
+			numberofmediastreams++;
+			parsed_sdp->port_number[SDP_TEXT] = x;
+
+			/* Scan through the RTP payload types specified in a "m=" line: */
+			codecs = m + len;
+               parsed_sdp->codecs[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, "Error in codec string '%s'\n", codecs);
+					return -1;
+				}
+				if (debug)
+					ast_verbose("Found RTP text format %d\n", codec);
+				ast_rtp_codecs_payloads_set_m_type(&newtextrtp, NULL, codec);
+			}
+               /* Search for image media definition */
+               //TODO p->
+		} else if (p->udptl && ((sscanf(m, "image %30u udptl t38%n", &x, &len) == 1 && len > 0) ||
+                                  (sscanf(m, "image %30u UDPTL t38%n", &x, &len) == 1 && len > 0) )) {
+			parsed_sdp->has_media[SDP_IMAGE] = TRUE;
+			if (debug)
+				ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid);
+			parsed_sdp->port_number[SDP_IMAGE] = x;
+			numberofmediastreams++;
+
+		} else {
+			ast_log(LOG_WARNING, "Unsupported SDP media type in offer: %s\n", m);
+			continue;
+		}
+
+		/* Check for number of ports */
+		if (numberofports > 1)
+			ast_log(LOG_WARNING, "SDP offered %d ports for media, not supported by Asterisk. Will try anyway...\n", numberofports);
+		
+		/* Media stream specific parameters */
+		while ((type = get_sdp_line(&iterator, next - 1, req, &value)) != '\0') {
+			int processed = FALSE;
+
+               //TODO process_sdp_*
+
+			switch (type) {
+			case 'c':
+				if (parsed_sdp->has_media[SDP_AUDIO]) {
+					if (process_sdp_c(value, &ast_hosts[SDP_AUDIO])) {
+						processed = TRUE;
+						parsed_sdp->hosts[SDP_AUDIO] = &ast_hosts[SDP_AUDIO].hp;
+					}
+				} else if (parsed_sdp->has_media[SDP_VIDEO]) {
+					if (process_sdp_c(value, &ast_hosts[SDP_VIDEO])) {
+						processed = TRUE;
+						parsed_sdp->hosts[SDP_VIDEO] = &ast_hosts[SDP_VIDEO].hp;
+					}
+				} else if (parsed_sdp->has_media[SDP_TEXT]) {
+					if (process_sdp_c(value, &ast_hosts[SDP_TEXT])) {
+						processed = TRUE;
+						parsed_sdp->hosts[SDP_TEXT] = &ast_hosts[SDP_TEXT].hp;
+					}
+				} else if (parsed_sdp->has_media[SDP_IMAGE]) {
+					if (process_sdp_c(value, &ast_hosts[SDP_IMAGE])) {
+						processed = TRUE;
+						parsed_sdp->hosts[SDP_IMAGE] = &ast_hosts[SDP_IMAGE].hp;
+					}
+				}
+				break;
+			case 'a':
+				/* Audio specific scanning */
+				if (parsed_sdp->has_media[SDP_AUDIO]) {
+					if (process_sdp_a_sendonly(value, &sendonly))
+						processed = TRUE;
+					else if (process_crypto(p, p->rtp, &p->srtp, value))
+						processed = TRUE;
+					else if (process_sdp_a_audio(value, p, &parsed_sdp->newaudiortp, &last_rtpmap_codec))
+						processed = TRUE;
+				}
+				/* Video specific scanning */
+				else if (parsed_sdp->has_media[SDP_VIDEO]) {
+					if (process_sdp_a_sendonly(value, &vsendonly))
+						processed = TRUE;
+					else if (process_crypto(p, p->vrtp, &p->vsrtp, value))
+						processed = TRUE;
+					else if (process_sdp_a_video(value, p, &newvideortp, &last_rtpmap_codec))
+						processed = TRUE;
+				}
+				/* Text (T.140) specific scanning */
+				else if (parsed_sdp->has_media[SDP_TEXT]) {
+					if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, parsed_sdp->red_data_pt, &last_rtpmap_codec))
+						processed = TRUE;
+					else if (process_crypto(p, p->trtp, &p->tsrtp, value))
+						processed = TRUE;
+				}
+				/* Image (T.38 FAX) specific scanning */
+				else if (parsed_sdp->has_media[SDP_IMAGE]) {
+					if (process_sdp_a_image(value, p))
+						processed = TRUE;
+				}
+				break;
+			}
+
+			if (option_debug > 2)
+				ast_log(LOG_DEBUG, "Processing media-level (%s) SDP %c=%s... %s\n",
+                            (parsed_sdp->has_media[SDP_AUDIO] == TRUE)? "audio" : (parsed_sdp->has_media[SDP_VIDEO] == TRUE)? "video" : "image",
+                            type, value,
+                            (processed == TRUE)? "OK." : "UNSUPPORTED.");
+		}
+	}
+
+
+	/* Sanity checks */
+	if (!parsed_sdp->hosts[SDP_AUDIO] && !parsed_sdp->hosts[SDP_VIDEO] && !parsed_sdp->hosts[SDP_TEXT] && !parsed_sdp->hosts[SDP_IMAGE]) {
+		ast_log(LOG_WARNING, "Insufficient information in SDP (c=)...\n");
+		return -1;
+	}
+
+	if (parsed_sdp->port_number[SDP_AUDIO] == -1 && parsed_sdp->port_number[SDP_VIDEO] == -1 && parsed_sdp->port_number[SDP_IMAGE] == -1  && parsed_sdp->port_number[SDP_TEXT] == -1) {
+		/* No acceptable offer found in SDP  - we have no ports */
+		/* Do not change RTP or VRTP if this is a re-invite */
+		ast_log(LOG_WARNING, "Failing due to no acceptable offer found\n");
+		return -2;
+	}
+
+	if (numberofmediastreams > 3) {
+		/* We have too many fax, audio and/or video and/or text media streams, fail this offer */
+		ast_log(LOG_WARNING, "Faling due to too many media streams\n");
+		return -3;
+	}
+	
+	return 0;
+
+     
+
+     return -1;
+}
+
+static int initialize_sip(struct sip_pvt *p)
+{
+    	/* 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));
+
+
+	/* default: novideo and notext set */
+	p->novideo = TRUE;
+	p->notext = TRUE;
+
+     return -1; //TODO
+}
+
+static int update_sip_dialog(struct sip_pvt *p, struct sdp_parsed *parsed_sdp, int t38action)
+{
+
+	if (parsed_sdp->secure_audio && !(p->srtp && (ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)))) {
+		ast_log(LOG_WARNING, "Can't provide secure audio requested in SDP offer\n");
+		return -4;
+	}
+
+	if (!parsed_sdp->secure_audio && p->srtp) {
+		ast_log(LOG_WARNING, "We are requesting SRTP, but they responded without it!\n");
+		return -4;
+	}
+
+	if (parsed_sdp->secure_video && !(p->vsrtp && (ast_test_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK)))) {
+		ast_log(LOG_WARNING, "Can't provide secure video requested in SDP offer\n");
+		return -4;
+	}
+
+	if (!p->novideo && !parsed_sdp->secure_video && p->vsrtp) {
+		ast_log(LOG_WARNING, "We are requesting SRTP, but they responded without it!\n");
+		return -4;
+	}
+
+	if (!(parsed_sdp->secure_audio || parsed_sdp->secure_video) && ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP)) {
+		ast_log(LOG_WARNING, "Matched device setup to use SRTP, but request was not!\n");
+		return -4;
+	}
+
+     initialize_sip(p);
+
+     /* -- Session-level parameters -- */
+
+	/* If we end up receiving SDP that doesn't actually modify the session we don't want to treat this as a fatal
+	 * error. We just want to ignore the SDP and let the rest of the packet be handled as normal.
+	 */
+     if (parsed_sdp->has_originator) {
+          if (!process_sdp_originator(parsed_sdp, p)) {
+               return (p->session_modify == FALSE) ? 0 : -1;
+          }
+     }
+
+     if (sdp->should_process_image) {
+          process_sdp_attribute_image(value, p);
+     }
+
+     for (int i = 0; i < OFFERED_MEDIA_COUNT; i++) {
+          if (parsed_sdp->has_media[i]) {
+              p->offered_media[i].offered = TRUE;
+
+              if (i == SDP_VIDEO) {
+                   p->novideo = FALSE;
+              }
+
+              if (i == SDP_TEXT) {
+                   p->notext = FALSE;
+              }
+
+              if (i != SDP_IMAGE) {
+                   ast_copy_string(p->offered_media[i].codecs, parsed_sdp->codecs[i], sizeof(p->offered_media[i].codecs));
+              } else if (p->t38.state != T38_ENABLED) {
+                   memset(&p->t38.their_parms, 0, sizeof(p->t38.their_parms));
+              }
+          }
+     }
+
+
+     struct sockaddr_in sin[OFFERED_MEDIA_COUNT];
+
+	/* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */	
+	format_t peercapability = 0, vpeercapability = 0, tpeercapability = 0;
+	int peernoncodeccapability = 0, vpeernoncodeccapability = 0, tpeernoncodeccapability = 0;
+
+	struct ast_rtp_codecs newvideortp, newtextrtp;
+	format_t newjointcapability;				/* Negotiated capability */
+	format_t newpeercapability;
+	int newnoncodeccapability;
+
+
+	if (parsed_sdp->port_number[SDP_IMAGE] == -1) {
+		change_t38_state(p, T38_DISABLED);
+	}
+
+	/* Now gather all of the codecs that we are asked for: */
+	ast_rtp_codecs_payload_formats(&parsed_sdp->newaudiortp, &peercapability, &peernoncodeccapability);
+	ast_rtp_codecs_payload_formats(&newvideortp, &vpeercapability, &vpeernoncodeccapability);
+	ast_rtp_codecs_payload_formats(&newtextrtp, &tpeercapability, &tpeernoncodeccapability);
+
+	newjointcapability = p->capability & (peercapability | vpeercapability | tpeercapability);
+	newpeercapability = (peercapability | vpeercapability | tpeercapability);
+	newnoncodeccapability = p->noncodeccapability & peernoncodeccapability;
+
+	if (debug) {
+		/* shame on whoever coded this.... */
+		char s1[SIPBUFSIZE], s2[SIPBUFSIZE], s3[SIPBUFSIZE], s4[SIPBUFSIZE], s5[SIPBUFSIZE];
+
+		ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s/text=%s, combined - %s\n",
+                      ast_getformatname_multiple(s1, SIPBUFSIZE, p->capability),
+                      ast_getformatname_multiple(s2, SIPBUFSIZE, peercapability),
+                      ast_getformatname_multiple(s3, SIPBUFSIZE, vpeercapability),
+                      ast_getformatname_multiple(s4, SIPBUFSIZE, tpeercapability),
+                      ast_getformatname_multiple(s5, SIPBUFSIZE, newjointcapability));
+	}
+	if (debug) {
+		struct ast_str *s1 = ast_str_alloca(SIPBUFSIZE);
+		struct ast_str *s2 = ast_str_alloca(SIPBUFSIZE);
+		struct ast_str *s3 = ast_str_alloca(SIPBUFSIZE);
+
+		ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n",
+                      ast_rtp_lookup_mime_multiple2(s1, p->noncodeccapability, 0, 0),
+                      ast_rtp_lookup_mime_multiple2(s2, peernoncodeccapability, 0, 0),
+                      ast_rtp_lookup_mime_multiple2(s3, newnoncodeccapability, 0, 0));
+	}
+	if (!newjointcapability && (parsed_sdp->port_number[SDP_AUDIO] != -1)) {
+		ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n");
+		/* Do NOT Change current setting */
+		return -1;
+	}
+
+	/* Setup audio address and port */
+	if (p->rtp) {
+		if (parsed_sdp->port_number[SDP_AUDIO] > 0) {
+			sin[SDP_AUDIO].sin_family = AF_INET;
+			sin[SDP_AUDIO].sin_port = htons(parsed_sdp->port_number[SDP_AUDIO]);
+			memcpy(&sin[SDP_AUDIO].sin_addr, parsed_sdp->hosts[SDP_AUDIO]->h_addr, sizeof(sin[SDP_AUDIO].sin_addr));
+			ast_rtp_instance_set_remote_address(p->rtp, &sin[SDP_AUDIO]);
+			if (debug)
+				ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin[SDP_AUDIO].sin_addr), ntohs(sin[SDP_AUDIO].sin_port));
+			/* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since                                                                                                    
+			   they are acceptable */
+			p->jointcapability = newjointcapability;                /* Our joint codec profile for this call */
+			p->peercapability = newpeercapability;                  /* The other sides capability in latest offer */
+			p->jointnoncodeccapability = newnoncodeccapability;     /* DTMF capabilities */
+
+			if (ast_test_flag(&p->flags[1], SIP_PAGE2_PREFERRED_CODEC)) { /* respond with single most preferred joint codec, limiting the other side's choice */
+				p->jointcapability = ast_codec_choose(&p->prefs, p->jointcapability, 1);
+			}
+
+			ast_rtp_codecs_payloads_copy(&parsed_sdp->newaudiortp, ast_rtp_instance_get_codecs(p->rtp), p->rtp);
+
+			if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) {
+				ast_clear_flag(&p->flags[0], SIP_DTMF);
+				if (newnoncodeccapability & AST_RTP_DTMF) {
+					/* XXX Would it be reasonable to drop the DSP at this point? XXX */
+					ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833);
+					/* Since RFC2833 is now negotiated we need to change some properties of the RTP stream */
+					ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, 1);
+					ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
+				} else {
+					ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
+				}
+			}
+		} else if (parsed_sdp->port_number[SDP_IMAGE] > 0) {
+			if (debug)
+				ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session.\n");
+		} else {
+			ast_rtp_instance_stop(p->rtp);
+			if (debug)
+				ast_verbose("Peer doesn't provide audio\n");
+		}
+	}
+
+	/* Setup video address and port */
+	if (p->vrtp) {
+		if (parsed_sdp->port_number[SDP_VIDEO] > 0) {
+			sin[SDP_VIDEO].sin_family = AF_INET;
+			sin[SDP_VIDEO].sin_port = htons(parsed_sdp->port_number[SDP_VIDEO]);
+			memcpy(&sin[SDP_VIDEO].sin_addr, parsed_sdp->hosts[SDP_VIDEO]->h_addr, sizeof(sin[SDP_VIDEO].sin_addr));
+			ast_rtp_instance_set_remote_address(p->vrtp, &sin[SDP_VIDEO]);
+			if (debug) 
+				ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(sin[SDP_VIDEO].sin_addr), ntohs(sin[SDP_VIDEO].sin_port));
+			ast_rtp_codecs_payloads_copy(&newvideortp, ast_rtp_instance_get_codecs(p->vrtp), p->vrtp);
+		} else {
+			ast_rtp_instance_stop(p->vrtp);
+			if (debug)
+				ast_verbose("Peer doesn't provide video\n");
+		}
+	}
+
+	/* Setup text address and port */
+	if (p->trtp) {
+		if (parsed_sdp->port_number[SDP_TEXT] > 0) {
+			sin[SDP_TEXT].sin_family = AF_INET;
+			sin[SDP_TEXT].sin_port = htons(parsed_sdp->port_number[SDP_TEXT]);
+			memcpy(&sin[SDP_TEXT].sin_addr, parsed_sdp->hosts[SDP_TEXT]->h_addr, sizeof(sin[SDP_TEXT].sin_addr));
+			ast_rtp_instance_set_remote_address(p->trtp, &sin[SDP_TEXT]);
+			if (debug) 
+				ast_verbose("Peer T.140 RTP is at port %s:%d\n", ast_inet_ntoa(sin[SDP_TEXT].sin_addr), ntohs(sin[SDP_TEXT].sin_port));
+			if ((p->jointcapability & AST_FORMAT_T140RED)) {
+				p->red = 1;
+				ast_rtp_red_init(p->trtp, 300, parsed_sdp->red_data_pt, 2);
+			} else {
+				p->red = 0;
+			}
+			ast_rtp_codecs_payloads_copy(&newtextrtp, ast_rtp_instance_get_codecs(p->trtp), p->trtp);
+		} else {
+			ast_rtp_instance_stop(p->trtp);
+			if (debug)
+				ast_verbose("Peer doesn't provide T.140\n");
+		}
+	}
+	/* Setup image address and port */
+	if (p->udptl) {
+		if (parsed_sdp->port_number[SDP_IMAGE] > 0) {
+			sin[SDP_IMAGE].sin_family = AF_INET;
+			sin[SDP_IMAGE].sin_port = htons(parsed_sdp->port_number[SDP_IMAGE]);
+			if (ast_test_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP) && ast_test_flag(&p->flags[1], SIP_PAGE2_UDPTL_DESTINATION)) {
+				struct sockaddr_in remote_address = { 0, };
+				ast_rtp_instance_get_remote_address(p->rtp, &remote_address);
+				if (remote_address.sin_addr.s_addr) {
+					memcpy(&sin[SDP_IMAGE], &remote_address, sizeof(sin[SDP_IMAGE]));
+					if (debug) {
+						ast_log(LOG_DEBUG, "Peer T.38 UDPTL is set behind NAT and with destination, destination address now %s\n", ast_inet_ntoa(sin[SDP_IMAGE].sin_addr));
+					}
+				}
+			} else {
+				memcpy(&sin[SDP_IMAGE].sin_addr, parsed_sdp->hosts[SDP_IMAGE]->h_addr, sizeof(sin[SDP_IMAGE].sin_addr));
+			}
+			ast_udptl_set_peer(p->udptl, &sin[SDP_IMAGE]);
+			if (debug)
+				ast_debug(1,"Peer T.38 UDPTL is at port %s:%d\n", ast_inet_ntoa(sin[SDP_IMAGE].sin_addr), ntohs(sin[SDP_IMAGE].sin_port));
+
+			/* verify the far max ifp can be calculated. this requires far max datagram to be set. */
+			if (!ast_udptl_get_far_max_datagram(p->udptl)) {
+				/* setting to zero will force a default if none was provided by the SDP */
+				ast_udptl_set_far_max_datagram(p->udptl, 0);
+			}
+
+			/* Remote party offers T38, we need to update state */
+			if ((t38action == SDP_T38_ACCEPT) &&
+			    (p->t38.state == T38_LOCAL_REINVITE)) {
+				change_t38_state(p, T38_ENABLED);
+			} else if ((t38action == SDP_T38_INITIATE) &&
+                          p->owner && p->lastinvite) {
+				change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */
+				/* If fax detection is enabled then send us off to the fax extension */
+				if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_T38)) {
+					ast_channel_lock(p->owner);
+					if (strcmp(p->owner->exten, "fax")) {
+						const char *target_context = S_OR(p->owner->macrocontext, p->owner->context);
+						ast_channel_unlock(p->owner);
+						if (ast_exists_extension(p->owner, target_context, "fax", 1, p->owner->cid.cid_num)) {
+							ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension due to peer T.38 re-INVITE\n", p->owner->name);
+							pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", p->owner->exten);
+							if (ast_async_goto(p->owner, target_context, "fax", 1)) {
+								ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", p->owner->name, target_context);
+							}
+						} else {
+							ast_log(LOG_NOTICE, "T.38 re-INVITE detected but no fax extension\n");
+						}
+					} else {
+						ast_channel_unlock(p->owner);
+					}
+				}
+			}
+		} else {
+			ast_udptl_stop(p->udptl);
+			if (debug)
+				ast_debug(1, "Peer doesn't provide T.38 UDPTL\n");
+		}
+	}
+
+	if ((parsed_sdp->port_number[SDP_AUDIO] == -1) && (p->t38.state != T38_DISABLED)) {
+		ast_debug(3, "Have T.38 but no audio, accepting offer anyway\n");
+		return 0;
+     }
+
+	/* Ok, we're going with this offer */
+	ast_debug(2, "We're settling with these formats: %s\n", ast_getformatname_multiple(buf, SIPBUFSIZE, p->jointcapability));
+
+	if (!p->owner) 	/* There's no open channel owning us so we can return here. For a re-invite or so, we proceed */
+		return 0;
+
+	ast_debug(4, "We have an owner, now see if we need to change this call\n");
+
+	if (!(p->owner->nativeformats & p->jointcapability) && (p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
+		if (debug) {
+			char s1[SIPBUFSIZE], s2[SIPBUFSIZE];
+			ast_debug(1, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n",
+                         ast_getformatname_multiple(s1, SIPBUFSIZE, p->jointcapability),
+                         ast_getformatname_multiple(s2, SIPBUFSIZE, p->owner->nativeformats));
+		}
+		p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability) | (p->capability & tpeercapability);
+		ast_set_read_format(p->owner, p->owner->readformat);
+		ast_set_write_format(p->owner, p->owner->writeformat);
+	}
+	
+	if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && sin[SDP_AUDIO].sin_addr.s_addr && (!sendonly || sendonly == -1)) {
+		ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
+		/* Activate a re-invite */
+		ast_queue_frame(p->owner, &ast_null_frame);
+		/* Queue Manager Unhold event */
+		append_history(p, "Unhold", "%s", req->data->str);
+		if (sip_cfg.callevents)
+			ast_manager_event(p->owner, EVENT_FLAG_CALL, "Hold",
+                                 "Status: Off\r\n"
+                                 "Channel: %s\r\n"
+                                 "Uniqueid: %s\r\n",
+                                 p->owner->name,
+                                 p->owner->uniqueid);
+		if (sip_cfg.notifyhold)
+			sip_peer_hold(p, FALSE);
+		ast_clear_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */
+	} else if (!sin[SDP_AUDIO].sin_addr.s_addr || (sendonly && sendonly != -1)) {
+		int already_on_hold = ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD);
+		ast_queue_control_data(p->owner, AST_CONTROL_HOLD,
+                                 S_OR(p->mohsuggest, NULL),

[... 508 lines stripped ...]



More information about the asterisk-commits mailing list