summaryrefslogtreecommitdiff
path: root/res/res_pjsip_registrar.c
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2017-07-31 14:21:06 -0500
committerRichard Mudgett <rmudgett@digium.com>2017-08-10 12:18:58 -0500
commit82f4ade959ef3ab7abb5deb4bc06882850dcde90 (patch)
treeb5ab08fe752a2e13c809fb9340b0713fc06e91fa /res/res_pjsip_registrar.c
parent1dcb92bba89440fff3454670ed5f892da157bf27 (diff)
res_pjsip: Remove ephemeral registered contacts on transport shutdown.
The fix for the issue is broken up into three parts. This is part two which handles the server side of REGISTER requests when rewrite_contact is enabled. Any registered reliable transport contact becomes invalid when the transport connection becomes disconnected. * Monitor the rewrite_contact's reliable transport REGISTER contact for shutdown. If it is shutdown then the contact must be removed because it is no longer valid. Otherwise, when the client attempts to re-REGISTER it may be blocked because the invalid contact is there. Also if we try to send a call to the endpoint using the invalid contact then the endpoint is not likely to see the request. The endpoint either won't be listening on that port for new connections or a NAT/firewall will block it. * Prune any rewrite_contact's registered reliable transport contacts on boot. The reliable transport no longer exists so the contact is invalid. * Websockets always rewrite the REGISTER contact address and the transport needs to be monitored for shutdown. * Made the websocket transport set a unique name since that is what we use as the ao2 container key. Otherwise, we would not know which transport we find when one of them shuts down. The names are also used for PJPROJECT debug logging. * Made the websocket transport post the PJSIP_TP_STATE_CONNECTED state event. Now the global keep_alive_interval option, initially idle shutdown timer, and the server REGISTER contact monitor can work on wetsocket transports. * Made the websocket transport set the PJSIP_TP_DIR_INCOMING direction. Now initially idle websockets will automatically shutdown. ASTERISK-27147 Change-Id: I397a5e7d18476830f7ffe1726adf9ee6c15964f4
Diffstat (limited to 'res/res_pjsip_registrar.c')
-rw-r--r--res/res_pjsip_registrar.c107
1 files changed, 103 insertions, 4 deletions
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index a4ce54769..ba1c074b3 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -310,6 +310,47 @@ static int registrar_validate_path(pjsip_rx_data *rdata, struct ast_sip_aor *aor
return -1;
}
+/*! Transport monitor for incoming REGISTER contacts */
+struct contact_transport_monitor {
+ /*!
+ * \brief Sorcery contact name to remove on transport shutdown
+ * \note Stored after aor_name in space reserved when struct allocated.
+ */
+ char *contact_name;
+ /*! AOR name the contact is associated */
+ char aor_name[0];
+};
+
+static void register_contact_transport_shutdown_cb(void *data)
+{
+ struct contact_transport_monitor *monitor = data;
+ struct ast_sip_contact *contact;
+ struct ast_sip_aor *aor;
+
+ aor = ast_sip_location_retrieve_aor(monitor->aor_name);
+ if (!aor) {
+ return;
+ }
+
+ ao2_lock(aor);
+ contact = ast_sip_location_retrieve_contact(monitor->contact_name);
+ if (contact) {
+ ast_sip_location_delete_contact(contact);
+ ast_verb(3, "Removed contact '%s' from AOR '%s' due to transport shutdown\n",
+ contact->uri, monitor->aor_name);
+ ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
+ "Contact: %s\r\n"
+ "AOR: %s\r\n"
+ "UserAgent: %s",
+ contact->uri,
+ monitor->aor_name,
+ contact->user_agent);
+ ao2_ref(contact, -1);
+ }
+ ao2_unlock(aor);
+ ao2_ref(aor, -1);
+}
+
static int register_aor_core(pjsip_rx_data *rdata,
struct ast_sip_endpoint *endpoint,
struct ast_sip_aor *aor,
@@ -419,6 +460,9 @@ static int register_aor_core(pjsip_rx_data *rdata,
pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) {
+ int prune_on_boot = 0;
+ pj_str_t host_name;
+
/* If they are actually trying to delete a contact that does not exist... be forgiving */
if (!expiration) {
ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
@@ -426,14 +470,68 @@ static int register_aor_core(pjsip_rx_data *rdata,
continue;
}
- if (ast_sip_location_add_contact_nolock(aor, contact_uri, ast_tvadd(ast_tvnow(),
- ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL,
- user_agent, via_addr, via_port, call_id, endpoint)) {
+ /* Determine if the contact cannot survive a restart/boot. */
+ if (details.uri->port == rdata->pkt_info.src_port
+ && !pj_strcmp(&details.uri->host,
+ pj_cstr(&host_name, rdata->pkt_info.src_name))
+ /* We have already checked if the URI scheme is sip: or sips: */
+ && PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport)) {
+ pj_str_t type_name;
+
+ /* Determine the transport parameter value */
+ if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
+ /* WSS is special, as it needs to be ws. */
+ pj_cstr(&type_name, "ws");
+ } else {
+ pj_cstr(&type_name, rdata->tp_info.transport->type_name);
+ }
+
+ if (!pj_stricmp(&details.uri->transport_param, &type_name)
+ && (endpoint->nat.rewrite_contact
+ /* Websockets are always rewritten */
+ || !pj_stricmp(&details.uri->transport_param,
+ pj_cstr(&type_name, "ws")))) {
+ /*
+ * The contact was rewritten to the reliable transport's
+ * source address. Disconnecting the transport for any
+ * reason invalidates the contact.
+ */
+ prune_on_boot = 1;
+ }
+ }
+
+ contact = ast_sip_location_create_contact(aor, contact_uri,
+ ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)),
+ path_str ? ast_str_buffer(path_str) : NULL,
+ user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint);
+ if (!contact) {
ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
- contact_uri, aor_name);
+ contact_uri, aor_name);
continue;
}
+ if (prune_on_boot) {
+ const char *contact_name;
+ struct contact_transport_monitor *monitor;
+
+ /*
+ * Monitor the transport in case it gets disconnected because
+ * the contact won't be valid anymore if that happens.
+ */
+ contact_name = ast_sorcery_object_get_id(contact);
+ monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name)
+ + strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (monitor) {
+ strcpy(monitor->aor_name, aor_name);/* Safe */
+ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1;
+ strcpy(monitor->contact_name, contact_name);/* Safe */
+
+ ast_sip_transport_monitor_register(rdata->tp_info.transport,
+ register_contact_transport_shutdown_cb, monitor);
+ ao2_ref(monitor, -1);
+ }
+ }
+
ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
contact_uri, aor_name, expiration);
ast_test_suite_event_notify("AOR_CONTACT_ADDED",
@@ -885,6 +983,7 @@ static int unload_module(void)
ast_manager_unregister(AMI_SHOW_REGISTRATIONS);
ast_manager_unregister(AMI_SHOW_REGISTRATION_CONTACT_STATUSES);
ast_sip_unregister_service(&registrar_module);
+ ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb);
return 0;
}