diff --git a/contrib/ast-db-manage/config/versions/539f68bede2c_add_peer_supported_to_100rel.py b/contrib/ast-db-manage/config/versions/539f68bede2c_add_peer_supported_to_100rel.py new file mode 100644 index 0000000000..6293dabb0f --- /dev/null +++ b/contrib/ast-db-manage/config/versions/539f68bede2c_add_peer_supported_to_100rel.py @@ -0,0 +1,57 @@ +"""Add peer_supported to 100rel + +Revision ID: 539f68bede2c +Revises: 9f3692b1654b +Create Date: 2022-08-10 09:36:16.576049 + +""" + +# revision identifiers, used by Alembic. +revision = '539f68bede2c' +down_revision = '9f3692b1654b' + +from alembic import op +from sqlalchemy.dialects.postgresql import ENUM +import sqlalchemy as sa + + +OLD_ENUM = ['no', 'required', 'yes'] +NEW_ENUM = ['no', 'required', 'peer_supported', 'yes'] + +old_type = sa.Enum(*OLD_ENUM, name='pjsip_100rel_values') +new_type = sa.Enum(*NEW_ENUM, name='pjsip_100rel_values_v2') + +def upgrade(): + context = op.get_context() + + # Upgrading to this revision WILL clear your directmedia values. + if context.bind.dialect.name != 'postgresql': + op.alter_column('ps_endpoints', '100rel', + type_=new_type, + existing_type=old_type) + else: + enum = ENUM(*NEW_ENUM, name='pjsip_100rel_values_v2') + enum.create(op.get_bind(), checkfirst=False) + + op.execute('ALTER TABLE ps_endpoints ALTER COLUMN 100rel TYPE' + ' pjsip_100rel_values_v2 USING' + ' 100rel::text::pjsip_100rel_values_v2') + + ENUM(name="pjsip_100rel_values").drop(op.get_bind(), checkfirst=False) + +def downgrade(): + context = op.get_context() + + if context.bind.dialect.name != 'postgresql': + op.alter_column('ps_endpoints', '100rel', + type_=old_type, + existing_type=new_type) + else: + enum = ENUM(*OLD_ENUM, name='pjsip_100rel_values') + enum.create(op.get_bind(), checkfirst=False) + + op.execute('ALTER TABLE ps_endpoints ALTER COLUMN 100rel TYPE' + ' pjsip_100rel_values USING' + ' 100rel::text::pjsip_100rel_values') + + ENUM(name="pjsip_100rel_values_v2").drop(op.get_bind(), checkfirst=False) diff --git a/doc/CHANGES-staging/res_pjsip_100rel_option.txt b/doc/CHANGES-staging/res_pjsip_100rel_option.txt new file mode 100644 index 0000000000..81af80ac03 --- /dev/null +++ b/doc/CHANGES-staging/res_pjsip_100rel_option.txt @@ -0,0 +1,6 @@ +Subject: res_pjsip + +A new option named "peer_supported" has been added to the endpoint option +100rel. When set to this option, Asterisk sends provisional responses +reliably if the peer supports it. If the peer does not support reliable +provisional responses, Asterisk sends them normally. diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index dcdf2b3920..3cba04aec5 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -449,6 +449,20 @@ struct ast_sip_contact_wrapper { struct ast_sip_contact *contact; }; +/*! + * \brief 100rel modes for SIP endpoints + */ +enum ast_sip_100rel_mode { + /*! Do not support 100rel. (no) */ + AST_SIP_100REL_UNSUPPORTED = 0, + /*! As UAC, indicate 100rel support in Supported header. (yes) */ + AST_SIP_100REL_SUPPORTED, + /*! As UAS, send 1xx responses reliably, if the UAC indicated its support. Otherwise same as AST_SIP_100REL_SUPPORTED. (peer_supported) */ + AST_SIP_100REL_PEER_SUPPORTED, + /*! Require the use of 100rel. (required) */ + AST_SIP_100REL_REQUIRED, +}; + /*! * \brief DTMF modes for SIP endpoints */ @@ -981,6 +995,8 @@ struct ast_sip_endpoint { AST_STRING_FIELD_EXTENDED(geoloc_incoming_call_profile); /*! The name of the geoloc profile to apply when Asterisk sends a call to this endpoint */ AST_STRING_FIELD_EXTENDED(geoloc_outgoing_call_profile); + /*! 100rel mode to use with this endpoint */ + enum ast_sip_100rel_mode rel100; }; /*! URI parameter for symmetric transport */ diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml index d78efe32ad..3cec9a41f1 100644 --- a/res/res_pjsip/pjsip_config.xml +++ b/res/res_pjsip/pjsip_config.xml @@ -32,9 +32,33 @@ Allow support for RFC3262 provisional ACK tags - - - + + If set to no, do not support transmission of + reliable provisional responses. As UAS, if an incoming request contains 100rel + in the Required header, it is rejected with 420 Bad Extension. + + + If set to required, require provisional responses to + be sent and received reliably. As UAS, incoming requests without 100rel + in the Supported header are rejected with 421 Extension Required. As UAC, + outgoing requests will have 100rel in the Required header. + + + If set to peer_supported, send provisional responses + reliably if the request by the peer contained 100rel in the Supported or + Require header. As UAS, if an incoming request contains 100rel in the Supported + header, send 1xx responses reliably. If the request by the peer does not contain 100rel + in the Supported and Require header, send responses normally. As UAC, outgoing requests + will contain 100rel in the Supported header. + + + If set to yes, indicate the support of reliable provisional + responses and PRACK them if required by the peer. As UAS, if the incoming request + contains 100rel in the Supported header but not in the Required header, send 1xx + responses normally. If the incoming request contains 100rel in the Required header, + send 1xx responses reliably. As UAC add 100rel to the Supported header and PRACK 1xx + responses if required. + diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 6bc978dae5..cf42f88932 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -182,9 +182,16 @@ static int prack_handler(const struct aco_option *opt, struct ast_variable *var, if (ast_true(var->value)) { endpoint->extensions.flags |= PJSIP_INV_SUPPORT_100REL; + endpoint->rel100 = AST_SIP_100REL_SUPPORTED; + } else if (!strcasecmp(var->value, "peer_supported")) { + endpoint->extensions.flags |= PJSIP_INV_SUPPORT_100REL; + endpoint->rel100 = AST_SIP_100REL_PEER_SUPPORTED; } else if (!strcasecmp(var->value, "required")) { endpoint->extensions.flags |= PJSIP_INV_REQUIRE_100REL; - } else if (!ast_false(var->value)){ + endpoint->rel100 = AST_SIP_100REL_REQUIRED; + } else if (ast_false(var->value)) { + endpoint->rel100 = AST_SIP_100REL_UNSUPPORTED; + } else { return -1; } @@ -195,10 +202,12 @@ static int prack_to_str(const void *obj, const intptr_t *args, char **buf) { const struct ast_sip_endpoint *endpoint = obj; - if (endpoint->extensions.flags & PJSIP_INV_REQUIRE_100REL) { - *buf = "required"; - } else if (endpoint->extensions.flags & PJSIP_INV_SUPPORT_100REL) { + if (endpoint->rel100 == AST_SIP_100REL_SUPPORTED) { *buf = "yes"; + } else if (endpoint->rel100 == AST_SIP_100REL_PEER_SUPPORTED) { + *buf = "peer_supported"; + } else if (endpoint->rel100 == AST_SIP_100REL_REQUIRED) { + *buf = "required"; } else { *buf = "no"; } diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 858faf365a..2b2d92ead5 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -3789,8 +3789,23 @@ static pjsip_inv_session *pre_session_setup(pjsip_rx_data *rdata, const struct a pjsip_dialog *dlg; pjsip_inv_session *inv_session; unsigned int options = endpoint->extensions.flags; + const pj_str_t STR_100REL = { "100rel", 6}; + unsigned int i; pj_status_t dlg_status = PJ_EUNKNOWN; + /* + * If 100rel is set to "peer_supported" on the endpoint and the peer indicated support for 100rel + * in the Supported header, send 1xx responses reliably by adding PJSIP_INV_REQUIRE_100REL to pjsip_inv_options flags. + */ + if (endpoint->rel100 == AST_SIP_100REL_PEER_SUPPORTED && rdata->msg_info.supported != NULL) { + for (i = 0; i < rdata->msg_info.supported->count; ++i) { + if (pj_stricmp(&rdata->msg_info.supported->values[i], &STR_100REL) == 0) { + options |= PJSIP_INV_REQUIRE_100REL; + break; + } + } + } + if (pjsip_inv_verify_request(rdata, &options, NULL, NULL, ast_sip_get_pjsip_endpoint(), &tdata) != PJ_SUCCESS) { if (tdata) { if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) {