;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
--- /dev/null
+"""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')
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;
};
/*!
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;
};
/*!
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);
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));
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));
If <literal>0</literal> no timeout. Time in fractional seconds.
</para></description>
</configOption>
+ <configOption name="qualify_2xx_only">
+ <synopsis>Only qualify contact if OPTIONS request returns 2XX</synopsis>
+ <description>
+ <para>If true only mark a contact as available if the qualify OPTIONS
+ request receives a 2XX response.
+ </para>
+ </description>
+ </configOption>
<configOption name="authenticate_qualify">
<synopsis>Authenticates a qualify challenge response if needed</synopsis>
<description>
If <literal>0</literal> no timeout. Time in fractional seconds.
</para></description>
</configOption>
+ <configOption name="qualify_2xx_only">
+ <synopsis>Only qualify contact if OPTIONS request returns 2XX</synopsis>
+ <description>
+ <para>If true only mark a contact as available if the qualify OPTIONS
+ request receives a 2XX response.
+ </para>
+ </description>
+ </configOption>
<configOption name="authenticate_qualify">
<synopsis>Authenticates a qualify challenge response if needed</synopsis>
<description>
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. */
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;
}
}
aor_options->authenticate_qualify = aor->authenticate_qualify;
+ aor_options->qualify_2xx_only = aor->qualify_2xx_only;
aor_options->qualify_timeout = aor->qualify_timeout;
/*
}
} 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;
}
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);
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);
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++;
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));
}