From b44ce141e5e418ff9e32fa628c35b451f74bf398 Mon Sep 17 00:00:00 2001 From: Kinsey Moore Date: Mon, 30 Sep 2013 15:57:11 +0000 Subject: [PATCH] chan_sip: Allow Asterisk to retry after 403 on register This adds a global option in chan_sip to allow it to continue attempting registration if a 403 is received, clearing the cached nonce and treating it as a non-fatal response. Normally, this would cause registration attempts to that endpoint to stop. This also adds a similar per-outbound-registration option to chan_pjsip which allows the retry interval to be altered for 403 responses to REGISTER requests. (closes issue ASTERISK-17138) Review: https://reviewboard.asterisk.org/r/2874/ Reported by: Rudi ........ Merged revisions 400137 from http://svn.asterisk.org/svn/asterisk/branches/1.8 ........ Merged revisions 400140 from http://svn.asterisk.org/svn/asterisk/branches/11 ........ Merged revisions 400141 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@400142 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- CHANGES | 4 +++ channels/chan_sip.c | 5 ++++ configs/pjsip.conf.sample | 1 + configs/sip.conf.sample | 3 +++ res/res_pjsip_outbound_registration.c | 36 ++++++++++++++++++++++++--- 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index f7078ea791..9f91afef38 100644 --- a/CHANGES +++ b/CHANGES @@ -854,6 +854,10 @@ chan_sip * Added 'ignore_requested_pref'. When enabled, this will use the preferred codecs configured for a peer instead of the requested codec. + * The option "register_retry_403" has been added to chan_sip to work around + servers that are known to erroneously send 403 in response to valid + REGISTER requests and allows Asterisk to continue attepmting to connect. + chan_skinny ------------------ * Added the 'immeddialkey' parameter. If set, when the user presses the diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 4a45a96146..9cbfd715d8 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -819,6 +819,7 @@ static int global_rtpholdtimeout; /*!< Time out call if no RTP during hold */ static int global_rtpkeepalive; /*!< Send RTP keepalives */ static int global_reg_timeout; /*!< Global time between attempts for outbound registrations */ static int global_regattempts_max; /*!< Registration attempts before giving up */ +static int global_reg_retry_403; /*!< Treat 403 responses to registrations as 401 responses */ static int global_shrinkcallerid; /*!< enable or disable shrinking of caller id */ static int global_callcounter; /*!< Enable call counters for all devices. This is currently enabled by setting the peer * call-limit to INT_MAX. When we remove the call-limit from the code, we can make it @@ -21143,6 +21144,7 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_ ast_cli(a->fd, " Sub. max duration: %d secs\n", max_subexpiry); ast_cli(a->fd, " Outbound reg. timeout: %d secs\n", global_reg_timeout); ast_cli(a->fd, " Outbound reg. attempts: %d\n", global_regattempts_max); + ast_cli(a->fd, " Outbound reg. retry 403:%d\n", global_reg_retry_403); ast_cli(a->fd, " Notify ringing state: %s\n", AST_CLI_YESNO(sip_cfg.notifyringing)); if (sip_cfg.notifyringing) { ast_cli(a->fd, " Include CID: %s%s\n", @@ -31373,6 +31375,7 @@ static int reload_config(enum channelreloadreason reason) sip_cfg.compactheaders = DEFAULT_COMPACTHEADERS; global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT; global_regattempts_max = 0; + global_reg_retry_403 = 0; sip_cfg.pedanticsipchecking = DEFAULT_PEDANTIC; sip_cfg.autocreatepeer = DEFAULT_AUTOCREATEPEER; global_autoframing = 0; @@ -31761,6 +31764,8 @@ static int reload_config(enum channelreloadreason reason) } } else if (!strcasecmp(v->name, "registerattempts")) { global_regattempts_max = atoi(v->value); + } else if (!strcasecmp(v->name, "register_retry_403")) { + global_reg_retry_403 = ast_true(v->value); } else if (!strcasecmp(v->name, "bindaddr") || !strcasecmp(v->name, "udpbindaddr")) { if (ast_parse_arg(v->value, PARSE_ADDR, &bindaddr)) { ast_log(LOG_WARNING, "Invalid address: %s\n", v->value); diff --git a/configs/pjsip.conf.sample b/configs/pjsip.conf.sample index dbeda4d62d..55a7ef670c 100644 --- a/configs/pjsip.conf.sample +++ b/configs/pjsip.conf.sample @@ -147,6 +147,7 @@ ;client_uri=sip:1234567890@sip.example.com ;contact_user=1234567890 ;retry_interval=60 +;forbidden_retry_interval=600 ;expiration=3600 ;[mytrunk_auth] diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index ba4af11713..31db6de921 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -787,6 +787,9 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; 0 = continue forever, hammering the other server ; until it accepts the registration ; Default is 0 tries, continue forever +;register_retry_403=yes ; Treat 403 responses to registrations as if they were + ; 401 responses and continue retrying according to normal + ; retry rules. ;----------------------------------------- OUTBOUND MWI SUBSCRIPTIONS ------------------------- ; Asterisk can subscribe to receive the MWI from another SIP server and store it locally for retrieval diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 9c3ae1fcea..4a8b46aeeb 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -85,6 +85,17 @@ Interval in seconds between retries if outbound registration is unsuccessful + + Interval used when receiving a 403 Forbidden response. + + If a 403 Forbidden is received, chan_pjsip will wait + forbidden_retry_interval seconds before + attempting registration again. If 0 is specified, chan_pjsip will not + retry after receiving a 403 Forbidden response. Setting this to a non-zero + value goes against a "SHOULD NOT" in RFC3261, but can be used to work around + buggy registrars. + + SIP URI of the server to register against @@ -159,6 +170,8 @@ struct sip_outbound_registration_client_state { unsigned int max_retries; /*! \brief Interval at which retries should occur for temporal responses */ unsigned int retry_interval; + /*! \brief Interval at which retries should occur for permanent responses */ + unsigned int forbidden_retry_interval; /*! \brief Treat authentication challenges that we cannot handle as permanent failures */ unsigned int auth_rejection_permanent; /*! \brief Serializer for stuff and things */ @@ -198,6 +211,8 @@ struct sip_outbound_registration { unsigned int expiration; /*! \brief Interval at which retries should occur for temporal responses */ unsigned int retry_interval; + /*! \brief Interval at which retries should occur for permanent responses */ + unsigned int forbidden_retry_interval; /*! \brief Treat authentication challenges that we cannot handle as permanent failures */ unsigned int auth_rejection_permanent; /*! \brief Maximum number of retries permitted */ @@ -412,10 +427,21 @@ static int handle_registration_response(void *data) response->code, server_uri, client_uri, response->client_state->retry_interval); } } else { - /* Finally if there's no hope of registering give up */ - response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT; - ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n", - response->code, server_uri, client_uri); + if (response->code == 403 + && response->client_state->forbidden_retry_interval + && response->client_state->retries < response->client_state->max_retries) { + /* A forbidden response retry interval is configured and there are retries remaining */ + response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY; + response->client_state->retries++; + 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 '%d' seconds\n", + server_uri, client_uri, response->client_state->forbidden_retry_interval); + } else { + /* Finally if there's no hope of registering give up */ + response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT; + ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n", + response->code, server_uri, client_uri); + } } ast_system_publish_registry("PJSIP", client_uri, server_uri, sip_outbound_registration_status_str[response->client_state->status], NULL); @@ -716,6 +742,7 @@ static int sip_outbound_registration_perform(void *data) } registration->state->client_state->outbound_auths.num = registration->outbound_auths.num; registration->state->client_state->retry_interval = registration->retry_interval; + registration->state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval; registration->state->client_state->max_retries = registration->max_retries; registration->state->client_state->retries = 0; @@ -911,6 +938,7 @@ static int load_module(void) ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy)); 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", "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, NULL, 0, 0);