res_pjsip_outbound_registration: registration stops due to fatal 4xx response
authorKevin Harwell <kharwell@digium.com>
Wed, 21 Oct 2015 17:22:19 +0000 (12:22 -0500)
committerKevin Harwell <kharwell@digium.com>
Fri, 23 Oct 2015 14:42:46 +0000 (09:42 -0500)
During outbound registration it is possible to receive a fatal (any permanent/
non-temporary 4xx, 5xx, 6xx) response from the registrar that is simply due
to a problem with the registrar itself. Upon receiving the failure response
Asterisk terminates outbound registration for the given endpoint.

This patch adds an option, 'fatal_retry_interval', that when set continues
outbound registration at the given interval up to 'max_retries' upon receiving
a fatal response.

ASTERISK-25485 #close

Change-Id: Ibc2c7b47164ac89cc803433c0bbe7063bfa143a2

CHANGES
configs/samples/pjsip.conf.sample
contrib/ast-db-manage/config/versions/28ce1e718f05_add_fatal_response_interval.py [new file with mode: 0644]
res/res_pjsip_outbound_registration.c

diff --git a/CHANGES b/CHANGES
index 88743cc..f3e3771 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -162,6 +162,13 @@ res_pjsip
    will be used instead. The new SIP resolver provides NAPTR support, improved
    SRV support, and AAAA record support.
 
+res_pjsip_outbound_registration
+-------------------------------
+* A new 'fatal_retry_interval' option has been added to outbound registration.
+  When set (default is zero), and upon receiving a failure response to an
+  outbound registration, registration is retried at the given interval up to
+  'max_retries'.
+
 CEL Backends
 ------------------
 
index 06befb4..9302fb2 100644 (file)
                         ; registration is unsuccessful (default: "60")
 ;forbidden_retry_interval=0     ; Interval used when receiving a 403 Forbidden
                                 ; response (default: "0")
+;fatal_retry_interval=0 ; Interval used when receiving a fatal response.
+                        ; (default: "0") A fatal response is any permanent
+                        ; failure (non-temporary 4xx, 5xx, 6xx) response
+                        ; received from the registrar. NOTE - if also set
+                        ; the 'forbidden_retry_interval' takes precedence
+                        ; over this one when a 403 is received. Also, if
+                        ; 'auth_rejection_permanent' equals 'yes' a 401 and
+                        ; 407 become subject to this retry interval.
 ;server_uri=    ; SIP URI of the server to register against (default: "")
 ;transport=     ; Transport used for outbound authentication (default: "")
 ;type=  ; Must be of type registration (default: "")
diff --git a/contrib/ast-db-manage/config/versions/28ce1e718f05_add_fatal_response_interval.py b/contrib/ast-db-manage/config/versions/28ce1e718f05_add_fatal_response_interval.py
new file mode 100644 (file)
index 0000000..8c499ae
--- /dev/null
@@ -0,0 +1,22 @@
+"""add fatal_response_interval
+
+Revision ID: 28ce1e718f05
+Revises: 154177371065
+Create Date: 2015-10-20 17:57:45.560585
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '28ce1e718f05'
+down_revision = '154177371065'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_registrations', sa.Column('fatal_retry_interval', sa.Integer))
+
+
+def downgrade():
+    op.drop_column('ps_registrations', 'fatal_retry_interval')
index 6bdba56..038cc0b 100644 (file)
                                                buggy registrars.
                                        </para></description>
                                </configOption>
+                               <configOption name="fatal_retry_interval" default="0">
+                                       <synopsis>Interval used when receiving a Fatal response.</synopsis>
+                                       <description><para>
+                                               If a fatal response is received, chan_pjsip will wait
+                                               <replaceable>fatal_retry_interval</replaceable> seconds before
+                                               attempting registration again. If 0 is specified, chan_pjsip will not
+                                               retry after receiving a fatal (non-temporary 4xx, 5xx, 6xx) response.
+                                               Setting this to a non-zero value may go against a "SHOULD NOT" in RFC3261,
+                                               but can be used to work around buggy registrars.</para>
+                                               <note><para>if also set the <replaceable>forbidden_retry_interval</replaceable>
+                                               takes precedence over this one when a 403 is received.
+                                               Also, if <replaceable>auth_rejection_permanent</replaceable> equals 'yes' then
+                                               a 401 and 407 become subject to this retry interval.</para></note>
+                                       </description>
+                               </configOption>
                                <configOption name="server_uri">
                                        <synopsis>SIP URI of the server to register against</synopsis>
                                        <description><para>
@@ -277,6 +292,8 @@ struct sip_outbound_registration {
        unsigned int retry_interval;
        /*! \brief Interval at which retries should occur for permanent responses */
        unsigned int forbidden_retry_interval;
+       /*! \brief Interval at which retries should occur for all permanent responses */
+       unsigned int fatal_retry_interval;
        /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
        unsigned int auth_rejection_permanent;
        /*! \brief Maximum number of retries permitted */
@@ -312,6 +329,8 @@ struct sip_outbound_registration_client_state {
        unsigned int retry_interval;
        /*! \brief Interval at which retries should occur for permanent responses */
        unsigned int forbidden_retry_interval;
+       /*! \brief Interval at which retries should occur for all permanent responses */
+       unsigned int fatal_retry_interval;
        /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
        unsigned int auth_rejection_permanent;
        /*! \brief Determines whether SIP Path support should be advertised */
@@ -799,6 +818,14 @@ static int handle_registration_response(void *data)
                        schedule_registration(response->client_state, response->client_state->forbidden_retry_interval);
                        ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
                                server_uri, client_uri, response->client_state->forbidden_retry_interval);
+               } else if (response->client_state->fatal_retry_interval
+                          && response->client_state->retries < response->client_state->max_retries) {
+                       /* Some kind of fatal failure response received, so retry according to configured interval */
+                       response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
+                       response->client_state->retries++;
+                       schedule_registration(response->client_state, response->client_state->fatal_retry_interval);
+                       ast_log(LOG_WARNING, "'%d' fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
+                               response->code, server_uri, client_uri, response->client_state->fatal_retry_interval);
                } else {
                        /* Finally if there's no hope of registering give up */
                        response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
@@ -1186,6 +1213,7 @@ static int sip_outbound_registration_perform(void *data)
        }
        state->client_state->retry_interval = registration->retry_interval;
        state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
+       state->client_state->fatal_retry_interval = registration->fatal_retry_interval;
        state->client_state->max_retries = registration->max_retries;
        state->client_state->retries = 0;
        state->client_state->support_path = registration->support_path;
@@ -1909,6 +1937,7 @@ static int load_module(void)
        ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
        ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "60", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval));
        ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "forbidden_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, forbidden_retry_interval));
+       ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "fatal_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, fatal_retry_interval));
        ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries));
        ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "auth_rejection_permanent", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, auth_rejection_permanent));
        ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, outbound_auths_to_var_list, 0, 0);