From 0fbb6de6f494159c1669a7eec755c8cf29e88784 Mon Sep 17 00:00:00 2001 From: Kent Date: Tue, 3 Dec 2024 08:24:44 -0600 Subject: [PATCH] res_pjsip: Add new AOR option "qualify_2xx_only" Added a new option "qualify_2xx_only" to the res_pjsip AOR qualify feature to mark a contact as available only if an OPTIONS request returns a 2XX response. If the option is not specified or is false, any response to the OPTIONS request marks the contact as available. UserNote: The pjsip.conf AOR section now has a "qualify_2xx_only" option that can be set so that only 2XX responses to OPTIONS requests used to qualify a contact will mark the contact as available. (cherry picked from commit 21dba60ff2a0d29d1bef5da33b77c7c7392d7156) --- configs/samples/pjsip.conf.sample | 2 ++ ...4bd6dd914fa_add_qualify_2xx_only_option.py | 34 +++++++++++++++++++ include/asterisk/res_pjsip.h | 4 +++ res/res_pjsip/location.c | 3 ++ res/res_pjsip/pjsip_config.xml | 16 +++++++++ res/res_pjsip/pjsip_options.c | 14 +++++++- res/res_pjsip_registrar.c | 1 + 7 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 contrib/ast-db-manage/config/versions/44bd6dd914fa_add_qualify_2xx_only_option.py diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index 32af1b2d13..c48f38240c 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -1219,6 +1219,8 @@ ;qualify_frequency=0 ; Interval at which to qualify an AoR via OPTIONS requests. ; (default: "0") ;qualify_timeout=3.0 ; Qualify timeout in fractional seconds (default: "3.0") +;qualify_2xx_only=no ; If true, only qualify AoR if OPTIONS request returns 2XX + ; (default: "no") ;authenticate_qualify=no ; Authenticates a qualify request if needed ; (default: "no") ;outbound_proxy= ; Proxy through which to send OPTIONS requests, a full SIP URI diff --git a/contrib/ast-db-manage/config/versions/44bd6dd914fa_add_qualify_2xx_only_option.py b/contrib/ast-db-manage/config/versions/44bd6dd914fa_add_qualify_2xx_only_option.py new file mode 100644 index 0000000000..bcdc79c8c5 --- /dev/null +++ b/contrib/ast-db-manage/config/versions/44bd6dd914fa_add_qualify_2xx_only_option.py @@ -0,0 +1,34 @@ +"""add qualify 2xx only option + +Revision ID: 44bd6dd914fa +Revises: 4f91fc18c979 +Create Date: 2024-12-02 21:08:41.130023 + +""" + +# revision identifiers, used by Alembic. +revision = '44bd6dd914fa' +down_revision = '4f91fc18c979' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import ENUM + +AST_BOOL_NAME = 'ast_bool_values' +AST_BOOL_VALUES = [ '0', '1', + 'off', 'on', + 'false', 'true', + 'no', 'yes' ] + +def upgrade(): + ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False) + op.add_column('ps_aors', sa.Column('qualify_2xx_only', ast_bool_values)) + op.add_column('ps_contacts', sa.Column('qualify_2xx_only', ast_bool_values)) + + +def downgrade(): + if op.get_context().bind.dialect.name == 'mssql': + op.drop_constraint('ck_ps_aors_qualify_2xx_only_ast_bool_values', 'ps_aors') + op.drop_constraint('ck_ps_contacts_qualify_2xx_only_ast_bool_values', 'ps_contacts') + op.drop_column('ps_aors', 'qualify_2xx_only') + op.drop_column('ps_contacts', 'qualify_2xx_only') diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index cdc71a5f74..d1061eeb07 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -423,6 +423,8 @@ struct ast_sip_contact { int via_port; /*! If true delete the contact on Asterisk restart/boot */ int prune_on_boot; + /*! If true only authenticate if OPTIONS response is 2XX */ + int qualify_2xx_only; }; /*! @@ -505,6 +507,8 @@ struct ast_sip_aor { char *voicemail_extension; /*! Whether to remove unavailable contacts over max_contacts at all or first if remove_existing is enabled */ unsigned int remove_unavailable; + /*! If true only authenticate if OPTIONS response is 2XX */ + int qualify_2xx_only; }; /*! diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c index a507b8903d..42934e5564 100644 --- a/res/res_pjsip/location.c +++ b/res/res_pjsip/location.c @@ -373,6 +373,7 @@ struct ast_sip_contact *ast_sip_location_create_contact(struct ast_sip_aor *aor, contact->expiration_time = expiration_time; contact->qualify_frequency = aor->qualify_frequency; contact->qualify_timeout = aor->qualify_timeout; + contact->qualify_2xx_only = aor->qualify_2xx_only; contact->authenticate_qualify = aor->authenticate_qualify; if (path_info && aor->support_path) { ast_string_field_set(contact, path, path_info); @@ -1394,6 +1395,7 @@ int ast_sip_initialize_sorcery_location(void) ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400); ast_sorcery_object_field_register(sorcery, "contact", "qualify_timeout", "3.0", OPT_DOUBLE_T, 0, FLDSET(struct ast_sip_contact, qualify_timeout)); + ast_sorcery_object_field_register(sorcery, "contact", "qualify_2xx_only", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_contact, qualify_2xx_only)); ast_sorcery_object_field_register(sorcery, "contact", "authenticate_qualify", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_contact, authenticate_qualify)); ast_sorcery_object_field_register(sorcery, "contact", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, outbound_proxy)); ast_sorcery_object_field_register(sorcery, "contact", "user_agent", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, user_agent)); @@ -1410,6 +1412,7 @@ int ast_sip_initialize_sorcery_location(void) ast_sorcery_object_field_register(sorcery, "aor", "default_expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, default_expiration)); ast_sorcery_object_field_register(sorcery, "aor", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_aor, qualify_frequency), 0, 86400); ast_sorcery_object_field_register(sorcery, "aor", "qualify_timeout", "3.0", OPT_DOUBLE_T, 0, FLDSET(struct ast_sip_aor, qualify_timeout)); + ast_sorcery_object_field_register(sorcery, "aor", "qualify_2xx_only", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, qualify_2xx_only)); ast_sorcery_object_field_register(sorcery, "aor", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, authenticate_qualify)); ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts)); ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing)); diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml index 71c73dc2ae..127499641c 100644 --- a/res/res_pjsip/pjsip_config.xml +++ b/res/res_pjsip/pjsip_config.xml @@ -1966,6 +1966,14 @@ If 0 no timeout. Time in fractional seconds. + + Only qualify contact if OPTIONS request returns 2XX + + If true only mark a contact as available if the qualify OPTIONS + request receives a 2XX response. + + + Authenticates a qualify challenge response if needed @@ -2192,6 +2200,14 @@ If 0 no timeout. Time in fractional seconds. + + Only qualify contact if OPTIONS request returns 2XX + + If true only mark a contact as available if the qualify OPTIONS + request receives a 2XX response. + + + Authenticates a qualify challenge response if needed diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c index e395ba0d7d..93354b9309 100644 --- a/res/res_pjsip/pjsip_options.c +++ b/res/res_pjsip/pjsip_options.c @@ -180,6 +180,8 @@ struct sip_options_aor { unsigned int available; /*! \brief Frequency to send OPTIONS requests to AOR contacts. 0 is disabled. */ unsigned int qualify_frequency; + /*! \brief If true only authenticate if OPTIONS response is 2XX */ + int qualify_2xx_only; /*! If true authenticate the qualify challenge response if needed */ int authenticate_qualify; /*! \brief Qualify timeout. 0 is diabled. */ @@ -799,7 +801,12 @@ static void qualify_contact_cb(void *token, pjsip_event *e) status = UNAVAILABLE; break; case PJSIP_EVENT_RX_MSG: - status = AVAILABLE; + if (contact_callback_data->aor_options->qualify_2xx_only && + (e->body.tsx_state.tsx->status_code < 200 || e->body.tsx_state.tsx->status_code >= 300)) { + status = UNAVAILABLE; + } else { + status = AVAILABLE; + } break; } @@ -1341,6 +1348,7 @@ static void sip_options_apply_aor_configuration(struct sip_options_aor *aor_opti } aor_options->authenticate_qualify = aor->authenticate_qualify; + aor_options->qualify_2xx_only = aor->qualify_2xx_only; aor_options->qualify_timeout = aor->qualify_timeout; /* @@ -2082,6 +2090,7 @@ static int has_qualify_changed (const struct ast_sip_contact *contact, const str } } else if (contact->qualify_frequency != aor_options->qualify_frequency || contact->authenticate_qualify != aor_options->authenticate_qualify + || contact->qualify_2xx_only != aor_options->qualify_2xx_only || ((int)(contact->qualify_timeout * 1000)) != ((int)(aor_options->qualify_timeout * 1000))) { return 1; } @@ -2530,6 +2539,7 @@ static char *cli_show_qualify_endpoint(struct ast_cli_entry *e, int cmd, struct ast_cli(a->fd, " * AOR '%s' on endpoint '%s'\n", aor_name, endpoint_name); ast_cli(a->fd, " Qualify frequency : %d sec\n", aor_options->qualify_frequency); ast_cli(a->fd, " Qualify timeout : %d ms\n", (int)(aor_options->qualify_timeout / 1000)); + ast_cli(a->fd, " Qualify 2xx only : %s\n", aor_options->qualify_2xx_only ? "yes" : "no"); ast_cli(a->fd, " Authenticate qualify : %s\n", aor_options->authenticate_qualify?"yes":"no"); ast_cli(a->fd, "\n"); ao2_ref(aor_options, -1); @@ -2569,6 +2579,7 @@ static char *cli_show_qualify_aor(struct ast_cli_entry *e, int cmd, struct ast_c ast_cli(a->fd, " * AOR '%s'\n", aor_name); ast_cli(a->fd, " Qualify frequency : %d sec\n", aor_options->qualify_frequency); ast_cli(a->fd, " Qualify timeout : %d ms\n", (int)(aor_options->qualify_timeout / 1000)); + ast_cli(a->fd, " Qualify 2xx only : %s\n", aor_options->qualify_2xx_only ? "yes" : "no"); ast_cli(a->fd, " Authenticate qualify : %s\n", aor_options->authenticate_qualify?"yes":"no"); ao2_ref(aor_options, -1); @@ -2764,6 +2775,7 @@ int ast_sip_format_contact_ami(void *obj, void *arg, int flags) ast_str_append(&buf, 0, "Path: %s\r\n", contact->path); ast_str_append(&buf, 0, "QualifyFrequency: %u\r\n", contact->qualify_frequency); ast_str_append(&buf, 0, "QualifyTimeout: %.3f\r\n", contact->qualify_timeout); + ast_str_append(&buf, 0, "Qualify2xxOnly: %d\r\n", contact->qualify_2xx_only); astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); ami->count++; diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c index f2b785bab9..5318c74620 100644 --- a/res/res_pjsip_registrar.c +++ b/res/res_pjsip_registrar.c @@ -913,6 +913,7 @@ static void register_aor_core(pjsip_rx_data *rdata, contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); contact_update->qualify_frequency = aor->qualify_frequency; contact_update->authenticate_qualify = aor->authenticate_qualify; + contact_update->qualify_2xx_only = aor->qualify_2xx_only; if (path_str) { ast_string_field_set(contact_update, path, ast_str_buffer(path_str)); }