From 758409de56f1bfc5e9b58230282f9a5382cb3846 Mon Sep 17 00:00:00 2001 From: George Joseph Date: Tue, 6 Feb 2018 11:07:18 -0700 Subject: AST-2018-005: res_pjsip_transport_management: Move to core Since res_pjsip_transport_management provides several attack mitigation features, its functionality moved to res_pjsip and this module has been removed. This way the features will always be available if res_pjsip is loaded. ASTERISK-27618 Reported By: Sandro Gauci Change-Id: I21a2d33d9dda001452ea040d350d7a075f9acf0d --- CHANGES | 7 + res/res_pjsip.c | 6 + res/res_pjsip/include/res_pjsip_private.h | 28 ++ res/res_pjsip/pjsip_transport_management.c | 376 +++++++++++++++++++++++++++ res/res_pjsip_transport_management.c | 399 ----------------------------- 5 files changed, 417 insertions(+), 399 deletions(-) create mode 100644 res/res_pjsip/pjsip_transport_management.c delete mode 100644 res/res_pjsip_transport_management.c diff --git a/CHANGES b/CHANGES index ba153bc6c..2d49c13fb 100644 --- a/CHANGES +++ b/CHANGES @@ -145,6 +145,13 @@ res_pjsip_pubsub need to run the "alembic upgrade head" process to add the column to the schema. +res_pjsip_transport_management +------------------ + * Since res_pjsip_transport_management provides several attack + mitigation features, its functionality moved to res_pjsip and + this module has been removed. This way the features will always + be available if res_pjsip is loaded. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------ ------------------------------------------------------------------------------ diff --git a/res/res_pjsip.c b/res/res_pjsip.c index df4dd47c6..ca173a8af 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -4972,6 +4972,7 @@ static int unload_pjsip(void *data) ast_res_pjsip_cleanup_options_handling(); ast_res_pjsip_cleanup_message_filter(); ast_sip_destroy_distributor(); + ast_sip_destroy_transport_management(); ast_res_pjsip_destroy_configuration(); ast_sip_destroy_system(); ast_sip_destroy_global_headers(); @@ -5135,6 +5136,11 @@ static int load_module(void) ast_sip_initialize_resolver(); ast_sip_initialize_dns(); + if (ast_sip_initialize_transport_management()) { + ast_log(LOG_ERROR, "Failed to initialize SIP transport management. Aborting load\n"); + goto error; + } + if (ast_sip_initialize_distributor()) { ast_log(LOG_ERROR, "Failed to register distributor module. Aborting load\n"); goto error; diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h index 7d434aa95..71947f840 100644 --- a/res/res_pjsip/include/res_pjsip_private.h +++ b/res/res_pjsip/include/res_pjsip_private.h @@ -361,4 +361,32 @@ int ast_sip_destroy_scheduler(void); int ast_sip_will_uri_survive_restart(pjsip_sip_uri *uri, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata); +/*! + * \internal + * \brief Initialize the transport management module + * \since 13.20.0 + * + * The transport management module is responsible for 3 things... + * 1. It automatically destroys any reliable transport that does not + * receive a valid request within system/timer_b milliseconds of the + * connection being opened. (Attack mitigation) + * 2. Since it increments the reliable transport's reference count + * for that period of time, it also prevents issues if the transport + * disconnects while we're still trying to process a response. + * (Attack mitigation) + * 3. If enabled by global/keep_alive_interval, it sends '\r\n' + * keepalives on reliable transports at the interval specified. + * + * \retval -1 Failure + * \retval 0 Success + */ +int ast_sip_initialize_transport_management(void); + +/*! + * \internal + * \brief Destruct the transport management module. + * \since 13.20.0 + */ +void ast_sip_destroy_transport_management(void); + #endif /* RES_PJSIP_PRIVATE_H_ */ diff --git a/res/res_pjsip/pjsip_transport_management.c b/res/res_pjsip/pjsip_transport_management.c new file mode 100644 index 000000000..efda37d7c --- /dev/null +++ b/res/res_pjsip/pjsip_transport_management.c @@ -0,0 +1,376 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2014, Digium, Inc. + * + * Joshua Colp + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include + +#include +#include + +#include "asterisk/res_pjsip.h" +#include "asterisk/module.h" +#include "asterisk/astobj2.h" +#include "include/res_pjsip_private.h" + +/*! \brief Number of buckets for monitored transports */ +#define TRANSPORTS_BUCKETS 127 + +#define IDLE_TIMEOUT (pjsip_cfg()->tsx.td) + +/*! \brief The keep alive packet to send */ +static const pj_str_t keepalive_packet = { "\r\n\r\n", 4 }; + +/*! \brief Global container of active transports */ +static AO2_GLOBAL_OBJ_STATIC(monitored_transports); + +/*! \brief Scheduler context for timing out connections with no data received */ +static struct ast_sched_context *sched; + +/*! \brief Thread keeping things alive */ +static pthread_t keepalive_thread = AST_PTHREADT_NULL; + +/*! \brief The global interval at which to send keepalives */ +static unsigned int keepalive_interval; + +/*! \brief Structure for transport to be monitored */ +struct monitored_transport { + /*! \brief The underlying PJSIP transport */ + pjsip_transport *transport; + /*! \brief Non-zero if a PJSIP request was received */ + int sip_received; +}; + +/*! \brief Callback function to send keepalive */ +static int keepalive_transport_cb(void *obj, void *arg, int flags) +{ + struct monitored_transport *monitored = obj; + pjsip_tpselector selector = { + .type = PJSIP_TPSELECTOR_TRANSPORT, + .u.transport = monitored->transport, + }; + + pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), + monitored->transport->key.type, &selector, NULL, keepalive_packet.ptr, keepalive_packet.slen, + &monitored->transport->key.rem_addr, pj_sockaddr_get_len(&monitored->transport->key.rem_addr), + NULL, NULL); + + return 0; +} + +/*! \brief Thread which sends keepalives to all active connection-oriented transports */ +static void *keepalive_transport_thread(void *data) +{ + struct ao2_container *transports; + pj_thread_desc desc; + pj_thread_t *thread; + + if (pj_thread_register("Asterisk Keepalive Thread", desc, &thread) != PJ_SUCCESS) { + ast_log(LOG_ERROR, "Could not register keepalive thread with PJLIB, keepalives will not occur.\n"); + return NULL; + } + + transports = ao2_global_obj_ref(monitored_transports); + if (!transports) { + return NULL; + } + + /* Once loaded this module just keeps on going as it is unsafe to stop and change the underlying + * callback for the transport manager. + */ + while (keepalive_interval) { + sleep(keepalive_interval); + ao2_callback(transports, OBJ_NODATA, keepalive_transport_cb, NULL); + } + + ao2_ref(transports, -1); + return NULL; +} + +AST_THREADSTORAGE(desc_storage); + +static int idle_sched_cb(const void *data) +{ + struct monitored_transport *monitored = (struct monitored_transport *) data; + + if (!pj_thread_is_registered()) { + pj_thread_t *thread; + pj_thread_desc *desc; + + desc = ast_threadstorage_get(&desc_storage, sizeof(pj_thread_desc)); + if (!desc) { + ast_log(LOG_ERROR, "Could not get thread desc from thread-local storage.\n"); + ao2_ref(monitored, -1); + return 0; + } + + pj_bzero(*desc, sizeof(*desc)); + + pj_thread_register("Transport Monitor", *desc, &thread); + } + + if (!monitored->sip_received) { + ast_log(LOG_NOTICE, "Shutting down transport '%s' since no request was received in %d seconds\n", + monitored->transport->info, IDLE_TIMEOUT / 1000); + pjsip_transport_shutdown(monitored->transport); + } + + ao2_ref(monitored, -1); + return 0; +} + +/*! \brief Destructor for keepalive transport */ +static void monitored_transport_destroy(void *obj) +{ + struct monitored_transport *monitored = obj; + + pjsip_transport_dec_ref(monitored->transport); +} + +/*! \brief Callback invoked when transport changes occur */ +static void monitored_transport_state_callback(pjsip_transport *transport, pjsip_transport_state state, + const pjsip_transport_state_info *info) +{ + struct ao2_container *transports; + + /* We only care about reliable transports */ + if (PJSIP_TRANSPORT_IS_RELIABLE(transport) + && (transport->dir == PJSIP_TP_DIR_INCOMING || keepalive_interval) + && (transports = ao2_global_obj_ref(monitored_transports))) { + struct monitored_transport *monitored; + + switch (state) { + case PJSIP_TP_STATE_CONNECTED: + monitored = ao2_alloc_options(sizeof(*monitored), + monitored_transport_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!monitored) { + break; + } + monitored->transport = transport; + pjsip_transport_add_ref(monitored->transport); + + ao2_link(transports, monitored); + + if (transport->dir == PJSIP_TP_DIR_INCOMING) { + /* Let the scheduler inherit the reference from allocation */ + if (ast_sched_add_variable(sched, IDLE_TIMEOUT, idle_sched_cb, monitored, 1) < 0) { + /* Uh Oh. Could not schedule the idle check. Kill the transport. */ + pjsip_transport_shutdown(transport); + } else { + /* monitored ref successfully passed to idle_sched_cb() */ + break; + } + } + ao2_ref(monitored, -1); + break; + case PJSIP_TP_STATE_SHUTDOWN: + case PJSIP_TP_STATE_DISCONNECTED: + ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK); + break; + default: + break; + } + + ao2_ref(transports, -1); + } +} + +struct ast_sip_tpmgr_state_callback monitored_transport_reg = { + monitored_transport_state_callback, +}; + +/*! \brief Hashing function for monitored transport */ +static int monitored_transport_hash_fn(const void *obj, int flags) +{ + const struct monitored_transport *object; + const char *key; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_KEY: + key = obj; + break; + case OBJ_SEARCH_OBJECT: + object = obj; + key = object->transport->obj_name; + break; + default: + /* Hash can only work on something with a full key. */ + ast_assert(0); + return 0; + } + return ast_str_hash(key); +} + +/*! \brief Comparison function for monitored transport */ +static int monitored_transport_cmp_fn(void *obj, void *arg, int flags) +{ + const struct monitored_transport *object_left = obj; + const struct monitored_transport *object_right = arg; + const char *right_key = arg; + int cmp; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + right_key = object_right->transport->obj_name; + /* Fall through */ + case OBJ_SEARCH_KEY: + cmp = strcmp(object_left->transport->obj_name, right_key); + break; + case OBJ_SEARCH_PARTIAL_KEY: + /* + * We could also use a partial key struct containing a length + * so strlen() does not get called for every comparison instead. + */ + cmp = strncmp(object_left->transport->obj_name, right_key, strlen(right_key)); + break; + default: + /* + * What arg points to is specific to this traversal callback + * and has no special meaning to astobj2. + */ + cmp = 0; + break; + } + + return !cmp ? CMP_MATCH : 0; +} + +static void keepalive_global_loaded(const char *object_type) +{ + unsigned int new_interval = ast_sip_get_keep_alive_interval(); + + if (new_interval) { + keepalive_interval = new_interval; + } else if (keepalive_interval) { + ast_log(LOG_NOTICE, "Keepalive support can not be disabled once activated.\n"); + return; + } else { + /* This will occur if no keepalive interval has been specified at initial start */ + return; + } + + if (keepalive_thread != AST_PTHREADT_NULL) { + return; + } + + if (ast_pthread_create(&keepalive_thread, NULL, keepalive_transport_thread, NULL)) { + ast_log(LOG_ERROR, "Could not create thread for sending keepalive messages.\n"); + keepalive_thread = AST_PTHREADT_NULL; + keepalive_interval = 0; + } +} + +/*! \brief Observer which is used to update our interval when the global setting changes */ +static struct ast_sorcery_observer keepalive_global_observer = { + .loaded = keepalive_global_loaded, +}; + +/*! + * \brief + * On incoming TCP connections, when we receive a SIP request, we mark that we have + * received a valid SIP request. This way, we will not shut the transport down for + * idleness + */ +static pj_bool_t idle_monitor_on_rx_request(pjsip_rx_data *rdata) +{ + struct ao2_container *transports; + struct monitored_transport *idle_trans; + + transports = ao2_global_obj_ref(monitored_transports); + if (!transports) { + return PJ_FALSE; + } + + idle_trans = ao2_find(transports, rdata->tp_info.transport->obj_name, OBJ_SEARCH_KEY); + ao2_ref(transports, -1); + if (!idle_trans) { + return PJ_FALSE; + } + + idle_trans->sip_received = 1; + ao2_ref(idle_trans, -1); + + return PJ_FALSE; +} + +static pjsip_module idle_monitor_module = { + .name = {"idle monitor module", 19}, + .priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER + 3, + .on_rx_request = idle_monitor_on_rx_request, +}; + +int ast_sip_initialize_transport_management(void) +{ + struct ao2_container *transports; + + transports = ao2_container_alloc(TRANSPORTS_BUCKETS, monitored_transport_hash_fn, + monitored_transport_cmp_fn); + if (!transports) { + ast_log(LOG_ERROR, "Could not create container for transports to perform keepalive on.\n"); + return AST_MODULE_LOAD_DECLINE; + } + ao2_global_obj_replace_unref(monitored_transports, transports); + ao2_ref(transports, -1); + + sched = ast_sched_context_create(); + if (!sched) { + ast_log(LOG_ERROR, "Failed to create keepalive scheduler context.\n"); + ao2_global_obj_release(monitored_transports); + return AST_MODULE_LOAD_DECLINE; + } + + if (ast_sched_start_thread(sched)) { + ast_log(LOG_ERROR, "Failed to start keepalive scheduler thread\n"); + ast_sched_context_destroy(sched); + sched = NULL; + ao2_global_obj_release(monitored_transports); + return AST_MODULE_LOAD_DECLINE; + } + + ast_sip_register_service(&idle_monitor_module); + + ast_sip_transport_state_register(&monitored_transport_reg); + + ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &keepalive_global_observer); + ast_sorcery_reload_object(ast_sip_get_sorcery(), "global"); + + return AST_MODULE_LOAD_SUCCESS; +} + +void ast_sip_destroy_transport_management(void) +{ + if (keepalive_interval) { + keepalive_interval = 0; + if (keepalive_thread != AST_PTHREADT_NULL) { + pthread_kill(keepalive_thread, SIGURG); + pthread_join(keepalive_thread, NULL); + keepalive_thread = AST_PTHREADT_NULL; + } + } + + ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &keepalive_global_observer); + + ast_sip_transport_state_unregister(&monitored_transport_reg); + + ast_sip_unregister_service(&idle_monitor_module); + + ast_sched_context_destroy(sched); + sched = NULL; + + ao2_global_obj_release(monitored_transports); +} diff --git a/res/res_pjsip_transport_management.c b/res/res_pjsip_transport_management.c deleted file mode 100644 index afaa24815..000000000 --- a/res/res_pjsip_transport_management.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2014, Digium, Inc. - * - * Joshua Colp - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*** MODULEINFO - pjproject - res_pjsip - core - ***/ - -#include "asterisk.h" - -#include - -#include -#include - -#include "asterisk/res_pjsip.h" -#include "asterisk/module.h" -#include "asterisk/astobj2.h" - -/*! \brief Number of buckets for monitored transports */ -#define TRANSPORTS_BUCKETS 127 - -#define IDLE_TIMEOUT (pjsip_cfg()->tsx.td) - -/*! \brief The keep alive packet to send */ -static const pj_str_t keepalive_packet = { "\r\n\r\n", 4 }; - -/*! \brief Global container of active transports */ -static AO2_GLOBAL_OBJ_STATIC(monitored_transports); - -/*! \brief Scheduler context for timing out connections with no data received */ -static struct ast_sched_context *sched; - -/*! \brief Thread keeping things alive */ -static pthread_t keepalive_thread = AST_PTHREADT_NULL; - -/*! \brief The global interval at which to send keepalives */ -static unsigned int keepalive_interval; - -/*! \brief Structure for transport to be monitored */ -struct monitored_transport { - /*! \brief The underlying PJSIP transport */ - pjsip_transport *transport; - /*! \brief Non-zero if a PJSIP request was received */ - int sip_received; -}; - -/*! \brief Callback function to send keepalive */ -static int keepalive_transport_cb(void *obj, void *arg, int flags) -{ - struct monitored_transport *monitored = obj; - pjsip_tpselector selector = { - .type = PJSIP_TPSELECTOR_TRANSPORT, - .u.transport = monitored->transport, - }; - - pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), - monitored->transport->key.type, &selector, NULL, keepalive_packet.ptr, keepalive_packet.slen, - &monitored->transport->key.rem_addr, pj_sockaddr_get_len(&monitored->transport->key.rem_addr), - NULL, NULL); - - return 0; -} - -/*! \brief Thread which sends keepalives to all active connection-oriented transports */ -static void *keepalive_transport_thread(void *data) -{ - struct ao2_container *transports; - pj_thread_desc desc; - pj_thread_t *thread; - - if (pj_thread_register("Asterisk Keepalive Thread", desc, &thread) != PJ_SUCCESS) { - ast_log(LOG_ERROR, "Could not register keepalive thread with PJLIB, keepalives will not occur.\n"); - return NULL; - } - - transports = ao2_global_obj_ref(monitored_transports); - if (!transports) { - return NULL; - } - - /* Once loaded this module just keeps on going as it is unsafe to stop and change the underlying - * callback for the transport manager. - */ - while (keepalive_interval) { - sleep(keepalive_interval); - ao2_callback(transports, OBJ_NODATA, keepalive_transport_cb, NULL); - } - - ao2_ref(transports, -1); - return NULL; -} - -AST_THREADSTORAGE(desc_storage); - -static int idle_sched_cb(const void *data) -{ - struct monitored_transport *monitored = (struct monitored_transport *) data; - - if (!pj_thread_is_registered()) { - pj_thread_t *thread; - pj_thread_desc *desc; - - desc = ast_threadstorage_get(&desc_storage, sizeof(pj_thread_desc)); - if (!desc) { - ast_log(LOG_ERROR, "Could not get thread desc from thread-local storage.\n"); - ao2_ref(monitored, -1); - return 0; - } - - pj_bzero(*desc, sizeof(*desc)); - - pj_thread_register("Transport Monitor", *desc, &thread); - } - - if (!monitored->sip_received) { - ast_log(LOG_NOTICE, "Shutting down transport '%s' since no request was received in %d seconds\n", - monitored->transport->info, IDLE_TIMEOUT / 1000); - pjsip_transport_shutdown(monitored->transport); - } - - ao2_ref(monitored, -1); - return 0; -} - -/*! \brief Destructor for keepalive transport */ -static void monitored_transport_destroy(void *obj) -{ - struct monitored_transport *monitored = obj; - - pjsip_transport_dec_ref(monitored->transport); -} - -/*! \brief Callback invoked when transport changes occur */ -static void monitored_transport_state_callback(pjsip_transport *transport, pjsip_transport_state state, - const pjsip_transport_state_info *info) -{ - struct ao2_container *transports; - - /* We only care about reliable transports */ - if (PJSIP_TRANSPORT_IS_RELIABLE(transport) - && (transport->dir == PJSIP_TP_DIR_INCOMING || keepalive_interval) - && (transports = ao2_global_obj_ref(monitored_transports))) { - struct monitored_transport *monitored; - - switch (state) { - case PJSIP_TP_STATE_CONNECTED: - monitored = ao2_alloc_options(sizeof(*monitored), - monitored_transport_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); - if (!monitored) { - break; - } - monitored->transport = transport; - pjsip_transport_add_ref(monitored->transport); - - ao2_link(transports, monitored); - - if (transport->dir == PJSIP_TP_DIR_INCOMING) { - /* Let the scheduler inherit the reference from allocation */ - if (ast_sched_add_variable(sched, IDLE_TIMEOUT, idle_sched_cb, monitored, 1) < 0) { - /* Uh Oh. Could not schedule the idle check. Kill the transport. */ - pjsip_transport_shutdown(transport); - } else { - /* monitored ref successfully passed to idle_sched_cb() */ - break; - } - } - ao2_ref(monitored, -1); - break; - case PJSIP_TP_STATE_SHUTDOWN: - case PJSIP_TP_STATE_DISCONNECTED: - ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK); - break; - default: - break; - } - - ao2_ref(transports, -1); - } -} - -struct ast_sip_tpmgr_state_callback monitored_transport_reg = { - monitored_transport_state_callback, -}; - -/*! \brief Hashing function for monitored transport */ -static int monitored_transport_hash_fn(const void *obj, int flags) -{ - const struct monitored_transport *object; - const char *key; - - switch (flags & OBJ_SEARCH_MASK) { - case OBJ_SEARCH_KEY: - key = obj; - break; - case OBJ_SEARCH_OBJECT: - object = obj; - key = object->transport->obj_name; - break; - default: - /* Hash can only work on something with a full key. */ - ast_assert(0); - return 0; - } - return ast_str_hash(key); -} - -/*! \brief Comparison function for monitored transport */ -static int monitored_transport_cmp_fn(void *obj, void *arg, int flags) -{ - const struct monitored_transport *object_left = obj; - const struct monitored_transport *object_right = arg; - const char *right_key = arg; - int cmp; - - switch (flags & OBJ_SEARCH_MASK) { - case OBJ_SEARCH_OBJECT: - right_key = object_right->transport->obj_name; - /* Fall through */ - case OBJ_SEARCH_KEY: - cmp = strcmp(object_left->transport->obj_name, right_key); - break; - case OBJ_SEARCH_PARTIAL_KEY: - /* - * We could also use a partial key struct containing a length - * so strlen() does not get called for every comparison instead. - */ - cmp = strncmp(object_left->transport->obj_name, right_key, strlen(right_key)); - break; - default: - /* - * What arg points to is specific to this traversal callback - * and has no special meaning to astobj2. - */ - cmp = 0; - break; - } - - return !cmp ? CMP_MATCH : 0; -} - -static void keepalive_global_loaded(const char *object_type) -{ - unsigned int new_interval = ast_sip_get_keep_alive_interval(); - - if (new_interval) { - keepalive_interval = new_interval; - } else if (keepalive_interval) { - ast_log(LOG_NOTICE, "Keepalive support can not be disabled once activated.\n"); - return; - } else { - /* This will occur if no keepalive interval has been specified at initial start */ - return; - } - - if (keepalive_thread != AST_PTHREADT_NULL) { - return; - } - - if (ast_pthread_create(&keepalive_thread, NULL, keepalive_transport_thread, NULL)) { - ast_log(LOG_ERROR, "Could not create thread for sending keepalive messages.\n"); - keepalive_thread = AST_PTHREADT_NULL; - keepalive_interval = 0; - } -} - -/*! \brief Observer which is used to update our interval when the global setting changes */ -static struct ast_sorcery_observer keepalive_global_observer = { - .loaded = keepalive_global_loaded, -}; - -/*! - * \brief - * On incoming TCP connections, when we receive a SIP request, we mark that we have - * received a valid SIP request. This way, we will not shut the transport down for - * idleness - */ -static pj_bool_t idle_monitor_on_rx_request(pjsip_rx_data *rdata) -{ - struct ao2_container *transports; - struct monitored_transport *idle_trans; - - transports = ao2_global_obj_ref(monitored_transports); - if (!transports) { - return PJ_FALSE; - } - - idle_trans = ao2_find(transports, rdata->tp_info.transport->obj_name, OBJ_SEARCH_KEY); - ao2_ref(transports, -1); - if (!idle_trans) { - return PJ_FALSE; - } - - idle_trans->sip_received = 1; - ao2_ref(idle_trans, -1); - - return PJ_FALSE; -} - -static pjsip_module idle_monitor_module = { - .name = {"idle monitor module", 19}, - .priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER + 3, - .on_rx_request = idle_monitor_on_rx_request, -}; - -static int load_module(void) -{ - struct ao2_container *transports; - - transports = ao2_container_alloc(TRANSPORTS_BUCKETS, monitored_transport_hash_fn, - monitored_transport_cmp_fn); - if (!transports) { - ast_log(LOG_ERROR, "Could not create container for transports to perform keepalive on.\n"); - return AST_MODULE_LOAD_DECLINE; - } - ao2_global_obj_replace_unref(monitored_transports, transports); - ao2_ref(transports, -1); - - sched = ast_sched_context_create(); - if (!sched) { - ast_log(LOG_ERROR, "Failed to create keepalive scheduler context.\n"); - ao2_global_obj_release(monitored_transports); - return AST_MODULE_LOAD_DECLINE; - } - - if (ast_sched_start_thread(sched)) { - ast_log(LOG_ERROR, "Failed to start keepalive scheduler thread\n"); - ast_sched_context_destroy(sched); - sched = NULL; - ao2_global_obj_release(monitored_transports); - return AST_MODULE_LOAD_DECLINE; - } - - ast_sip_register_service(&idle_monitor_module); - - ast_sip_transport_state_register(&monitored_transport_reg); - - ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &keepalive_global_observer); - ast_sorcery_reload_object(ast_sip_get_sorcery(), "global"); - - ast_module_shutdown_ref(ast_module_info->self); - return AST_MODULE_LOAD_SUCCESS; -} - -static int unload_module(void) -{ - if (keepalive_interval) { - keepalive_interval = 0; - if (keepalive_thread != AST_PTHREADT_NULL) { - pthread_kill(keepalive_thread, SIGURG); - pthread_join(keepalive_thread, NULL); - keepalive_thread = AST_PTHREADT_NULL; - } - } - - ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &keepalive_global_observer); - - ast_sip_transport_state_unregister(&monitored_transport_reg); - - ast_sip_unregister_service(&idle_monitor_module); - - ast_sched_context_destroy(sched); - sched = NULL; - - ao2_global_obj_release(monitored_transports); - - return 0; -} - -static int reload_module(void) -{ - ast_sorcery_reload_object(ast_sip_get_sorcery(), "global"); - return 0; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Reliable Transport Management", - .support_level = AST_MODULE_SUPPORT_CORE, - .load = load_module, - .reload = reload_module, - .unload = unload_module, - .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4, - .requires = "res_pjsip", -); -- cgit v1.2.3