[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