res_pjsip: Use reasonable buffer lengths for endpoint identification
[asterisk/asterisk.git] / res / res_pjsip_outbound_registration.c
index 683642f..d9afcd2 100644 (file)
@@ -19,6 +19,7 @@
 /*** MODULEINFO
        <depend>pjproject</depend>
        <depend>res_pjsip</depend>
+       <use type="module">res_statsd</use>
        <support_level>core</support_level>
  ***/
 
@@ -96,7 +97,7 @@
                                        </description>
                                </configOption>
                                <configOption name="outbound_proxy" default="">
-                                       <synopsis>Outbound Proxy used to send registrations</synopsis>
+                                       <synopsis>Full SIP URI of the outbound proxy used to send registrations</synopsis>
                                </configOption>
                                <configOption name="retry_interval" default="60">
                                        <synopsis>Interval in seconds between retries if outbound registration is unsuccessful</synopsis>
@@ -358,6 +359,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) */
@@ -459,7 +462,7 @@ static int line_identify_relationship(void *obj, void *arg, int flags)
        struct sip_outbound_registration_state *state = obj;
        pjsip_param *line = arg;
 
-       return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH | CMP_STOP : 0;
+       return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH : 0;
 }
 
 static struct pjsip_param *get_uri_option_line(const void *uri)
@@ -558,20 +561,21 @@ static int handle_client_registration(void *data)
 {
        RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
        pjsip_tx_data *tdata;
-       pjsip_regc_info info;
-       char server_uri[PJSIP_MAX_URL_SIZE];
-       char client_uri[PJSIP_MAX_URL_SIZE];
 
        if (client_state->status == SIP_REGISTRATION_STOPPED
                || pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS) {
                return 0;
        }
 
-       pjsip_regc_get_info(client_state->client, &info);
-       ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
-       ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
-       ast_debug(1, "Outbound REGISTER attempt %u to '%s' with client '%s'\n",
-               client_state->retries + 1, server_uri, client_uri);
+       if (DEBUG_ATLEAST(1)) {
+               pjsip_regc_info info;
+
+               pjsip_regc_get_info(client_state->client, &info);
+               ast_log(LOG_DEBUG, "Outbound REGISTER attempt %u to '%.*s' with client '%.*s'\n",
+                       client_state->retries + 1,
+                       (int) info.server_uri.slen, info.server_uri.ptr,
+                       (int) info.client_uri.slen, info.client_uri.ptr);
+       }
 
        if (client_state->support_path) {
                pjsip_supported_hdr *hdr;
@@ -794,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)
 {
@@ -862,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. */
@@ -987,7 +1073,8 @@ static void sip_outbound_registration_state_destroy(void *obj)
        struct sip_outbound_registration_state *state = obj;
 
        ast_debug(3, "Destroying registration state for registration to server '%s' from client '%s'\n",
-                       state->registration->server_uri, state->registration->client_uri);
+               state->registration ? state->registration->server_uri : "",
+               state->registration ? state->registration->client_uri : "");
        ao2_cleanup(state->registration);
 
        if (!state->client_state) {
@@ -1006,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 */
@@ -1031,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));
@@ -1041,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;
@@ -1086,7 +1183,7 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c
        pj_str_t tmp, local_addr;
        pjsip_uri *uri;
        pjsip_sip_uri *sip_uri;
-       pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
+       pjsip_transport_type_e type;
        int local_port;
 
        pj_strdup_with_null(pool, &tmp, target);
@@ -1098,20 +1195,20 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c
 
        sip_uri = pjsip_uri_get_uri(uri);
 
+       type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
        if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) {
-               type = PJSIP_TRANSPORT_TLS;
+               if (type == PJSIP_TRANSPORT_UNSPECIFIED
+                       || !(pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE)) {
+                       type = PJSIP_TRANSPORT_TLS;
+               }
        } else if (!sip_uri->transport_param.slen) {
                type = PJSIP_TRANSPORT_UDP;
-       } else {
-               type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
-       }
-
-       if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
+       } else if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
                return -1;
        }
 
        if (pj_strchr(&sip_uri->host, ':')) {
-               type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
+               type |= PJSIP_TRANSPORT_IPV6;
        }
 
        if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()),
