Allow chan_sip to decline unwanted media streams
authorKinsey Moore <kmoore@digium.com>
Fri, 15 Jun 2012 17:13:20 +0000 (17:13 +0000)
committerKinsey Moore <kmoore@digium.com>
Fri, 15 Jun 2012 17:13:20 +0000 (17:13 +0000)
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

channels/chan_sip.c
channels/sip/include/sip.h

index 58dd23a..d913b93 100644 (file)
@@ -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);
index e8f079d..a9b7e02 100644 (file)
@@ -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 */