diff --git a/CHANGES b/CHANGES index e969d85bf47cb75b735f83aee29bbae2621967ac..4bbe26dc3bda4d2676b1e2c39c07499d6d4ceab1 100644 --- 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 ------------ ------------------------------------------------------------------------------ diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index a39a8675a6ed5f19508987d00d55e6522bd26084..9b64001663e48e7d7151274f5ede97bafc6b60f1 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -809,6 +809,27 @@ ; 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] @@ -961,6 +982,27 @@ ; 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========================= diff --git a/configure b/configure index b7d349011fb036795192a3759cf2dc49c09889d0..92c1853ba7ffcc4cfb6a1ed7629f05e61befb672 100755 --- 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 @@ -25238,6 +25257,46 @@ $as_echo "#define HAVE_PJSIP_TLS_TRANSPORT_PROTO 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 + + + 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; } diff --git a/configure.ac b/configure.ac index 5da5afa4dd7c1159a23f1febedcd78a07880b8de..be8de3311b9d990065c37dbb9d08b8ab21d127e2 100644 --- a/configure.ac +++ b/configure.ac @@ -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 index 0000000000000000000000000000000000000000..d82b1d2346870e86ea3748377f02396fa958bda6 --- /dev/null +++ b/contrib/ast-db-manage/config/versions/0be05c3a8225_add_early_media_options.py @@ -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') diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index 39d2e8088ce0fcdb5fddc6c868401267e231357a..def4efd52a9e20fbab3677baad8adf060ec2ed2a 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -623,6 +623,10 @@ /* 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 diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 028051a2ba8b00b18e1e0e801b811d1b01bf29af..b94269a32673e24194c5880922eba4882fd7fd0c 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -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; }; /*! diff --git a/res/res_pjsip.c b/res/res_pjsip.c index b6a736b2021795cd688c660e3a57f2e669f26135..9a6b310715a2dd4bf3053374d73c44881d121457 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -1080,6 +1080,36 @@ 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> @@ -1647,6 +1677,34 @@ 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> diff --git a/res/res_pjsip/config_system.c b/res/res_pjsip/config_system.c index ed2b5d232b27f6f431c77a9873ad97df19a80a85..65e4e2c05ec7f7101aaddc87071a8e828bcaf4c8 100644 --- a/res/res_pjsip/config_system.c +++ b/res/res_pjsip/config_system.c @@ -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); diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 80983af634cf1c28d2242cd6e4ef450acfacf692..f44ceb0c325b86adee4afd8670b3058c5bde1949 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -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"); diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 8b1012e5e005e7c0c638cc34656380c04a498423..1ec6901fc502e9f098eab4f9cdf19acd206dd20a 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -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); diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4 index 42482b2e5e6ba758a382f3c8375098df500ddcc4..9b70e09ff6d248bbfe903c13429c745f3c6f730d 100644 --- a/third-party/pjproject/configure.m4 +++ b/third-party/pjproject/configure.m4 @@ -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 index 0000000000000000000000000000000000000000..cfcc9e8826e2584288af24c46f2a36840eda28b3 --- /dev/null +++ b/third-party/pjproject/patches/0100-sip_inv-Add-option-to-accept-updated-SDP-on-same-To-.patch @@ -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 +