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/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@369028 65c4cc65-6c06-0410-ace0-fbb531ad65f3
certified/11.2
Kinsey Moore 14 years ago
parent 88c9c6bef8
commit bf6ef69702

@ -5850,6 +5850,16 @@ static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi)
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)
{
@ -5953,6 +5963,8 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
ast_free(req);
}
offered_media_list_destroy(p);
if (p->chanvars) {
ast_variables_destroy(p->chanvars);
p->chanvars = NULL;
@ -8033,6 +8045,7 @@ struct sip_pvt *sip_alloc(ast_string_field callid, struct ast_sockaddr *addr,
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 @@ static int sockaddr_is_null_or_any(const struct ast_sockaddr *addr)
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
/* 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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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)) {
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 media offer because port number is zero\n");
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 media, not supported by Asterisk. Will try anyway...\n", numberofports);
ast_log(LOG_WARNING, "%d ports offered for text stream, 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);
res = -1;
goto process_sdp_cleanup;
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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))) {
/* 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;
}
/* 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 media offer because port number is zero\n");
ast_log(LOG_WARNING, "Ignoring image stream offer because port number is zero\n");
continue;
}
if (initialize_udptl(p)) {
res = -1;
goto process_sdp_cleanup;
ast_log(LOG_WARNING, "Failed to initialize UDPTL, declining image stream\n");
continue;
}
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;
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
}
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 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
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 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
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 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
}
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 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
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 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
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 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
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 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int old
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 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init,
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);

@ -467,6 +467,7 @@ enum media_type {
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 @@ struct sip_st_cfg {
};
/*! \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 @@ struct sip_pvt {
*
* 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 */

Loading…
Cancel
Save