res_pjsip_session: Add ability to accept multiple sdp answers
authorGeorge Joseph <gjoseph@digium.com>
Tue, 19 Jun 2018 02:22:17 +0000 (20:22 -0600)
committerGeorge Joseph <gjoseph@digium.com>
Tue, 26 Jun 2018 13:05:34 +0000 (07:05 -0600)
pjproject by default currently will follow media forked during an INVITE
on outbound calls if the To tag is different on a subsequent response as
that on an earlier response.  We handle this correctly.  There have
been reported cases where the To tag is the same but we still need to
follow the media.  The pjproject patch in this commit adds the
capability to sip_inv and also adds the capability to control it at
runtime.  The original "different tag" behavior was always controllable
at runtime but we never did anything with it and left it to default to
TRUE.

So, along with the pjproject patch, this commit adds options to both the
system and endpoint objects to control the two behaviors, and a small
logic change to session_inv_on_media_update in res_pjsip_session to
control the behavior at the endpoint level.

The default behavior for "different tags" remains the same at TRUE and
the default for "same tag" is FALSE.

Change-Id: I64d071942b79adb2f0a4e13137389b19404fe3d6
ASTERISK-27936
Reported-by: Ross Beer

13 files changed:
CHANGES
configs/samples/pjsip.conf.sample
configure
configure.ac
contrib/ast-db-manage/config/versions/0be05c3a8225_add_early_media_options.py [new file with mode: 0644]
include/asterisk/autoconfig.h.in
include/asterisk/res_pjsip.h
res/res_pjsip.c
res/res_pjsip/config_system.c
res/res_pjsip/pjsip_configuration.c
res/res_pjsip_session.c
third-party/pjproject/configure.m4
third-party/pjproject/patches/0100-sip_inv-Add-option-to-accept-updated-SDP-on-same-To-.patch [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index e969d85..4bbe26d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -141,6 +141,30 @@ app_confbridge
    ConfbridgeWelcome has been added that will send a list of all
    current participants to a new participant.
 
+res_pjsip
+------------------
+  * Two new options have been added to the system and endpoint objects to
+    control whether, on outbound calls, Asterisk will accept updated SDP answers
+    during the initial INVITE transaction when 100rel is not in effect.
+    This usually happens when the INVITE is forked to multiple UASs and more
+    than one sends an SDP answer or when a single UAS needs to change a media
+    port to switch from custom ringback to the actual media destination.
+
+    The 'follow_early_media_forked' option sets whether Asterisk will accept
+    the updated SDP when the To tag on the subsequent response is different than
+    that on the the previous response.  This usually occurs in the forked INVITE
+    scenario. The default value is "yes" which is the current behavior.
+
+    The 'accept_multiple_sdp_answers' flag sets whether Asterisk will accept the
+    updated SDP when the To tag on the subsequent response is the same as that
+    on the previous response. This can occur when a UAS needs to switch media
+    ports from custom ringback to the final media path.  The default value is
+    "no" which is the current behavior.
+
+    These options have to be enabled system-wide in the system config section
+    of pjsip.conf as well as on individual endpoints that require the
+    functionality.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 15.3.0 to Asterisk 15.4.0 ------------
 ------------------------------------------------------------------------------
index a39a867..9b64001 100644 (file)
                         ; this mailbox will be used when notifying other modules
                         ; of MWI status changes.  If not set, incoming MWI
                         ; NOTIFYs are ignored.
+;follow_early_media_fork = ; On outgoing calls, if the UAS responds with
+                           ; different SDP attributes on subsequent 18X or 2XX
+                           ; responses (such as a port update) AND the To tag
+                           ; on the subsequent response is different than that
+                           ; on the previous one, follow it.  This usually
+                           ; happens when the INVITE is forked to multiple UASs
+                           ; and more than 1 sends an SDP answer.
+                           ; This option must also be enabled in the system
+                           ; section.
+                           ; (default: yes)
+;accept_multiple_sdp_answers =
+                           ; On outgoing calls, if the UAS responds with
+                           ; different SDP attributes on non-100rel 18X or 2XX
+                           ; responses (such as a port update) AND the To tag on
+                           ; the subsequent response is the same as that on the
+                           ; previous one, process it. This can happen when the
+                           ; UAS needs to change ports for some reason such as
+                           ; using a separate port for custom ringback.
+                           ; This option must also be enabled in the system
+                           ; section.
+                           ; (default: no)
 
 ;==========================AUTH SECTION OPTIONS=========================
 ;[auth]
                         ; Disabling this option has been known to cause interoperability
                         ; issues, so disable at your own risk.
                         ; (default: "yes")
+;follow_early_media_fork = ; On outgoing calls, if the UAS responds with
+                           ; different SDP attributes on subsequent 18X or 2XX
+                           ; responses (such as a port update) AND the To tag
+                           ; on the subsequent response is different than that
+                           ; on the previous one, follow it.  This usually
+                           ; happens when the INVITE is forked to multiple UASs
+                           ; and more than 1 sends an SDP answer.
+                           ; This option must also be enabled on endpoints that
+                           ; require this functionality.
+                           ; (default: yes)
+;accept_multiple_sdp_answers =
+                           ; On outgoing calls, if the UAS responds with
+                           ; different SDP attributes on non-100rel 18X or 2XX
+                           ; responses (such as a port update) AND the To tag on
+                           ; the subsequent response is the same as that on the
+                           ; previous one, process it. This can happen when the
+                           ; UAS needs to change ports for some reason such as
+                           ; using a separate port for custom ringback.
+                           ; This option must also be enabled on endpoints that
+                           ; require this functionality.
+                           ; (default: no)
 ;type=  ; Must be of type system (default: "")
 
 ;==========================GLOBAL SECTION OPTIONS=========================
index b7d3490..92c1853 100755 (executable)
--- a/configure
+++ b/configure
@@ -924,6 +924,10 @@ PBX_POPT
 POPT_DIR
 POPT_INCLUDE
 POPT_LIB
+PBX_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_DIR
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_INCLUDE
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_LIB
 PBX_PJSIP_TSX_LAYER_FIND_TSX2
 PJSIP_TSX_LAYER_FIND_TSX2_DIR
 PJSIP_TSX_LAYER_FIND_TSX2_INCLUDE
@@ -9272,6 +9276,9 @@ $as_echo "#define HAVE_PJSIP_AUTH_CLT_DEINIT 1" >>confdefs.h
 $as_echo "#define HAVE_PJSIP_TSX_LAYER_FIND_TSX2 1" >>confdefs.h
 
 
+$as_echo "#define HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS 1" >>confdefs.h
+
+
 
 
 
@@ -11402,6 +11409,18 @@ PBX_PJSIP_TSX_LAYER_FIND_TSX2=0
 
 
 
+
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_DESCRIP="PJSIP INVITE Accept Multiple SDP Answers"
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_OPTION=pjsip
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_DIR=${PJPROJECT_DIR}
+
+PBX_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS=0
+
+
+
+
+
+
 fi
 
 
@@ -25247,6 +25266,46 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
        CPPFLAGS="${saved_cppflags}"
     fi
 
+
+    if test "x${PBX_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS}" != "x1" -a "${USE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS}" != "no"; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking if \"pjsip_cfg()->endpt.accept_multiple_sdp_answers = 0;\" compiles using pjsip.h" >&5
+$as_echo_n "checking if \"pjsip_cfg()->endpt.accept_multiple_sdp_answers = 0;\" compiles using pjsip.h... " >&6; }
+       saved_cppflags="${CPPFLAGS}"
+       if test "x${PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_DIR}" != "x"; then
+           PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_INCLUDE="-I${PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_DIR}/include"
+       fi
+       CPPFLAGS="${CPPFLAGS} ${PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_INCLUDE}"
+
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+ #include <pjsip.h>
+int
+main ()
+{
+ pjsip_cfg()->endpt.accept_multiple_sdp_answers = 0;;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+               PBX_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS=1
+
+$as_echo "#define HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS 1" >>confdefs.h
+
+
+
+else
+         { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       CPPFLAGS="${saved_cppflags}"
+    fi
+
       LIBS="${saved_libs}"
       CPPFLAGS="${saved_cppflags}"
 
index 5da5afa..be8de33 100644 (file)
@@ -515,6 +515,7 @@ AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EVSUB_GRP_LOCK], [PJSIP EVSUB Group Lock suppo
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_INV_SESSION_REF], [PJSIP INVITE Session Reference Count support], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_AUTH_CLT_DEINIT], [pjsip_auth_clt_deinit support], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TSX_LAYER_FIND_TSX2], [pjsip_tsx_layer_find_tsx2 support], [PJPROJECT], [pjsip])
+AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS], [PJSIP INVITE Accept Multiple SDP Answers], [PJPROJECT], [pjsip])
 fi
 
 AST_EXT_LIB_SETUP([POPT], [popt], [popt])
