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) {