@@ -1120,7 +1217,7 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c
        }
 
        if (!pj_strchr(&sip_uri->host, ':') && pj_strchr(&local_addr, ':')) {
-               type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
+               type |= PJSIP_TRANSPORT_IPV6;
        }
 
        contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
@@ -1211,6 +1308,17 @@ static int sip_outbound_registration_regc_alloc(void *data)
                return -1;
        }
 
+       if (!ast_strlen_zero(registration->outbound_proxy)) {
+               pj_strdup2_with_null(pool, &tmp, registration->outbound_proxy);
+               uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
+               if (!uri) {
+                       ast_log(LOG_ERROR, "Invalid outbound proxy URI '%s' specified on outbound registration '%s'\n",
+                               registration->outbound_proxy, ast_sorcery_object_get_id(registration));
+                       pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
+                       return -1;
+               }
+       }
+
        pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
 
 
@@ -1277,10 +1385,10 @@ static int sip_outbound_registration_perform(void *data)
 
        AST_VECTOR_INIT(&state->client_state->outbound_auths, AST_VECTOR_SIZE(&registration->outbound_auths));
        for (i = 0; i < AST_VECTOR_SIZE(&registration->outbound_auths); ++i) {
-               const char *name = ast_strdup(AST_VECTOR_GET(&registration->outbound_auths, i));
+               char *name = ast_strdup(AST_VECTOR_GET(&registration->outbound_auths, i));
 
-               if (name) {
-                       AST_VECTOR_APPEND(&state->client_state->outbound_auths, name);
+               if (name && AST_VECTOR_APPEND(&state->client_state->outbound_auths, name)) {
+                       ast_free(name);
                }
        }
        state->client_state->retry_interval = registration->retry_interval;
@@ -1321,7 +1429,7 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
                        ast_sorcery_object_get_id(applied));
                return -1;
        } else if (ast_sip_validate_uri_length(applied->server_uri)) {
-                       ast_log(LOG_ERROR, "Server URI or hostname length exceeds pjpropject limit '%s'\n",
+                       ast_log(LOG_ERROR, "Server URI or hostname length exceeds pjproject limit or is not a sip(s) uri: '%s'\n",
                                ast_sorcery_object_get_id(applied));
                        return -1;
        } else if (ast_strlen_zero(applied->client_uri)) {
@@ -1329,7 +1437,7 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
                        ast_sorcery_object_get_id(applied));
                return -1;
        } else if (ast_sip_validate_uri_length(applied->client_uri)) {
-                       ast_log(LOG_ERROR, "Client URI or hostname length exceeds pjpropject limit '%s'\n",
+                       ast_log(LOG_ERROR, "Client URI or hostname length exceeds pjproject limit or is not a sip(s) uri: '%s'\n",
                                ast_sorcery_object_get_id(applied));
                        return -1;
        } else if (applied->line && ast_strlen_zero(applied->endpoint)) {
@@ -2041,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);
@@ -2071,7 +2181,7 @@ static int load_module(void)
 
        shutdown_group = ast_serializer_shutdown_group_alloc();
        if (!shutdown_group) {
-               return AST_MODULE_LOAD_FAILURE;
+               return AST_MODULE_LOAD_DECLINE;
        }
 
        /* Create outbound registration states container. */
@@ -2080,7 +2190,7 @@ static int load_module(void)
        if (!new_states) {
                ast_log(LOG_ERROR, "Unable to allocate registration states container\n");
                unload_module();
-               return AST_MODULE_LOAD_FAILURE;
+               return AST_MODULE_LOAD_DECLINE;
        }
        ao2_global_obj_replace_unref(current_states, new_states);
        ao2_ref(new_states, -1);
@@ -2124,7 +2234,7 @@ static int load_module(void)
                        &registration_observer)) {
                ast_log(LOG_ERROR, "Unable to register observers.\n");
                unload_module();
-               return AST_MODULE_LOAD_FAILURE;
+               return AST_MODULE_LOAD_DECLINE;
        }
 
        /* Register how this module identifies endpoints. */
@@ -2135,7 +2245,7 @@ static int load_module(void)
        if (!cli_formatter) {
                ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
                unload_module();
-               return AST_MODULE_LOAD_FAILURE;
+               return AST_MODULE_LOAD_DECLINE;
        }
        cli_formatter->name = "registration";
        cli_formatter->print_header = cli_print_header;