@@ -2340,6 +2341,7 @@ if test "$USE_PJPROJECT" != "no" ; then
       CPPFLAGS="${CPPFLAGS} ${PJPROJECT_CFLAGS}"
       LIBS="${LIBS} ${PJPROJECT_LIB}"
       AST_C_COMPILE_CHECK([PJSIP_TLS_TRANSPORT_PROTO], [struct pjsip_tls_setting setting; int proto; proto = setting.proto;], [pjsip.h])
+      AST_C_COMPILE_CHECK([PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS], [pjsip_cfg()->endpt.accept_multiple_sdp_answers = 0;], [pjsip.h])
       LIBS="${saved_libs}"
       CPPFLAGS="${saved_cppflags}"
 
diff --git a/contrib/ast-db-manage/config/versions/0be05c3a8225_add_early_media_options.py b/contrib/ast-db-manage/config/versions/0be05c3a8225_add_early_media_options.py
new file mode 100644 (file)
index 0000000..d82b1d2
--- /dev/null
@@ -0,0 +1,37 @@
+"""Add early media options
+
+Revision ID: 0be05c3a8225
+Revises: d3e4284f8707
+Create Date: 2018-06-18 17:26:16.737692
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '0be05c3a8225'
+down_revision = 'd3e4284f8707'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+    yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+
+    op.add_column('ps_systems', sa.Column('follow_early_media_fork', yesno_values))
+    op.add_column('ps_systems', sa.Column('accept_multiple_sdp_answers', yesno_values))
+    op.add_column('ps_endpoints', sa.Column('follow_early_media_fork', yesno_values))
+    op.add_column('ps_endpoints', sa.Column('accept_multiple_sdp_answers', yesno_values))
+
+def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_systems_follow_early_media_fork_yesno_values','ps_systems')
+        op.drop_constraint('ck_ps_systems_accept_multiple_sdp_answers_yesno_values','ps_systems')
+        op.drop_constraint('ck_ps_endpoints_follow_early_media_fork_yesno_values','ps_endpoints')
+        op.drop_constraint('ck_ps_endpoints_accept_multiple_sdp_answers_yesno_values','ps_endpoints')
+    op.drop_column('ps_systems', 'follow_early_media_fork')
+    op.drop_column('ps_systems', 'accept_multiple_sdp_answers')
+    op.drop_column('ps_endpoints', 'follow_early_media_fork')
+    op.drop_column('ps_endpoints', 'accept_multiple_sdp_answers')
index 39d2e80..def4efd 100644 (file)
 /* Define to 1 if PJPROJECT has the pjsip_get_dest_info support feature. */
 #undef HAVE_PJSIP_GET_DEST_INFO
 
+/* Define if your system has the PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
+   headers. */
+#undef HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
+
 /* Define to 1 if PJPROJECT has the PJSIP INVITE Session Reference Count
    support feature. */
 #undef HAVE_PJSIP_INV_SESSION_REF
index 028051a..b94269a 100644 (file)
@@ -643,6 +643,10 @@ struct ast_sip_media_rtp_configuration {
        unsigned int timeout;
        /*! Number of seconds before terminating channel due to lack of RTP (when on hold) */
        unsigned int timeout_hold;
+       /*! Follow forked media with a different To tag */
+       unsigned int follow_early_media_fork;
+       /*! Accept updated SDPs on non-100rel 18X and 2XX responses with the same To tag */
+       unsigned int accept_multiple_sdp_answers;
 };
 
 /*!
index b6a736b..9a6b310 100644 (file)
                                                changes.  If not set, incoming MWI NOTIFYs are ignored.
                                        </para></description>
                                </configOption>
+                               <configOption name="follow_early_media_fork">
+                                       <synopsis>Follow SDP forked media when To tag is different</synopsis>
+                                       <description><para>
+                                               On outgoing calls, if the UAS responds with different SDP attributes
+                                               on subsequent 18X or 2XX responses (such as a port update) AND the
+                                               To tag on the subsequent response is different than that on the previous
+                                               one, follow it. This usually happens when the INVITE is forked to multiple
+                                               UASs and more than one sends an SDP answer.
+                                               </para>
+                                               <note><para>
+                                                       This option must also be enabled in the <literal>system</literal>
+                                                       section for it to take effect here.
+                                               </para></note>
+                                       </description>
+                               </configOption>
+                               <configOption name="accept_multiple_sdp_answers" default="no">
+                                       <synopsis>Accept multiple SDP answers on non-100rel responses</synopsis>
+                                       <description><para>
+                                               On outgoing calls, if the UAS responds with different SDP attributes
+                                               on non-100rel 18X or 2XX responses (such as a port update) AND the
+                                               To tag on the subsequent response is the same as that on the previous one,
+                                               process the updated SDP.  This can happen when the UAS needs to change ports
+                                               for some reason such as using a separate port for custom ringback.
+                                               </para>
+                                               <note><para>
+                                                       This option must also be enabled in the <literal>system</literal>
+                                                       section for it to take effect here.
+                                               </para></note>
+                                       </description>
+                               </configOption>
                        </configObject>
                        <configObject name="auth">
                                <synopsis>Authentication type</synopsis>
                                                request is too large.  See RFC 3261 section 18.1.1.
                                        </para></description>
                                </configOption>
+                               <configOption name="follow_early_media_fork">
+                                       <synopsis>Follow SDP forked media when To tag is different</synopsis>
+                                       <description><para>
+                                               On outgoing calls, if the UAS responds with different SDP attributes
+                                               on subsequent 18X or 2XX responses (such as a port update) AND the
+                                               To tag on the subsequent response is different than that on the previous
+                                               one, follow it.
+                                               </para>
+                                               <note><para>
+                                                       This option must also be enabled on endpoints that require
+                                                       this functionality.
+                                               </para></note>
+                                       </description>
+                               </configOption>
+                               <configOption name="accept_multiple_sdp_answers">
+                                       <synopsis>Follow SDP forked media when To tag is the same</synopsis>
+                                       <description><para>
+                                               On outgoing calls, if the UAS responds with different SDP attributes
+                                               on non-100rel 18X or 2XX responses (such as a port update) AND the
+                                               To tag on the subsequent response is the same as that on the previous one,
+                                               process the updated SDP.
+                                               </para>
+                                               <note><para>
+                                                       This option must also be enabled on endpoints that require
+                                                       this functionality.
+                                               </para></note>
+                                       </description>
+                               </configOption>
                                <configOption name="type">
                                        <synopsis>Must be of type 'system'.</synopsis>
                                </configOption>
index ed2b5d2..65e4e2c 100644 (file)
@@ -52,6 +52,13 @@ struct system_config {
        } threadpool;
        /*! Nonzero to disable switching from UDP to TCP transport */
        unsigned int disable_tcp_switch;
+       /*!
+        * Although early media is enabled in pjproject by default, it's only
+        * enabled when the To tags are different. These options allow turning
+        * on or off the feature for different tags and same tags.
+        */
+       unsigned int follow_early_media_fork;
+       unsigned int accept_multiple_sdp_answers;
 };
 
 static struct ast_threadpool_options sip_threadpool_options = {
@@ -96,6 +103,16 @@ static int system_apply(const struct ast_sorcery *system_sorcery, void *obj)
        pjsip_cfg()->tsx.t1 = system->timert1;
        pjsip_cfg()->tsx.td = system->timerb;
 
+       pjsip_cfg()->endpt.follow_early_media_fork = system->follow_early_media_fork;
+#ifdef HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
+       pjsip_cfg()->endpt.accept_multiple_sdp_answers = system->accept_multiple_sdp_answers;
+#else
+       if (system->accept_multiple_sdp_answers) {
+               ast_log(LOG_WARNING,
+                       "The accept_multiple_sdp_answers flag is not supported in this version of pjproject. Ignoring\n");
+       }
+#endif
+
        if (system->compactheaders) {
                extern pj_bool_t pjsip_use_compact_form;
 
@@ -184,6 +201,10 @@ int ast_sip_initialize_system(void)
                        OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.max_size));
        ast_sorcery_object_field_register(system_sorcery, "system", "disable_tcp_switch", "yes",
                        OPT_BOOL_T, 1, FLDSET(struct system_config, disable_tcp_switch));
+       ast_sorcery_object_field_register(system_sorcery, "system", "follow_early_media_fork", "yes",
+                       OPT_BOOL_T, 1, FLDSET(struct system_config, follow_early_media_fork));
+       ast_sorcery_object_field_register(system_sorcery, "system", "accept_multiple_sdp_answers", "no",
+                       OPT_BOOL_T, 1, FLDSET(struct system_config, accept_multiple_sdp_answers));
 
        ast_sorcery_load(system_sorcery);
 
index 80983af..f44ceb0 100644 (file)
@@ -1902,6 +1902,8 @@ int ast_res_pjsip_initialize_configuration(void)
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "bundle", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.bundle));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "webrtc", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_endpoint, media.webrtc));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "incoming_mwi_mailbox", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, incoming_mwi_mailbox));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "follow_early_media_fork", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.follow_early_media_fork));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "accept_multiple_sdp_answers", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.accept_multiple_sdp_answers));
 
        if (ast_sip_initialize_sorcery_transport()) {
                ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
index 8b1012e..1ec6901 100644 (file)
@@ -4053,6 +4053,42 @@ static void session_inv_on_media_update(pjsip_inv_session *inv, pj_status_t stat
                return;
        }
 
+       if (session->endpoint) {
+               int bail = 0;
+
+               /*
+                * If following_fork is set, then this is probably the result of a
+                * forked INVITE and SDP asnwers coming from the different fork UAS
+                * destinations.  In this case updated_sdp_answer will also be set.
+                *
+                * If only updated_sdp_answer is set, then this is the non-forking
+                * scenario where the same UAS just needs to change something like
+                * the media port.
+                */
+
+               if (inv->following_fork) {
+                       if (session->endpoint->media.rtp.follow_early_media_fork) {
+                               ast_debug(3, "Following early media fork with different To tags\n");
+                       } else {
+                               ast_debug(3, "Not following early media fork with different To tags\n");
+                               bail = 1;
+                       }
+               }
+#ifdef HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
+               else if (inv->updated_sdp_answer) {
+                       if (session->endpoint->media.rtp.accept_multiple_sdp_answers) {
+                               ast_debug(3, "Accepting updated SDP with same To tag\n");
+                       } else {
+                               ast_debug(3, "Ignoring updated SDP answer with same To tag\n");
+                               bail = 1;
+                       }
+               }
+#endif
+               if (bail) {
+                       return;
+               }
+       }
+
        if ((status != PJ_SUCCESS) || (pjmedia_sdp_neg_get_active_local(inv->neg, &local) != PJ_SUCCESS) ||
                (pjmedia_sdp_neg_get_active_remote(inv->neg, &remote) != PJ_SUCCESS)) {
                ast_channel_hangupcause_set(session->channel, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
index 42482b2..9b70e09 100644 (file)
@@ -87,6 +87,7 @@ AC_DEFUN([_PJPROJECT_CONFIGURE],
        AC_DEFINE([HAVE_PJSIP_INV_SESSION_REF], 1, [Define if your system has PJSIP_INV_SESSION_REF])
        AC_DEFINE([HAVE_PJSIP_AUTH_CLT_DEINIT], 1, [Define if your system has pjsip_auth_clt_deinit declared.])
        AC_DEFINE([HAVE_PJSIP_TSX_LAYER_FIND_TSX2], 1, [Define if your system has pjsip_tsx_layer_find_tsx2 declared.])
+       AC_DEFINE([HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS], 1, [Define if your system has HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS declared.])
 
        AC_SUBST([PJPROJECT_BUNDLED])
        AC_SUBST([PJPROJECT_DIR])
diff --git a/third-party/pjproject/patches/0100-sip_inv-Add-option-to-accept-updated-SDP-on-same-To-.patch b/third-party/pjproject/patches/0100-sip_inv-Add-option-to-accept-updated-SDP-on-same-To-.patch
new file mode 100644 (file)
index 0000000..cfcc9e8
--- /dev/null
@@ -0,0 +1,215 @@
+From 13e20772cd3c8735a6b78e30391a33f3eba4c023 Mon Sep 17 00:00:00 2001
+From: George Joseph <gjoseph@digium.com>
+Date: Fri, 22 Jun 2018 09:33:34 -0600
+Subject: [PATCH] sip_inv:  Add option to accept updated SDP on same To tag
+
+Currently, setting pjsip_cfg()->endpt.follow_early_media_fork allows
+sip_inv to process media updates when the To tag is different.  There
+are some cases where media updates need to be processed when the tags
+are the same.  Since removing the requirement for different tags would
+change default behavior, a new option "accept_multiple_sdp_answers"
+has been added along with a new pjsip_inv_session flag
+"updated_sdp_answer" to indicate under which condition we're
+updating.
+
+The logic was also updated to more closely follow RFC6337 in that
+if 100rel is efffect, do not accept updated SDPs.
+
+See
+https://tools.ietf.org/html/rfc6337#section-3.1.1
+for more information.
+---
+ pjsip/include/pjsip-ua/sip_inv.h |  2 ++
+ pjsip/include/pjsip/sip_config.h | 25 ++++++++++++++++
+ pjsip/src/pjsip-ua/sip_inv.c     | 62 +++++++++++++++++++++++++---------------
+ pjsip/src/pjsip/sip_config.c     |  3 +-
+ 4 files changed, 68 insertions(+), 24 deletions(-)
+
+diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h
+index 1bb7b8adc..77ef070c3 100644
+--- a/pjsip/include/pjsip-ua/sip_inv.h
++++ b/pjsip/include/pjsip-ua/sip_inv.h
+@@ -442,6 +442,8 @@ struct pjsip_inv_session
+     pj_bool_t          following_fork;            /**< Internal, following
+                                                        forked media?      */
+     pj_atomic_t               *ref_cnt;                   /**< Reference counter. */
++    pj_bool_t            updated_sdp_answer;        /**< SDP answer just been
++                                                       updated?           */
+ };
+diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
+index b3a9468e2..b7cf6feed 100644
+--- a/pjsip/include/pjsip/sip_config.h
++++ b/pjsip/include/pjsip/sip_config.h
+@@ -157,6 +157,17 @@ typedef struct pjsip_cfg_t
+        */
+       pj_bool_t disable_secure_dlg_check;
++        /**
++         * Accept multiple SDP answers on non-reliable 18X responses and the 2XX
++         * response when they are all received from the same source (same To tag).
++         *
++         * See also:
++         * https://tools.ietf.org/html/rfc6337#section-3.1.1
++         *
++         * Default is PJSIP_ACCEPT_MULTIPLE_SDP_ANSWERS.
++         */
++        pj_bool_t accept_multiple_sdp_answers;
++
+     } endpt;
+     /** Transaction layer settings. */
+@@ -402,6 +413,20 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
+ #endif
++/**
++ * Accept multiple SDP answers on non-reliable 18X responses and the 2XX
++ * response when they are all received from the same source (same To tag).
++ *
++ * This option can also be controlled at run-time by the
++ * \a accept_multiple_sdp_answers setting in pjsip_cfg_t.
++ *
++ * Default is PJ_FALSE.
++ */
++#ifndef PJSIP_ACCEPT_MULTIPLE_SDP_ANSWERS
++#   define PJSIP_ACCEPT_MULTIPLE_SDP_ANSWERS        PJ_TRUE
++#endif
++
++
+ /**
+  * Specify whether "alias" param should be added to the Via header
+  * in any outgoing request with connection oriented transport.
+diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
+index c9686a088..c22726bad 100644
+--- a/pjsip/src/pjsip-ua/sip_inv.c
++++ b/pjsip/src/pjsip-ua/sip_inv.c
+@@ -162,6 +162,7 @@ struct tsx_inv_data
+     pj_bool_t          retrying;  /* Resend (e.g. due to 401/407)         */
+     pj_str_t           done_tag;  /* To tag in RX response with answer    */
+     pj_bool_t          done_early;/* Negotiation was done for early med?  */
++    pj_bool_t          done_early_rel;/* Early med was realiable?         */
+     pj_bool_t          has_sdp;   /* Message with SDP?                    */
+ };
+@@ -2000,18 +2001,20 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+     /* Initialize info that we are following forked media */
+     inv->following_fork = PJ_FALSE;
++    inv->updated_sdp_answer = PJ_FALSE;
+     /* MUST NOT do multiple SDP offer/answer in a single transaction,
+-     * EXCEPT if:
+-     *        - this is an initial UAC INVITE transaction (i.e. not re-INVITE), and
+-     *        - the previous negotiation was done on an early media (18x) and
+-     *    this response is a final/2xx response, and
+-     *  - the 2xx response has different To tag than the 18x response
+-     *    (i.e. the request has forked).
++     * EXCEPT previous nego was in 18x (early media) and any of the following
++     * condition is met:
++     *  - Non-forking scenario:
++     *          - 'accept_multiple_sdp_answers' is set, and
++     *    - previous early response was not reliable (rfc6337 section 3.1.1).
++     *  - Forking scenario:
++     *    - This response has different To tag than the previous response, and
++     *    - This response is 18x/2xx (early or final). If this is 18x,
++     *      only do multiple SDP nego if 'follow_early_media_fork' is set.
+      *
+-     * The exception above is to add a rudimentary support for early media
+-     * forking (sample case: custom ringback). See this ticket for more
+-     * info: http://trac.pjsip.org/repos/ticket/657
++     * See also ticket #657, #1644, #1764 for more info.
+      */
+     if (tsx_inv_data->sdp_done) {
+       pj_str_t res_tag;
+@@ -2020,21 +2023,29 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+       res_tag = rdata->msg_info.to->tag;
+       st_code = rdata->msg_info.msg->line.status.code;
+-      /* Allow final/early response after SDP has been negotiated in early
+-       * media, IF this response is a final/early response with different
+-       * tag.
+-         * See ticket #1644 and #1764 for forked early media case.
+-       */
+-      if (tsx->role == PJSIP_ROLE_UAC &&
+-          (st_code/100 == 2 ||
+-           (st_code/10 == 18 /* st_code == 18x */
+-              && pjsip_cfg()->endpt.follow_early_media_fork)) &&
+-          tsx_inv_data->done_early &&
+-          pj_stricmp(&tsx_inv_data->done_tag, &res_tag))
++      if (tsx->role == PJSIP_ROLE_UAC && tsx_inv_data->done_early &&
++             (
++                 /* Non-forking scenario */
++                 (
++                     !tsx_inv_data->done_early_rel &&
++                     (st_code/100 == 2 || st_code/10 == 18) &&
++                       pjsip_cfg()->endpt.accept_multiple_sdp_answers &&
++                     !pj_stricmp(&tsx_inv_data->done_tag, &res_tag)
++                 )
++                 ||
++                 /* Forking scenario */
++                 (
++                     (st_code/100 == 2 ||
++                         (st_code/10 == 18 &&
++                             pjsip_cfg()->endpt.follow_early_media_fork)) &&
++                     pj_stricmp(&tsx_inv_data->done_tag, &res_tag)
++                 )
++             )
++         )
+       {
+           const pjmedia_sdp_session *reoffer_sdp = NULL;
+-          PJ_LOG(4,(inv->obj_name, "Received forked %s response "
++          PJ_LOG(4,(inv->obj_name, "Received %s response "
+                     "after SDP negotiation has been done in early "
+                     "media. Renegotiating SDP..",
+                     (st_code/10==18? "early" : "final" )));
+@@ -2054,7 +2065,9 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+               return status;
+           }
+-          inv->following_fork = PJ_TRUE;
++          inv->following_fork = !!pj_stricmp(&tsx_inv_data->done_tag,
++                                             &res_tag);
++          inv->updated_sdp_answer = PJ_TRUE;
+       } else {
+@@ -2135,6 +2148,7 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+               PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) 
+     {
+       int status_code;
++      pjsip_msg *msg = rdata->msg_info.msg;
+       /* This is an answer. 
+        * Process and negotiate remote answer.
+@@ -2161,8 +2175,10 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+        */
+       tsx_inv_data->sdp_done = 1;
+-      status_code = rdata->msg_info.msg->line.status.code;
++      status_code = msg->line.status.code;
+       tsx_inv_data->done_early = (status_code/100==1);
++      tsx_inv_data->done_early_rel = tsx_inv_data->done_early &&
++                                     pjsip_100rel_is_reliable(rdata);
+       pj_strdup(tsx->pool, &tsx_inv_data->done_tag, 
+                 &rdata->msg_info.to->tag);
+diff --git a/pjsip/src/pjsip/sip_config.c b/pjsip/src/pjsip/sip_config.c
+index 3576f351e..316824a00 100644
+--- a/pjsip/src/pjsip/sip_config.c
++++ b/pjsip/src/pjsip/sip_config.c
+@@ -34,7 +34,8 @@ pjsip_cfg_t pjsip_sip_cfg_var =
+        PJSIP_FOLLOW_EARLY_MEDIA_FORK,
+        PJSIP_REQ_HAS_VIA_ALIAS,
+        PJSIP_RESOLVE_HOSTNAME_TO_GET_INTERFACE,
+-       0
++       0,
++       PJSIP_ACCEPT_MULTIPLE_SDP_ANSWERS
+     },
+     /* Transaction settings */
+-- 
+2.14.4
+