summaryrefslogtreecommitdiff
path: root/res/res_pjsip
diff options
context:
space:
mode:
Diffstat (limited to 'res/res_pjsip')
-rw-r--r--res/res_pjsip/config_global.c57
-rw-r--r--res/res_pjsip/pjsip_configuration.c56
-rw-r--r--res/res_pjsip/pjsip_distributor.c358
3 files changed, 459 insertions, 12 deletions
diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c
index ccfec5435..6bb688804 100644
--- a/res/res_pjsip/config_global.c
+++ b/res/res_pjsip/config_global.c
@@ -35,10 +35,14 @@
#define DEFAULT_ENDPOINT_IDENTIFIER_ORDER "ip,username,anonymous"
#define DEFAULT_MAX_INITIAL_QUALIFY_TIME 0
#define DEFAULT_FROM_USER "asterisk"
+#define DEFAULT_REALM "asterisk"
#define DEFAULT_REGCONTEXT ""
#define DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL 30
#define DEFAULT_DISABLE_MULTI_DOMAIN 0
#define DEFAULT_VOICEMAIL_EXTENSION ""
+#define DEFAULT_UNIDENTIFIED_REQUEST_COUNT 5
+#define DEFAULT_UNIDENTIFIED_REQUEST_PERIOD 5
+#define DEFAULT_UNIDENTIFIED_REQUEST_PRUNE_INTERVAL 30
static char default_useragent[256];
@@ -56,6 +60,8 @@ struct global_config {
AST_STRING_FIELD(default_from_user);
/*! Default voicemail extension */
AST_STRING_FIELD(default_voicemail_extension);
+ /*! Realm to use in challenges before an endpoint is identified */
+ AST_STRING_FIELD(default_realm);
);
/* Value to put in Max-Forwards header */
unsigned int max_forwards;
@@ -67,6 +73,12 @@ struct global_config {
unsigned int contact_expiration_check_interval;
/*! Nonzero to disable multi domain support */
unsigned int disable_multi_domain;
+ /* The maximum number of unidentified requests per source IP address before a security event is logged */
+ unsigned int unidentified_request_count;
+ /* The period during which unidentified requests are accumulated */
+ unsigned int unidentified_request_period;
+ /* Interval at which expired unidentifed requests will be pruned */
+ unsigned int unidentified_request_prune_interval;
};
static void global_destructor(void *obj)
@@ -255,6 +267,40 @@ unsigned int ast_sip_get_max_initial_qualify_time(void)
return time;
}
+void ast_sip_get_unidentified_request_thresholds(unsigned int *count, unsigned int *period,
+ unsigned int *prune_interval)
+{
+ struct global_config *cfg;
+
+ cfg = get_global_cfg();
+ if (!cfg) {
+ *count = DEFAULT_UNIDENTIFIED_REQUEST_COUNT;
+ *period = DEFAULT_UNIDENTIFIED_REQUEST_PERIOD;
+ *prune_interval = DEFAULT_UNIDENTIFIED_REQUEST_PRUNE_INTERVAL;
+ return;
+ }
+
+ *count = cfg->unidentified_request_count;
+ *period = cfg->unidentified_request_period;
+ *prune_interval = cfg->unidentified_request_prune_interval;
+
+ ao2_ref(cfg, -1);
+ return;
+}
+
+void ast_sip_get_default_realm(char *realm, size_t size)
+{
+ struct global_config *cfg;
+
+ cfg = get_global_cfg();
+ if (!cfg) {
+ ast_copy_string(realm, DEFAULT_REALM, size);
+ } else {
+ ast_copy_string(realm, cfg->default_realm, size);
+ ao2_ref(cfg, -1);
+ }
+}
+
void ast_sip_get_default_from_user(char *from_user, size_t size)
{
struct global_config *cfg;
@@ -393,6 +439,17 @@ int ast_sip_initialize_sorcery_global(void)
OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
ast_sorcery_object_field_register(sorcery, "global", "disable_multi_domain", "no",
OPT_BOOL_T, 1, FLDSET(struct global_config, disable_multi_domain));
+ ast_sorcery_object_field_register(sorcery, "global", "unidentified_request_count",
+ __stringify(DEFAULT_UNIDENTIFIED_REQUEST_COUNT),
+ OPT_UINT_T, 0, FLDSET(struct global_config, unidentified_request_count));
+ ast_sorcery_object_field_register(sorcery, "global", "unidentified_request_period",
+ __stringify(DEFAULT_UNIDENTIFIED_REQUEST_PERIOD),
+ OPT_UINT_T, 0, FLDSET(struct global_config, unidentified_request_period));
+ ast_sorcery_object_field_register(sorcery, "global", "unidentified_request_prune_interval",
+ __stringify(DEFAULT_UNIDENTIFIED_REQUEST_PRUNE_INTERVAL),
+ OPT_UINT_T, 0, FLDSET(struct global_config, unidentified_request_prune_interval));
+ ast_sorcery_object_field_register(sorcery, "global", "default_realm", DEFAULT_REALM,
+ OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_realm));
if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
return -1;
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index baa5063e4..c370ab75f 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -476,6 +476,16 @@ static int ident_handler(const struct aco_option *opt, struct ast_variable *var,
struct ast_sip_endpoint *endpoint = obj;
char *idents = ast_strdupa(var->value);
char *val;
+ enum ast_sip_endpoint_identifier_type method;
+
+ /*
+ * If there's already something in the vector when we get here,
+ * it's the default value so we need to clean it out.
+ */
+ if (AST_VECTOR_SIZE(&endpoint->ident_method_order)) {
+ AST_VECTOR_RESET(&endpoint->ident_method_order, AST_VECTOR_ELEM_CLEANUP_NOOP);
+ endpoint->ident_method = 0;
+ }
while ((val = ast_strip(strsep(&idents, ",")))) {
if (ast_strlen_zero(val)) {
@@ -483,27 +493,55 @@ static int ident_handler(const struct aco_option *opt, struct ast_variable *var,
}
if (!strcasecmp(val, "username")) {
- endpoint->ident_method |= AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME;
+ method = AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME;
+ } else if (!strcasecmp(val, "auth_username")) {
+ method = AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME;
} else {
ast_log(LOG_ERROR, "Unrecognized identification method %s specified for endpoint %s\n",
val, ast_sorcery_object_get_id(endpoint));
+ AST_VECTOR_RESET(&endpoint->ident_method_order, AST_VECTOR_ELEM_CLEANUP_NOOP);
+ endpoint->ident_method = 0;
return -1;
}
+
+ endpoint->ident_method |= method;
+ AST_VECTOR_APPEND(&endpoint->ident_method_order, method);
}
+
return 0;
}
static int ident_to_str(const void *obj, const intptr_t *args, char **buf)
{
const struct ast_sip_endpoint *endpoint = obj;
- switch (endpoint->ident_method) {
- case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME :
- *buf = "username"; break;
- default:
+ int methods;
+ char *method;
+ int i;
+ int j = 0;
+
+ methods = AST_VECTOR_SIZE(&endpoint->ident_method_order);
+ if (!methods) {
return 0;
}
- *buf = ast_strdup(*buf);
+ if (!(*buf = ast_calloc(MAX_OBJECT_FIELD, sizeof(char)))) {
+ return -1;
+ }
+
+ for (i = 0; i < methods; i++) {
+ switch (AST_VECTOR_GET(&endpoint->ident_method_order, i)) {
+ case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME :
+ method = "username";
+ break;
+ case AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME :
+ method = "auth_username";
+ break;
+ default:
+ continue;
+ }
+ j = sprintf(*buf + j, "%s%s", method, i < methods - 1 ? "," : "");
+ }
+
return 0;
}
@@ -1849,6 +1887,7 @@ static void endpoint_destructor(void* obj)
endpoint->pickup.named_pickupgroups = ast_unref_namedgroups(endpoint->pickup.named_pickupgroups);
ao2_cleanup(endpoint->persistent);
ast_variables_destroy(endpoint->channel_vars);
+ AST_VECTOR_FREE(&endpoint->ident_method_order);
}
static int init_subscription_configuration(struct ast_sip_endpoint_subscription_configuration *subscription)
@@ -1893,6 +1932,11 @@ void *ast_sip_endpoint_alloc(const char *name)
return NULL;
}
ast_party_id_init(&endpoint->id.self);
+
+ if (AST_VECTOR_INIT(&endpoint->ident_method_order, 1)) {
+ return NULL;
+ }
+
return endpoint;
}
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index 834ca10d3..cbe955728 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -24,6 +24,7 @@
#include "include/res_pjsip_private.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/threadpool.h"
+#include "asterisk/res_pjsip_cli.h"
static int distribute(void *data);
static pj_bool_t distributor(pjsip_rx_data *rdata);
@@ -37,6 +38,26 @@ static pjsip_module distributor_mod = {
.on_rx_response = distributor,
};
+struct ast_sched_context *prune_context;
+
+/* From the auth/realm realtime column size */
+#define MAX_REALM_LENGTH 40
+static char default_realm[MAX_REALM_LENGTH + 1];
+
+#define DEFAULT_SUSPECTS_BUCKETS 53
+
+static struct ao2_container *unidentified_requests;
+static unsigned int unidentified_count;
+static unsigned int unidentified_period;
+static unsigned int unidentified_prune_interval;
+static int using_auth_username;
+
+struct unidentified_request{
+ struct timeval first_seen;
+ int count;
+ char src_name[];
+};
+
/*!
* \internal
* \brief Record the task's serializer name on the tdata structure.
@@ -322,7 +343,7 @@ static int create_artificial_auth(void)
return -1;
}
- ast_string_field_set(artificial_auth, realm, "asterisk");
+ ast_string_field_set(artificial_auth, realm, default_realm);
ast_string_field_set(artificial_auth, auth_user, "");
ast_string_field_set(artificial_auth, auth_pass, "");
artificial_auth->type = AST_SIP_AUTH_TYPE_ARTIFICIAL;
@@ -359,27 +380,65 @@ struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void)
return artificial_endpoint;
}
-static void log_unidentified_request(pjsip_rx_data *rdata)
+static void log_unidentified_request(pjsip_rx_data *rdata, unsigned int count, unsigned int period)
{
char from_buf[PJSIP_MAX_URL_SIZE];
char callid_buf[PJSIP_MAX_URL_SIZE];
pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.from->uri, from_buf, PJSIP_MAX_URL_SIZE);
ast_copy_pj_str(callid_buf, &rdata->msg_info.cid->id, PJSIP_MAX_URL_SIZE);
- ast_log(LOG_NOTICE, "Request from '%s' failed for '%s:%d' (callid: %s) - No matching endpoint found\n",
- from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf);
+ if (count) {
+ ast_log(LOG_NOTICE, "Request from '%s' failed for '%s:%d' (callid: %s) - No matching endpoint found"
+ " after %u tries in %.3f ms\n",
+ from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf, count, period / 1000.0);
+ } else {
+ ast_log(LOG_NOTICE, "Request from '%s' failed for '%s:%d' (callid: %s) - No matching endpoint found",
+ from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf);
+ }
+}
+
+static void check_endpoint(pjsip_rx_data *rdata, struct unidentified_request *unid,
+ const char *name)
+{
+ int64_t ms = ast_tvdiff_ms(ast_tvnow(), unid->first_seen);
+
+ ao2_wrlock(unid);
+ unid->count++;
+
+ if (ms < (unidentified_period * 1000) && unid->count >= unidentified_count) {
+ log_unidentified_request(rdata, unid->count, ms);
+ ast_sip_report_invalid_endpoint(name, rdata);
+ }
+ ao2_unlock(unid);
}
static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata)
{
struct ast_sip_endpoint *endpoint;
+ struct unidentified_request *unid;
int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD;
endpoint = rdata->endpt_info.mod_data[endpoint_mod.id];
if (endpoint) {
+ /*
+ * ao2_find with OBJ_UNLINK always write locks the container before even searching
+ * for the object. Since the majority case is that the object won't be found, do
+ * the find without OBJ_UNLINK to prevent the unnecessary write lock, then unlink
+ * if needed.
+ */
+ if ((unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY))) {
+ ao2_unlink(unidentified_requests, unid);
+ ao2_ref(unid, -1);
+ }
return PJ_FALSE;
}
endpoint = ast_sip_identify_endpoint(rdata);
+ if (endpoint) {
+ if ((unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY))) {
+ ao2_unlink(unidentified_requests, unid);
+ ao2_ref(unid, -1);
+ }
+ }
if (!endpoint && !is_ack) {
char name[AST_UUID_STR_LEN] = "";
@@ -397,8 +456,32 @@ static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata)
ast_copy_pj_str(name, &sip_from->user, sizeof(name));
}
- log_unidentified_request(rdata);
- ast_sip_report_invalid_endpoint(name, rdata);
+ if ((unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY))) {
+ check_endpoint(rdata, unid, name);
+ ao2_ref(unid, -1);
+ } else if (using_auth_username) {
+ ao2_wrlock(unidentified_requests);
+ /* The check again with the write lock held allows us to eliminate the DUPS_REPLACE and sort_fn */
+ if ((unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY | OBJ_NOLOCK))) {
+ check_endpoint(rdata, unid, name);
+ } else {
+ unid = ao2_alloc_options(sizeof(*unid) + strlen(rdata->pkt_info.src_name) + 1, NULL,
+ AO2_ALLOC_OPT_LOCK_RWLOCK);
+ if (!unid) {
+ ao2_unlock(unidentified_requests);
+ return PJ_TRUE;
+ }
+ strcpy(unid->src_name, rdata->pkt_info.src_name); /* Safe */
+ unid->first_seen = ast_tvnow();
+ unid->count = 1;
+ ao2_link_flags(unidentified_requests, unid, OBJ_NOLOCK);
+ }
+ ao2_ref(unid, -1);
+ ao2_unlock(unidentified_requests);
+ } else {
+ log_unidentified_request(rdata, 0, 0);
+ ast_sip_report_invalid_endpoint(name, rdata);
+ }
}
rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint;
return PJ_FALSE;
@@ -413,6 +496,8 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata)
if (!is_ack && ast_sip_requires_authentication(endpoint, rdata)) {
pjsip_tx_data *tdata;
+ struct unidentified_request *unid;
+
pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 401, NULL, &tdata);
switch (ast_sip_check_authentication(endpoint, rdata, tdata)) {
case AST_SIP_AUTHENTICATION_CHALLENGE:
@@ -421,6 +506,11 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata)
pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL);
return PJ_TRUE;
case AST_SIP_AUTHENTICATION_SUCCESS:
+ /* See note in endpoint_lookup about not holding an unnecessary write lock */
+ if ((unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY))) {
+ ao2_unlink(unidentified_requests, unid);
+ ao2_ref(unid, -1);
+ }
ast_sip_report_auth_success(endpoint, rdata);
pjsip_tx_data_dec_ref(tdata);
return PJ_FALSE;
@@ -480,31 +570,287 @@ struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata)
return endpoint;
}
+static int suspects_sort(const void *obj, const void *arg, int flags)
+{
+ const struct unidentified_request *object_left = obj;
+ const struct unidentified_request *object_right = arg;
+ const char *right_key = arg;
+ int cmp;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = object_right->src_name;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = strcmp(object_left->src_name, right_key);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ cmp = strncmp(object_left->src_name, right_key, strlen(right_key));
+ break;
+ default:
+ cmp = 0;
+ break;
+ }
+ return cmp;
+}
+
+static int suspects_compare(void *obj, void *arg, int flags)
+{
+ const struct unidentified_request *object_left = obj;
+ const struct unidentified_request *object_right = arg;
+ const char *right_key = arg;
+ int cmp = 0;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = object_right->src_name;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ if (strcmp(object_left->src_name, right_key) == 0) {
+ cmp = CMP_MATCH | CMP_STOP;
+ }
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ if (strncmp(object_left->src_name, right_key, strlen(right_key)) == 0) {
+ cmp = CMP_MATCH;
+ }
+ break;
+ default:
+ cmp = 0;
+ break;
+ }
+ return cmp;
+}
+
+static int suspects_hash(const void *obj, int flags) {
+ const struct unidentified_request *object_left = obj;
+
+ if (flags & OBJ_SEARCH_OBJECT) {
+ return ast_str_hash(object_left->src_name);
+ } else if (flags & OBJ_SEARCH_KEY) {
+ return ast_str_hash(obj);
+ }
+ return -1;
+}
+
+static struct ao2_container *cli_unid_get_container(const char *regex)
+{
+ struct ao2_container *s_container;
+
+ s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+ suspects_sort, suspects_compare);
+ if (!s_container) {
+ return NULL;
+ }
+
+ if (ao2_container_dup(s_container, unidentified_requests, 0)) {
+ ao2_ref(s_container, -1);
+ return NULL;
+ }
+
+ return s_container;
+}
+
+static int cli_unid_iterate(void *container, ao2_callback_fn callback, void *args)
+{
+ ao2_callback(container, 0, callback, args);
+
+ return 0;
+}
+
+static void *cli_unid_retrieve_by_id(const char *id)
+{
+ return ao2_find(unidentified_requests, id, OBJ_SEARCH_KEY);
+}
+
+static const char *cli_unid_get_id(const void *obj)
+{
+ const struct unidentified_request *unid = obj;
+
+ return unid->src_name;
+}
+
+static int cli_unid_print_header(void *obj, void *arg, int flags)
+{
+ struct ast_sip_cli_context *context = arg;
+ RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup);
+
+ int indent = CLI_INDENT_TO_SPACES(context->indent_level);
+ int filler = CLI_LAST_TABSTOP - indent - 7;
+
+ ast_assert(context->output_buffer != NULL);
+
+ ast_str_append(&context->output_buffer, 0,
+ "%*s: <IP Address%*.*s> <Count> <Age(sec)>\n",
+ indent, "Request", filler, filler, CLI_HEADER_FILLER);
+
+ return 0;
+}
+static int cli_unid_print_body(void *obj, void *arg, int flags)
+{
+ struct unidentified_request *unid = obj;
+ struct ast_sip_cli_context *context = arg;
+ int indent;
+ int flexwidth;
+ int64_t ms = ast_tvdiff_ms(ast_tvnow(), unid->first_seen);
+
+ ast_assert(context->output_buffer != NULL);
+
+ indent = CLI_INDENT_TO_SPACES(context->indent_level);
+ flexwidth = CLI_LAST_TABSTOP - 4;
+
+ ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %7d %10.3f\n",
+ indent,
+ "Request",
+ flexwidth, flexwidth,
+ unid->src_name, unid->count, ms / 1000.0);
+
+ return 0;
+}
+
+static struct ast_cli_entry cli_commands[] = {
+ AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Unidentified Requests",
+ .command = "pjsip show unidentified_requests",
+ .usage = "Usage: pjsip show unidentified_requests\n"
+ " Show the PJSIP Unidentified Requests\n"),
+};
+
+struct ast_sip_cli_formatter_entry *unid_formatter;
+
+static int expire_requests(void *object, void *arg, int flags)
+{
+ struct unidentified_request *unid = object;
+ int *maxage = arg;
+ int64_t ms = ast_tvdiff_ms(ast_tvnow(), unid->first_seen);
+
+ if (ms > (*maxage) * 2 * 1000) {
+ return CMP_MATCH;
+ }
+
+ return 0;
+}
+
+static int prune_task(const void *data)
+{
+ unsigned int maxage;
+
+ ast_sip_get_unidentified_request_thresholds(&unidentified_count, &unidentified_period, &unidentified_prune_interval);
+ maxage = unidentified_period * 2;
+ ao2_callback(unidentified_requests, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK, expire_requests, &maxage);
+
+ return unidentified_prune_interval * 1000;
+}
+
+static int clean_task(const void *data)
+{
+ return 0;
+}
+
+static void global_loaded(const char *object_type)
+{
+ char *identifier_order = ast_sip_get_endpoint_identifier_order();
+ char *io_copy = ast_strdupa(identifier_order);
+ char *identify_method;
+
+ ast_free(identifier_order);
+ using_auth_username = 0;
+ while ((identify_method = ast_strip(strsep(&io_copy, ",")))) {
+ if (!strcmp(identify_method, "auth_username")) {
+ using_auth_username = 1;
+ break;
+ }
+ }
+
+ ast_sip_get_default_realm(default_realm, sizeof(default_realm));
+ ast_sip_get_unidentified_request_thresholds(&unidentified_count, &unidentified_period, &unidentified_prune_interval);
+
+ /* Clean out the old task, if any */
+ ast_sched_clean_by_callback(prune_context, prune_task, clean_task);
+ if (ast_sched_add_variable(prune_context, unidentified_prune_interval * 1000, prune_task, NULL, 1) < 0) {
+ return;
+ }
+}
+
+/*! \brief Observer which is used to update our interval and default_realm when the global setting changes */
+static struct ast_sorcery_observer global_observer = {
+ .loaded = global_loaded,
+};
+
+
int ast_sip_initialize_distributor(void)
{
+ unidentified_requests = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0,
+ DEFAULT_SUSPECTS_BUCKETS, suspects_hash, NULL, suspects_compare);
+ if (!unidentified_requests) {
+ return -1;
+ }
+
+ prune_context = ast_sched_context_create();
+ if (!prune_context) {
+ ast_sip_destroy_distributor();
+ return -1;
+ }
+
+ if (ast_sched_start_thread(prune_context)) {
+ ast_sip_destroy_distributor();
+ return -1;
+ }
+
+ ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer);
+ ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
+
if (create_artificial_endpoint() || create_artificial_auth()) {
+ ast_sip_destroy_distributor();
return -1;
}
if (internal_sip_register_service(&distributor_mod)) {
+ ast_sip_destroy_distributor();
return -1;
}
if (internal_sip_register_service(&endpoint_mod)) {
+ ast_sip_destroy_distributor();
return -1;
}
if (internal_sip_register_service(&auth_mod)) {
+ ast_sip_destroy_distributor();
return -1;
}
+ unid_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
+ if (!unid_formatter) {
+ ast_log(LOG_ERROR, "Unable to allocate memory for unid_formatter\n");
+ return -1;
+ }
+ unid_formatter->name = "unidentified_request";
+ unid_formatter->print_header = cli_unid_print_header;
+ unid_formatter->print_body = cli_unid_print_body;
+ unid_formatter->get_container = cli_unid_get_container;
+ unid_formatter->iterate = cli_unid_iterate;
+ unid_formatter->get_id = cli_unid_get_id;
+ unid_formatter->retrieve_by_id = cli_unid_retrieve_by_id;
+ ast_sip_register_cli_formatter(unid_formatter);
+ ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
+
return 0;
}
void ast_sip_destroy_distributor(void)
{
+ ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
+ ast_sip_unregister_cli_formatter(unid_formatter);
+
internal_sip_unregister_service(&distributor_mod);
internal_sip_unregister_service(&endpoint_mod);
internal_sip_unregister_service(&auth_mod);
ao2_cleanup(artificial_auth);
ao2_cleanup(artificial_endpoint);
+ ao2_cleanup(unidentified_requests);
+
+ ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &global_observer);
+
+ if (prune_context) {
+ ast_sched_context_destroy(prune_context);
+ }
}