summaryrefslogtreecommitdiff
path: root/res/res_pjsip_outbound_registration.c
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2017-08-02 18:44:12 -0500
committerRichard Mudgett <rmudgett@digium.com>2017-08-10 12:13:18 -0500
commit1cf2c79f37ac3dfedf27f96358b09bb45429f22d (patch)
tree51bfdc3396f5446398af7ff883cf0a6b84d5637d /res/res_pjsip_outbound_registration.c
parent07d026b4cdaae7d20d0fa2d691b3f3b3f7e1e4e4 (diff)
res_pjsip_outbound_registration.c: Re-REGISTER on transport shutdown.
The fix for the issue is broken up into three parts. This is part three which handles the client side of REGISTER requests. The registered contact may no longer be valid on the server when the transport used is reliable and the connection is broken. * Re-REGISTER our contact if the reliable transport is broken after registration completes. We attempt to re-REGISTER immediately to minimize the time we are unreachable. Time may have already passed between the connection being broken and the loss being detected. * Reorder sip_outbound_registration_state_alloc() so the STATSD_GUAGE's are still correct if an allocation failure happens. ASTERISK-27147 Change-Id: I3668405b1ee75dfefb07c0d637826176f741ce83
Diffstat (limited to 'res/res_pjsip_outbound_registration.c')
-rw-r--r--res/res_pjsip_outbound_registration.c114
1 files changed, 105 insertions, 9 deletions
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 2760567aa..76d756d9e 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -358,6 +358,8 @@ struct sip_outbound_registration_client_state {
unsigned int auth_attempted:1;
/*! \brief The name of the transport to be used for the registration */
char *transport_name;
+ /*! \brief The name of the registration sorcery object */
+ char *registration_name;
};
/*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
@@ -796,6 +798,82 @@ static void schedule_retry(struct registration_response *response, unsigned int
}
}
+static int reregister_immediately_cb(void *obj)
+{
+ struct sip_outbound_registration_state *state = obj;
+
+ if (state->client_state->status != SIP_REGISTRATION_REGISTERED) {
+ ao2_ref(state, -1);
+ return 0;
+ }
+
+ if (DEBUG_ATLEAST(1)) {
+ pjsip_regc_info info;
+
+ pjsip_regc_get_info(state->client_state->client, &info);
+ ast_log(LOG_DEBUG,
+ "Outbound registration transport to server '%.*s' from client '%.*s' shutdown\n",
+ (int) info.server_uri.slen, info.server_uri.ptr,
+ (int) info.client_uri.slen, info.client_uri.ptr);
+ }
+
+ cancel_registration(state->client_state);
+
+ ao2_ref(state->client_state, +1);
+ handle_client_registration(state->client_state);
+
+ ao2_ref(state, -1);
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief The reliable transport we registered using has shutdown.
+ * \since 13.18.0
+ *
+ * \param obj What is needed to initiate a reregister attempt.
+ *
+ * \return Nothing
+ */
+static void registration_transport_shutdown_cb(void *obj)
+{
+ const char *registration_name = obj;
+ struct sip_outbound_registration_state *state;
+
+ state = get_state(registration_name);
+ if (!state) {
+ /* Registration no longer exists or shutting down. */
+ return;
+ }
+ if (ast_sip_push_task(state->client_state->serializer, reregister_immediately_cb, state)) {
+ ao2_ref(state, -1);
+ }
+}
+
+static void registration_transport_monitor_setup(pjsip_transport *transport, const char *registration_name)
+{
+ char *monitor;
+
+ if (!PJSIP_TRANSPORT_IS_RELIABLE(transport)) {
+ return;
+ }
+ monitor = ao2_alloc_options(strlen(registration_name) + 1, NULL,
+ AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!monitor) {
+ return;
+ }
+ strcpy(monitor, registration_name);/* Safe */
+
+ /*
+ * We'll ignore if the transport has already been shutdown before we
+ * register the monitor. We might get into a message spamming infinite
+ * loop of registration, shutdown, reregistration...
+ */
+ ast_sip_transport_monitor_register(transport, registration_transport_shutdown_cb,
+ monitor);
+ ao2_ref(monitor, -1);
+}
+
/*! \brief Callback function for handling a response to a registration attempt */
static int handle_registration_response(void *data)
{
@@ -864,9 +942,15 @@ static int handle_registration_response(void *data)
next_registration_round = 0;
}
schedule_registration(response->client_state, next_registration_round);
+
+ /* See if we should monitor for transport shutdown */
+ registration_transport_monitor_setup(response->rdata->tp_info.transport,
+ response->client_state->registration_name);
} else {
ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED);
+ ast_sip_transport_monitor_unregister(response->rdata->tp_info.transport,
+ registration_transport_shutdown_cb);
}
} else if (response->client_state->destroy) {
/* We need to deal with the pending destruction instead. */
@@ -1009,12 +1093,13 @@ static void sip_outbound_registration_client_state_destroy(void *obj)
{
struct sip_outbound_registration_client_state *client_state = obj;
- ast_free(client_state->transport_name);
ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "-1", 1.0);
ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
sip_outbound_registration_status_str(client_state->status));
ast_taskprocessor_unreference(client_state->serializer);
+ ast_free(client_state->transport_name);
+ ast_free(client_state->registration_name);
}
/*! \brief Allocator function for registration state */
@@ -1034,6 +1119,23 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
return NULL;
}
+ state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
+ state->client_state->timer.user_data = state->client_state;
+ state->client_state->timer.cb = sip_outbound_registration_timer_cb;
+ state->client_state->transport_name = ast_strdup(registration->transport);
+ state->client_state->registration_name =
+ ast_strdup(ast_sorcery_object_get_id(registration));
+
+ ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
+ ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
+ sip_outbound_registration_status_str(state->client_state->status));
+
+ if (!state->client_state->transport_name
+ || !state->client_state->registration_name) {
+ ao2_cleanup(state);
+ return NULL;
+ }
+
/* Create name with seq number appended. */
ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/outreg/%s",
ast_sorcery_object_get_id(registration));
@@ -1044,14 +1146,6 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
ao2_cleanup(state);
return NULL;
}
- state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
- state->client_state->timer.user_data = state->client_state;
- state->client_state->timer.cb = sip_outbound_registration_timer_cb;
- state->client_state->transport_name = ast_strdup(registration->transport);
-
- ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
- ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
- sip_outbound_registration_status_str(state->client_state->status));
state->registration = ao2_bump(registration);
return state;
@@ -2055,6 +2149,8 @@ static int unload_module(void)
ao2_global_obj_release(current_states);
+ ast_sip_transport_monitor_unregister_all(registration_transport_shutdown_cb);
+
/* Wait for registration serializers to get destroyed. */
ast_debug(2, "Waiting for registration transactions to complete for unload.\n");
remaining = ast_serializer_shutdown_group_join(shutdown_group, MAX_UNLOAD_TIMEOUT_TIME);