asterisk/res/res_pjsip/pjsip_options.c

1456 lines
38 KiB
C
Raw Normal View History

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2013, Digium, Inc.
*
* Matt Jordan <mjordan@digium.com>
*
* 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 <pjsip.h>
#include <pjsip_ua.h>
#include <pjlib.h>
#include "asterisk/res_pjsip.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/astobj2.h"
#include "asterisk/cli.h"
#include "asterisk/time.h"
#include "asterisk/test.h"
#include "asterisk/statsd.h"
#include "include/res_pjsip_private.h"
#include "asterisk/taskprocessor.h"
#define DEFAULT_LANGUAGE "en"
#define DEFAULT_ENCODING "text/plain"
#define QUALIFIED_BUCKETS 211
static const char *status_map [] = {
[UNAVAILABLE] = "Unreachable",
[AVAILABLE] = "Reachable",
[UNKNOWN] = "Unknown",
[CREATED] = "Created",
[REMOVED] = "Removed",
};
static const char *short_status_map [] = {
[UNAVAILABLE] = "Unavail",
[AVAILABLE] = "Avail",
[UNKNOWN] = "Unknown",
[CREATED] = "Created",
[REMOVED] = "Removed",
};
const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status)
{
return status_map[status];
}
const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_status_type status)
{
return short_status_map[status];
}
/*!
* \internal
* \brief Destroy a ast_sip_contact_status object.
*/
static void contact_status_destroy(void * obj)
{
struct ast_sip_contact_status *status = obj;
ast_string_field_free_memory(status);
}
/*!
* \internal
* \brief Create a ast_sip_contact_status object.
*/
static void *contact_status_alloc(const char *name)
{
struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), contact_status_destroy);
char *id = ast_strdupa(name);
char *aor = id;
char *aor_separator = NULL;
if (!status) {
ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n");
return NULL;
}
if (ast_string_field_init(status, 256)) {
ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status stringfields\n");
ao2_cleanup(status);
return NULL;
}
/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) {
*aor_separator = '\0';
}
ast_assert(aor_separator != NULL);
ast_string_field_set(status, aor, aor);
status->status = CREATED;
return status;
}
AST_MUTEX_DEFINE_STATIC(creation_lock);
/*!
* \brief Retrieve a ast_sip_contact_status object from sorcery creating
* one if not found.
*/
struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const struct ast_sip_contact *contact)
{
struct ast_sip_contact_status *status;
SCOPED_MUTEX(lock, &creation_lock);
status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS,
ast_sorcery_object_get_id(contact));
if (status) {
return status;
}
status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
ast_sorcery_object_get_id(contact));
if (!status) {
ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n",
contact->aor, contact->uri);
return NULL;
}
ast_string_field_set(status, uri, contact->uri);
status->rtt_start = ast_tv(0, 0);
status->rtt = 0;
if (ast_sorcery_create(ast_sip_get_sorcery(), status)) {
ast_log(LOG_ERROR, "Unable to persist ast_sip_contact_status for contact %s\n",
contact->uri);
ao2_ref(status, -1);
return NULL;
}
ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
"+1", 1.0, ast_sip_get_contact_status_label(status->status));
return status;
}
/*!
* \internal
* \brief Update an ast_sip_contact_status's elements.
*/
static void update_contact_status(const struct ast_sip_contact *contact,
enum ast_sip_contact_status_type value, int is_contact_refresh)
{
RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);
RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup);
status = ast_res_pjsip_find_or_create_contact_status(contact);
if (!status) {
ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",
contact->uri);
return;
}
if (is_contact_refresh
&& status->status == CREATED) {
/*
* The contact status hasn't been updated since creation
* and we don't want to re-send a created status.
*/
if (contact->qualify_frequency
|| status->rtt_start.tv_sec > 0) {
/* Ignore, the status will change soon. */
return;
}
/*
* Convert to a regular contact status update
* because the status may never change.
*/
is_contact_refresh = 0;
value = UNKNOWN;
}
update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
ast_sorcery_object_get_id(status));
if (!update) {
ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status for contact %s\n",
contact->uri);
return;
}
ast_string_field_set(update, uri, contact->uri);
if (is_contact_refresh) {
/* Copy everything just to set the refresh flag. */
update->status = status->status;
update->last_status = status->last_status;
update->rtt = status->rtt;
update->rtt_start = status->rtt_start;
update->refresh = 1;
} else {
update->last_status = status->status;
update->status = value;
/*
* if the contact is available calculate the rtt as
* the diff between the last start time and "now"
*/
update->rtt = update->status == AVAILABLE && status->rtt_start.tv_sec > 0
? ast_tvdiff_us(ast_tvnow(), status->rtt_start)
: 0;
update->rtt_start = ast_tv(0, 0);
ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT",
"Contact: %s\r\n"
"Status: %s\r\n"
"RTT: %" PRId64,
ast_sorcery_object_get_id(update),
ast_sip_get_contact_status_label(update->status),
update->rtt);
}
if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
contact->uri);
}
}
/*!
* \internal
* \brief Initialize the start time on a contact status so the round
* trip time can be calculated upon a valid response.
*/
static void init_start_time(const struct ast_sip_contact *contact)
{
RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);
RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup);
status = ast_res_pjsip_find_or_create_contact_status(contact);
if (!status) {
ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",
contact->uri);
return;
}
update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
ast_sorcery_object_get_id(status));
if (!update) {
ast_log(LOG_ERROR, "Unable to copy ast_sip_contact_status for contact %s\n",
contact->uri);
return;
}
ast_string_field_set(status, uri, contact->uri);
update->status = status->status;
update->last_status = status->last_status;
update->rtt = status->rtt;
update->rtt_start = ast_tvnow();
if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
contact->uri);
}
}
/*!
* \internal
res_pjsip/pjsip_options: Fix From generation on outgoing OPTIONS No one seemed to notice but every time an OPTIONS goes out, it goes out with a From of "asterisk" (or whatever the default from_user is set to), even if you specify an endpoint. The issue had several causes... qualify_contact is only called with an endpoint if called from the CLI. If the endpoint is NULL, qualify_contact only looks up the endpoint if authenticate_qualify=yes. Even then, it never passes it on to ast_sip_create_request where the From header is set. Therefore From is always "asterisk" (or whatever the default from_user is set to). Even if ast_sip_create_request were to get an endpoint, it only sets the From if endpoint->from_user is set. The fix is 4 parts... First, create_out_of_dialog_request was modified to use the endpoint id if endpoint was specified and from_user is not set. Second, qualify_contact was modified to always look up an endpoint if one wasn't specified regardless of authenticate_qualify. It then passes the endpoint on to create_out_of_dialog_request. Third (and most importantly), find_an_endpoint was modified to find an endpoint by using an "aors LIKE %contact->aor%" predicate with ast_sorcery_retrieve_by_fields. As such, this patch will only work if the sorcery realtime optimizations patch goes in. Otherwise we'd be pulling the entire endpoints database every time we send an OPTIONS. Since we already know the contact's aor, the on_endpoint callback was also modified to just check if the contact->aor is an exact match to one of the endpoint's. Finally, since we now have an endpoint for every OPTIONS request, res_pjsip/endpt_send_request (which handles out-of-dialog reqests) was updated to get the transport from the endpoint and set it on tdata. Now the correct transport is used. Change-Id: I2207e12bb435e373bd1e03ad091d82e5aba011af
2016-03-11 01:52:14 +00:00
* \brief For an endpoint try to match the given contact->aor.
*/
static int on_endpoint(void *obj, void *arg, int flags)
{
struct ast_sip_endpoint *endpoint = obj;
res_pjsip/pjsip_options: Fix From generation on outgoing OPTIONS No one seemed to notice but every time an OPTIONS goes out, it goes out with a From of "asterisk" (or whatever the default from_user is set to), even if you specify an endpoint. The issue had several causes... qualify_contact is only called with an endpoint if called from the CLI. If the endpoint is NULL, qualify_contact only looks up the endpoint if authenticate_qualify=yes. Even then, it never passes it on to ast_sip_create_request where the From header is set. Therefore From is always "asterisk" (or whatever the default from_user is set to). Even if ast_sip_create_request were to get an endpoint, it only sets the From if endpoint->from_user is set. The fix is 4 parts... First, create_out_of_dialog_request was modified to use the endpoint id if endpoint was specified and from_user is not set. Second, qualify_contact was modified to always look up an endpoint if one wasn't specified regardless of authenticate_qualify. It then passes the endpoint on to create_out_of_dialog_request. Third (and most importantly), find_an_endpoint was modified to find an endpoint by using an "aors LIKE %contact->aor%" predicate with ast_sorcery_retrieve_by_fields. As such, this patch will only work if the sorcery realtime optimizations patch goes in. Otherwise we'd be pulling the entire endpoints database every time we send an OPTIONS. Since we already know the contact's aor, the on_endpoint callback was also modified to just check if the contact->aor is an exact match to one of the endpoint's. Finally, since we now have an endpoint for every OPTIONS request, res_pjsip/endpt_send_request (which handles out-of-dialog reqests) was updated to get the transport from the endpoint and set it on tdata. Now the correct transport is used. Change-Id: I2207e12bb435e373bd1e03ad091d82e5aba011af
2016-03-11 01:52:14 +00:00
char *contact_aor = arg;
char *aor_name;
char *aors;
if (!arg || ast_strlen_zero(endpoint->aors)) {
return 0;
}
aors = ast_strdupa(endpoint->aors);
while ((aor_name = ast_strip(strsep(&aors, ",")))) {
res_pjsip/pjsip_options: Fix From generation on outgoing OPTIONS No one seemed to notice but every time an OPTIONS goes out, it goes out with a From of "asterisk" (or whatever the default from_user is set to), even if you specify an endpoint. The issue had several causes... qualify_contact is only called with an endpoint if called from the CLI. If the endpoint is NULL, qualify_contact only looks up the endpoint if authenticate_qualify=yes. Even then, it never passes it on to ast_sip_create_request where the From header is set. Therefore From is always "asterisk" (or whatever the default from_user is set to). Even if ast_sip_create_request were to get an endpoint, it only sets the From if endpoint->from_user is set. The fix is 4 parts... First, create_out_of_dialog_request was modified to use the endpoint id if endpoint was specified and from_user is not set. Second, qualify_contact was modified to always look up an endpoint if one wasn't specified regardless of authenticate_qualify. It then passes the endpoint on to create_out_of_dialog_request. Third (and most importantly), find_an_endpoint was modified to find an endpoint by using an "aors LIKE %contact->aor%" predicate with ast_sorcery_retrieve_by_fields. As such, this patch will only work if the sorcery realtime optimizations patch goes in. Otherwise we'd be pulling the entire endpoints database every time we send an OPTIONS. Since we already know the contact's aor, the on_endpoint callback was also modified to just check if the contact->aor is an exact match to one of the endpoint's. Finally, since we now have an endpoint for every OPTIONS request, res_pjsip/endpt_send_request (which handles out-of-dialog reqests) was updated to get the transport from the endpoint and set it on tdata. Now the correct transport is used. Change-Id: I2207e12bb435e373bd1e03ad091d82e5aba011af
2016-03-11 01:52:14 +00:00
if (!strcmp(contact_aor, aor_name)) {
return CMP_MATCH;
}
}
return 0;
}
/*!
* \internal
* \brief Find an endpoint associated with the given contact.
*/
static struct ast_sip_endpoint *find_an_endpoint(struct ast_sip_contact *contact)
{
res_pjsip/pjsip_options: Fix From generation on outgoing OPTIONS No one seemed to notice but every time an OPTIONS goes out, it goes out with a From of "asterisk" (or whatever the default from_user is set to), even if you specify an endpoint. The issue had several causes... qualify_contact is only called with an endpoint if called from the CLI. If the endpoint is NULL, qualify_contact only looks up the endpoint if authenticate_qualify=yes. Even then, it never passes it on to ast_sip_create_request where the From header is set. Therefore From is always "asterisk" (or whatever the default from_user is set to). Even if ast_sip_create_request were to get an endpoint, it only sets the From if endpoint->from_user is set. The fix is 4 parts... First, create_out_of_dialog_request was modified to use the endpoint id if endpoint was specified and from_user is not set. Second, qualify_contact was modified to always look up an endpoint if one wasn't specified regardless of authenticate_qualify. It then passes the endpoint on to create_out_of_dialog_request. Third (and most importantly), find_an_endpoint was modified to find an endpoint by using an "aors LIKE %contact->aor%" predicate with ast_sorcery_retrieve_by_fields. As such, this patch will only work if the sorcery realtime optimizations patch goes in. Otherwise we'd be pulling the entire endpoints database every time we send an OPTIONS. Since we already know the contact's aor, the on_endpoint callback was also modified to just check if the contact->aor is an exact match to one of the endpoint's. Finally, since we now have an endpoint for every OPTIONS request, res_pjsip/endpt_send_request (which handles out-of-dialog reqests) was updated to get the transport from the endpoint and set it on tdata. Now the correct transport is used. Change-Id: I2207e12bb435e373bd1e03ad091d82e5aba011af
2016-03-11 01:52:14 +00:00
struct ao2_container *endpoints;
struct ast_sip_endpoint *endpoint;
res_pjsip/pjsip_options: Fix From generation on outgoing OPTIONS No one seemed to notice but every time an OPTIONS goes out, it goes out with a From of "asterisk" (or whatever the default from_user is set to), even if you specify an endpoint. The issue had several causes... qualify_contact is only called with an endpoint if called from the CLI. If the endpoint is NULL, qualify_contact only looks up the endpoint if authenticate_qualify=yes. Even then, it never passes it on to ast_sip_create_request where the From header is set. Therefore From is always "asterisk" (or whatever the default from_user is set to). Even if ast_sip_create_request were to get an endpoint, it only sets the From if endpoint->from_user is set. The fix is 4 parts... First, create_out_of_dialog_request was modified to use the endpoint id if endpoint was specified and from_user is not set. Second, qualify_contact was modified to always look up an endpoint if one wasn't specified regardless of authenticate_qualify. It then passes the endpoint on to create_out_of_dialog_request. Third (and most importantly), find_an_endpoint was modified to find an endpoint by using an "aors LIKE %contact->aor%" predicate with ast_sorcery_retrieve_by_fields. As such, this patch will only work if the sorcery realtime optimizations patch goes in. Otherwise we'd be pulling the entire endpoints database every time we send an OPTIONS. Since we already know the contact's aor, the on_endpoint callback was also modified to just check if the contact->aor is an exact match to one of the endpoint's. Finally, since we now have an endpoint for every OPTIONS request, res_pjsip/endpt_send_request (which handles out-of-dialog reqests) was updated to get the transport from the endpoint and set it on tdata. Now the correct transport is used. Change-Id: I2207e12bb435e373bd1e03ad091d82e5aba011af
2016-03-11 01:52:14 +00:00
struct ast_variable *var;
char *aor = ast_alloca(strlen(contact->aor) + 3);
sprintf(aor, "%%%s%%", contact->aor);
var = ast_variable_new("aors LIKE", aor, "");
endpoints = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
"endpoint", AST_RETRIEVE_FLAG_MULTIPLE, var);
ast_variables_destroy(var);
/*
* Because aors are a string list, we have to use a pattern match but since a simple
* pattern match could return an endpoint that has an aor of "aaabccc" when searching
* for "abc", we still have to iterate over them to find an exact aor match.
*/
endpoint = ao2_callback(endpoints, 0, on_endpoint, (char *)contact->aor);
ao2_ref(endpoints, -1);
res_pjsip/pjsip_options: Fix From generation on outgoing OPTIONS No one seemed to notice but every time an OPTIONS goes out, it goes out with a From of "asterisk" (or whatever the default from_user is set to), even if you specify an endpoint. The issue had several causes... qualify_contact is only called with an endpoint if called from the CLI. If the endpoint is NULL, qualify_contact only looks up the endpoint if authenticate_qualify=yes. Even then, it never passes it on to ast_sip_create_request where the From header is set. Therefore From is always "asterisk" (or whatever the default from_user is set to). Even if ast_sip_create_request were to get an endpoint, it only sets the From if endpoint->from_user is set. The fix is 4 parts... First, create_out_of_dialog_request was modified to use the endpoint id if endpoint was specified and from_user is not set. Second, qualify_contact was modified to always look up an endpoint if one wasn't specified regardless of authenticate_qualify. It then passes the endpoint on to create_out_of_dialog_request. Third (and most importantly), find_an_endpoint was modified to find an endpoint by using an "aors LIKE %contact->aor%" predicate with ast_sorcery_retrieve_by_fields. As such, this patch will only work if the sorcery realtime optimizations patch goes in. Otherwise we'd be pulling the entire endpoints database every time we send an OPTIONS. Since we already know the contact's aor, the on_endpoint callback was also modified to just check if the contact->aor is an exact match to one of the endpoint's. Finally, since we now have an endpoint for every OPTIONS request, res_pjsip/endpt_send_request (which handles out-of-dialog reqests) was updated to get the transport from the endpoint and set it on tdata. Now the correct transport is used. Change-Id: I2207e12bb435e373bd1e03ad091d82e5aba011af
2016-03-11 01:52:14 +00:00
return endpoint;
}
/*!
* \internal
* \brief Receive a response to the qualify contact request.
*/
static void qualify_contact_cb(void *token, pjsip_event *e)
{
struct ast_sip_contact *contact = token;
switch(e->body.tsx_state.type) {
default:
ast_log(LOG_ERROR, "Unexpected PJSIP event %u\n", e->body.tsx_state.type);
/* Fall through */
case PJSIP_EVENT_TRANSPORT_ERROR:
case PJSIP_EVENT_TIMER:
update_contact_status(contact, UNAVAILABLE, 0);
break;
case PJSIP_EVENT_RX_MSG:
update_contact_status(contact, AVAILABLE, 0);
break;
}
ao2_cleanup(contact);
}
/*!
* \internal
* \brief Attempt to qualify the contact
*
* \details Sends a SIP OPTIONS request to the given contact in order to make
* sure that contact is available.
*/
static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact)
{
pjsip_tx_data *tdata;
RAII_VAR(struct ast_sip_endpoint *, endpoint_local, NULL, ao2_cleanup);
res_pjsip/pjsip_options: Fix From generation on outgoing OPTIONS No one seemed to notice but every time an OPTIONS goes out, it goes out with a From of "asterisk" (or whatever the default from_user is set to), even if you specify an endpoint. The issue had several causes... qualify_contact is only called with an endpoint if called from the CLI. If the endpoint is NULL, qualify_contact only looks up the endpoint if authenticate_qualify=yes. Even then, it never passes it on to ast_sip_create_request where the From header is set. Therefore From is always "asterisk" (or whatever the default from_user is set to). Even if ast_sip_create_request were to get an endpoint, it only sets the From if endpoint->from_user is set. The fix is 4 parts... First, create_out_of_dialog_request was modified to use the endpoint id if endpoint was specified and from_user is not set. Second, qualify_contact was modified to always look up an endpoint if one wasn't specified regardless of authenticate_qualify. It then passes the endpoint on to create_out_of_dialog_request. Third (and most importantly), find_an_endpoint was modified to find an endpoint by using an "aors LIKE %contact->aor%" predicate with ast_sorcery_retrieve_by_fields. As such, this patch will only work if the sorcery realtime optimizations patch goes in. Otherwise we'd be pulling the entire endpoints database every time we send an OPTIONS. Since we already know the contact's aor, the on_endpoint callback was also modified to just check if the contact->aor is an exact match to one of the endpoint's. Finally, since we now have an endpoint for every OPTIONS request, res_pjsip/endpt_send_request (which handles out-of-dialog reqests) was updated to get the transport from the endpoint and set it on tdata. Now the correct transport is used. Change-Id: I2207e12bb435e373bd1e03ad091d82e5aba011af
2016-03-11 01:52:14 +00:00
if (endpoint) {
endpoint_local = ao2_bump(endpoint);
res_pjsip/pjsip_options: Fix From generation on outgoing OPTIONS No one seemed to notice but every time an OPTIONS goes out, it goes out with a From of "asterisk" (or whatever the default from_user is set to), even if you specify an endpoint. The issue had several causes... qualify_contact is only called with an endpoint if called from the CLI. If the endpoint is NULL, qualify_contact only looks up the endpoint if authenticate_qualify=yes. Even then, it never passes it on to ast_sip_create_request where the From header is set. Therefore From is always "asterisk" (or whatever the default from_user is set to). Even if ast_sip_create_request were to get an endpoint, it only sets the From if endpoint->from_user is set. The fix is 4 parts... First, create_out_of_dialog_request was modified to use the endpoint id if endpoint was specified and from_user is not set. Second, qualify_contact was modified to always look up an endpoint if one wasn't specified regardless of authenticate_qualify. It then passes the endpoint on to create_out_of_dialog_request. Third (and most importantly), find_an_endpoint was modified to find an endpoint by using an "aors LIKE %contact->aor%" predicate with ast_sorcery_retrieve_by_fields. As such, this patch will only work if the sorcery realtime optimizations patch goes in. Otherwise we'd be pulling the entire endpoints database every time we send an OPTIONS. Since we already know the contact's aor, the on_endpoint callback was also modified to just check if the contact->aor is an exact match to one of the endpoint's. Finally, since we now have an endpoint for every OPTIONS request, res_pjsip/endpt_send_request (which handles out-of-dialog reqests) was updated to get the transport from the endpoint and set it on tdata. Now the correct transport is used. Change-Id: I2207e12bb435e373bd1e03ad091d82e5aba011af
2016-03-11 01:52:14 +00:00
} else {
if (!ast_strlen_zero(contact->endpoint_name)) {
endpoint_local = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", contact->endpoint_name);
}
if (!endpoint_local) {
endpoint_local = find_an_endpoint(contact);
}
if (!endpoint_local) {
res_pjsip/pjsip_options: Fix From generation on outgoing OPTIONS No one seemed to notice but every time an OPTIONS goes out, it goes out with a From of "asterisk" (or whatever the default from_user is set to), even if you specify an endpoint. The issue had several causes... qualify_contact is only called with an endpoint if called from the CLI. If the endpoint is NULL, qualify_contact only looks up the endpoint if authenticate_qualify=yes. Even then, it never passes it on to ast_sip_create_request where the From header is set. Therefore From is always "asterisk" (or whatever the default from_user is set to). Even if ast_sip_create_request were to get an endpoint, it only sets the From if endpoint->from_user is set. The fix is 4 parts... First, create_out_of_dialog_request was modified to use the endpoint id if endpoint was specified and from_user is not set. Second, qualify_contact was modified to always look up an endpoint if one wasn't specified regardless of authenticate_qualify. It then passes the endpoint on to create_out_of_dialog_request. Third (and most importantly), find_an_endpoint was modified to find an endpoint by using an "aors LIKE %contact->aor%" predicate with ast_sorcery_retrieve_by_fields. As such, this patch will only work if the sorcery realtime optimizations patch goes in. Otherwise we'd be pulling the entire endpoints database every time we send an OPTIONS. Since we already know the contact's aor, the on_endpoint callback was also modified to just check if the contact->aor is an exact match to one of the endpoint's. Finally, since we now have an endpoint for every OPTIONS request, res_pjsip/endpt_send_request (which handles out-of-dialog reqests) was updated to get the transport from the endpoint and set it on tdata. Now the correct transport is used. Change-Id: I2207e12bb435e373bd1e03ad091d82e5aba011af
2016-03-11 01:52:14 +00:00
ast_log(LOG_ERROR, "Unable to find an endpoint to qualify contact %s\n",
contact->uri);
return -1;
}
}
res_pjsip/pjsip_options: Fix From generation on outgoing OPTIONS No one seemed to notice but every time an OPTIONS goes out, it goes out with a From of "asterisk" (or whatever the default from_user is set to), even if you specify an endpoint. The issue had several causes... qualify_contact is only called with an endpoint if called from the CLI. If the endpoint is NULL, qualify_contact only looks up the endpoint if authenticate_qualify=yes. Even then, it never passes it on to ast_sip_create_request where the From header is set. Therefore From is always "asterisk" (or whatever the default from_user is set to). Even if ast_sip_create_request were to get an endpoint, it only sets the From if endpoint->from_user is set. The fix is 4 parts... First, create_out_of_dialog_request was modified to use the endpoint id if endpoint was specified and from_user is not set. Second, qualify_contact was modified to always look up an endpoint if one wasn't specified regardless of authenticate_qualify. It then passes the endpoint on to create_out_of_dialog_request. Third (and most importantly), find_an_endpoint was modified to find an endpoint by using an "aors LIKE %contact->aor%" predicate with ast_sorcery_retrieve_by_fields. As such, this patch will only work if the sorcery realtime optimizations patch goes in. Otherwise we'd be pulling the entire endpoints database every time we send an OPTIONS. Since we already know the contact's aor, the on_endpoint callback was also modified to just check if the contact->aor is an exact match to one of the endpoint's. Finally, since we now have an endpoint for every OPTIONS request, res_pjsip/endpt_send_request (which handles out-of-dialog reqests) was updated to get the transport from the endpoint and set it on tdata. Now the correct transport is used. Change-Id: I2207e12bb435e373bd1e03ad091d82e5aba011af
2016-03-11 01:52:14 +00:00
if (ast_sip_create_request("OPTIONS", NULL, endpoint_local, NULL, contact, &tdata)) {
ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n",
contact->uri);
return -1;
}
/* If an outbound proxy is specified set it on this request */
if (!ast_strlen_zero(contact->outbound_proxy) &&
ast_sip_set_outbound_proxy(tdata, contact->outbound_proxy)) {
pjsip_tx_data_dec_ref(tdata);
ast_log(LOG_ERROR, "Unable to apply outbound proxy on request to qualify contact %s\n",
contact->uri);
return -1;
}
init_start_time(contact);
ao2_ref(contact, +1);
if (ast_sip_send_out_of_dialog_request(tdata, endpoint_local, (int)(contact->qualify_timeout * 1000), contact, qualify_contact_cb)
!= PJ_SUCCESS) {
ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
contact->uri);
update_contact_status(contact, UNAVAILABLE, 0);
ao2_ref(contact, -1);
return -1;
}
return 0;
}
/*!
* \internal
* \brief Scheduling context for sending QUALIFY request at specified intervals.
*/
static struct ast_sched_context *sched;
/*!
* \internal
* \brief Container to hold all actively scheduled qualifies.
*/
static struct ao2_container *sched_qualifies;
/*!
* \internal
* \brief Structure to hold qualify contact scheduling information.
*/
struct sched_data {
/*! The scheduling id */
int id;
/*! The the contact being checked */
struct ast_sip_contact *contact;
};
/*!
* \internal
* \brief Destroy the scheduled data and remove from scheduler.
*/
static void sched_data_destructor(void *obj)
{
struct sched_data *data = obj;
ao2_cleanup(data->contact);
}
/*!
* \internal
* \brief Create the scheduling data object.
*/
static struct sched_data *sched_data_create(struct ast_sip_contact *contact)
{
struct sched_data *data;
data = ao2_t_alloc(sizeof(*data), sched_data_destructor, contact->uri);
if (!data) {
ast_log(LOG_ERROR, "Unable to create schedule qualify data for contact %s\n",
contact->uri);
return NULL;
}
data->contact = contact;
ao2_ref(data->contact, +1);
return data;
}
/*!
* \internal
* \brief Send a qualify contact request within a threaded task.
*/
static int qualify_contact_task(void *obj)
{
struct ast_sip_contact *contact = obj;
int res;
res = qualify_contact(NULL, contact);
ao2_ref(contact, -1);
return res;
}
/*!
* \internal
* \brief Send a scheduled qualify contact request.
*/
static int qualify_contact_sched(const void *obj)
{
struct sched_data *data = (struct sched_data *) obj;
ao2_ref(data->contact, +1);
if (ast_sip_push_task(NULL, qualify_contact_task, data->contact)) {
ao2_ref(data->contact, -1);
}
/*
* Always reschedule rather than have a potential race cleaning
* up the data object ref between self deletion and an external
* deletion.
*/
return data->contact->qualify_frequency * 1000;
}
/*!
* \internal
* \brief Set up a scheduled qualify contact check.
*/
static void schedule_qualify(struct ast_sip_contact *contact, int initial_interval)
{
struct sched_data *data;
data = sched_data_create(contact);
if (!data) {
return;
}
ast_assert(contact->qualify_frequency != 0);
ao2_t_ref(data, +1, "Ref for qualify_contact_sched() scheduler entry");
data->id = ast_sched_add_variable(sched, initial_interval,
qualify_contact_sched, data, 1);
if (data->id < 0) {
ao2_t_ref(data, -1, "Cleanup failed scheduler add");
ast_log(LOG_ERROR, "Unable to schedule qualify for contact %s\n",
contact->uri);
} else if (!ao2_link(sched_qualifies, data)) {
AST_SCHED_DEL_UNREF(sched, data->id,
ao2_t_ref(data, -1, "Cleanup scheduler for failed ao2_link"));
}
ao2_t_ref(data, -1, "Done setting up scheduler entry");
}
/*!
* \internal
* \brief Remove the contact from the scheduler.
*/
static void unschedule_qualify(struct ast_sip_contact *contact)
{
struct sched_data *data;
data = ao2_find(sched_qualifies, contact, OBJ_UNLINK | OBJ_SEARCH_KEY);
if (!data) {
return;
}
AST_SCHED_DEL_UNREF(sched, data->id,
ao2_t_ref(data, -1, "Delete scheduler entry ref"));
ao2_t_ref(data, -1, "Done with ao2_find ref");
}
/*!
* \internal
* \brief Qualify the given contact and set up scheduling if configured.
*/
static void qualify_and_schedule(struct ast_sip_contact *contact)
{
unschedule_qualify(contact);
if (contact->qualify_frequency) {
ao2_ref(contact, +1);
if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {
ao2_ref(contact, -1);
}
schedule_qualify(contact, contact->qualify_frequency * 1000);
} else {
update_contact_status(contact, UNKNOWN, 0);
}
}
/*!
* \internal
* \brief A new contact has been created make sure it is available.
*/
static void contact_created(const void *obj)
{
qualify_and_schedule((struct ast_sip_contact *) obj);
}
/*!
* \internal
* \brief A contact has been updated.
*/
static void contact_updated(const void *obj)
{
update_contact_status(obj, AVAILABLE, 1);
}
/*!
* \internal
* \brief A contact has been deleted remove status tracking.
*/
static void contact_deleted(const void *obj)
{
struct ast_sip_contact *contact = (struct ast_sip_contact *) obj;
struct ast_sip_contact_status *status;
unschedule_qualify(contact);
status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS,
ast_sorcery_object_get_id(contact));
if (!status) {
return;
}
if (ast_sorcery_delete(ast_sip_get_sorcery(), status)) {
ast_log(LOG_ERROR, "Unable to delete ast_sip_contact_status for contact %s\n",
contact->uri);
}
ao2_ref(status, -1);
}
static const struct ast_sorcery_observer contact_observer = {
.created = contact_created,
.updated = contact_updated,
.deleted = contact_deleted,
};
static pj_bool_t options_start(void)
{
sched = ast_sched_context_create();
if (!sched) {
return -1;
}
if (ast_sched_start_thread(sched)) {
ast_sched_context_destroy(sched);
sched = NULL;
return -1;
}
if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_observer)) {
ast_log(LOG_WARNING, "Unable to add contact observer\n");
ast_sched_context_destroy(sched);
sched = NULL;
return -1;
}
return PJ_SUCCESS;
}
static int sched_qualifies_empty(void *obj, void *arg, int flags)
{
ao2_t_ref(obj, -1, "Release ref held by destroyed scheduler context.");
return CMP_MATCH;
}
static pj_bool_t options_stop(void)
{
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_observer);
if (sched) {
ast_sched_context_destroy(sched);
sched = NULL;
}
/* Empty the container of scheduling data refs. */
ao2_callback(sched_qualifies, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
sched_qualifies_empty, NULL);
return PJ_SUCCESS;
}
static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
{
pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata);
pjsip_tx_data *tdata;
const pjsip_hdr *hdr;
pj_status_t status;
/* Make the response object */
if ((status = ast_sip_create_response(rdata, code, NULL, &tdata) != PJ_SUCCESS)) {
ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
return status;
}
/* Add appropriate headers */
if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) {
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
}
if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) {
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
}
if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) {
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
}
/*
* XXX TODO: pjsip doesn't care a lot about either of these headers -
* while it provides specific methods to create them, they are defined
* to be the standard string header creation. We never did add them
* in chan_sip, although RFC 3261 says they SHOULD. Hard coded here.
*/
ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING);
ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE);
if (dlg && trans) {
status = pjsip_dlg_send_response(dlg, trans, tdata);
} else {
struct ast_sip_endpoint *endpoint;
endpoint = ast_pjsip_rdata_get_endpoint(rdata);
status = ast_sip_send_stateful_response(rdata, tdata, endpoint);
ao2_cleanup(endpoint);
}
if (status != PJ_SUCCESS) {
ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
}
return status;
}
static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
{
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
pjsip_uri *ruri;
pjsip_sip_uri *sip_ruri;
char exten[AST_MAX_EXTENSION];
if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
&pjsip_options_method)) {
return PJ_FALSE;
}
if (!(endpoint = ast_pjsip_rdata_get_endpoint(rdata))) {
return PJ_FALSE;
}
ruri = rdata->msg_info.msg->line.req.uri;
if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) {
send_options_response(rdata, 416);
return PJ_TRUE;
}
sip_ruri = pjsip_uri_get_uri(ruri);
ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten));
if (ast_shutting_down()) {
HTTP: Stop accepting requests on final system shutdown. There are three CLI commands to stop and restart Asterisk each. 1) core stop/restart now - Hangup all calls and stop or restart Asterisk. New channels are prevented while the shutdown request is pending. 2) core stop/restart gracefully - Stop or restart Asterisk when there are no calls remaining in the system. New channels are prevented while the shutdown request is pending. 3) core stop/restart when convenient - Stop or restart Asterisk when there are no calls in the system. New calls are not prevented while the shutdown request is pending. ARI has made stopping/restarting Asterisk more problematic. While a shutdown request is pending it is desirable to continue to process ARI HTTP requests for current calls. To handle the current calls while a shutdown request is pending, a new committed to shutdown phase is needed so ARI applications can deal with the calls until the system is fully committed to shutdown. * Added a new shutdown committed phase so ARI applications can deal with calls until the final committed to shutdown phase is reached. * Made refuse new HTTP requests when the system has reached the final system shutdown phase. Starting anything while the system is actively releasing resources and unloading modules is not a good thing. * Split the bridging framework shutdown to not cleanup the global bridging containers when shutting down in a hurry. This is similar to how other modules prevent crashes on rapid system shutdown. * Moved ast_begin_shutdown(), ast_cancel_shutdown(), and ast_shutting_down(). You should not have to include channel.h just to access these system functions. ASTERISK-24752 #close Reported by: Matthew Jordan Review: https://reviewboard.asterisk.org/r/4399/ ........ Merged revisions 431692 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431694 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2015-02-11 17:39:13 +00:00
/*
* Not taking any new calls at this time.
* Likely a server availability OPTIONS poll.
*/
send_options_response(rdata, 503);
} else if (!ast_strlen_zero(exten) && !ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) {
send_options_response(rdata, 404);
} else {
send_options_response(rdata, 200);
}
return PJ_TRUE;
}
static pjsip_module options_module = {
.name = {"Options Module", 14},
.id = -1,
.priority = PJSIP_MOD_PRIORITY_APPLICATION,
.start = options_start,
.stop = options_stop,
.on_rx_request = options_on_rx_request,
};
/*!
* \internal
* \brief Send qualify request to the given contact.
*/
static int cli_on_contact(void *obj, void *arg, void *data, int flags)
{
struct ast_sip_contact *contact = obj;
struct ast_sip_endpoint *endpoint = data;
int *cli_fd = arg;
ast_cli(*cli_fd, " contact %s\n", contact->uri);
qualify_contact(endpoint, contact);
return 0;
}
/*!
* \brief Data pushed to threadpool to qualify endpoints from the CLI
*/
struct qualify_data {
/*! Endpoint that is being qualified */
struct ast_sip_endpoint *endpoint;
/*! CLI File descriptor for printing messages */
int cli_fd;
};
static struct qualify_data *qualify_data_alloc(struct ast_sip_endpoint *endpoint, int cli_fd)
{
struct qualify_data *qual_data;
qual_data = ast_malloc(sizeof(*qual_data));
if (!qual_data) {
return NULL;
}
qual_data->endpoint = ao2_bump(endpoint);
qual_data->cli_fd = cli_fd;
return qual_data;
}
static void qualify_data_destroy(struct qualify_data *qual_data)
{
ao2_cleanup(qual_data->endpoint);
ast_free(qual_data);
}
/*!
* \internal
* \brief For an endpoint iterate over and qualify all aors/contacts
*/
static int cli_qualify_contacts(void *data)
{
char *aors;
char *aor_name;
RAII_VAR(struct qualify_data *, qual_data, data, qualify_data_destroy);
struct ast_sip_endpoint *endpoint = qual_data->endpoint;
int cli_fd = qual_data->cli_fd;
const char *endpoint_name = ast_sorcery_object_get_id(endpoint);
if (ast_strlen_zero(endpoint->aors)) {
ast_cli(cli_fd, "Endpoint %s has no AoR's configured\n",
endpoint_name);
return 0;
}
aors = ast_strdupa(endpoint->aors);
while ((aor_name = ast_strip(strsep(&aors, ",")))) {
struct ast_sip_aor *aor;
struct ao2_container *contacts;
aor = ast_sip_location_retrieve_aor(aor_name);
if (!aor) {
continue;
}
contacts = ast_sip_location_retrieve_aor_contacts(aor);
if (contacts) {
ast_cli(cli_fd, "Sending qualify to endpoint %s\n", endpoint_name);
ao2_callback_data(contacts, OBJ_NODATA, cli_on_contact, &cli_fd, endpoint);
ao2_ref(contacts, -1);
}
ao2_ref(aor, -1);
}
return 0;
}
static char *cli_qualify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
const char *endpoint_name;
struct qualify_data *qual_data;
switch (cmd) {
case CLI_INIT:
e->command = "pjsip qualify";
e->usage =
"Usage: pjsip qualify <endpoint>\n"
" Send a SIP OPTIONS request to all contacts on the endpoint.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 3) {
return CLI_SHOWUSAGE;
}
endpoint_name = a->argv[2];
if (!(endpoint = ast_sorcery_retrieve_by_id(
ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
ast_cli(a->fd, "Unable to retrieve endpoint %s\n", endpoint_name);
return CLI_FAILURE;
}
qual_data = qualify_data_alloc(endpoint, a->fd);
if (!qual_data) {
return CLI_FAILURE;
}
if (ast_sip_push_task(NULL, cli_qualify_contacts, qual_data)) {
qualify_data_destroy(qual_data);
return CLI_FAILURE;
}
return CLI_SUCCESS;
}
/*!
* \internal
* \brief Send qualify request to the given contact.
*/
static int ami_contact_cb(void *obj, void *arg, int flags)
{
struct ast_sip_contact *contact = obj;
ao2_ref(contact, +1);
if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {
ao2_ref(contact, -1);
}
return 0;
}
static int ami_sip_qualify(struct mansession *s, const struct message *m)
{
const char *endpoint_name = astman_get_header(m, "Endpoint");
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
char *aors;
char *aor_name;
if (ast_strlen_zero(endpoint_name)) {
astman_send_error(s, m, "Endpoint parameter missing.");
return 0;
}
endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
endpoint_name);
if (!endpoint) {
astman_send_error(s, m, "Unable to retrieve endpoint\n");
return 0;
}
/* send a qualify for all contacts registered with the endpoint */
if (ast_strlen_zero(endpoint->aors)) {
astman_send_error(s, m, "No AoRs configured for endpoint\n");
return 0;
}
aors = ast_strdupa(endpoint->aors);
while ((aor_name = ast_strip(strsep(&aors, ",")))) {
struct ast_sip_aor *aor;
struct ao2_container *contacts;
aor = ast_sip_location_retrieve_aor(aor_name);
if (!aor) {
continue;
}
contacts = ast_sip_location_retrieve_aor_contacts(aor);
if (contacts) {
ao2_callback(contacts, OBJ_NODATA, ami_contact_cb, NULL);
ao2_ref(contacts, -1);
}
ao2_ref(aor, -1);
}
astman_send_ack(s, m, "Endpoint found, will qualify");
return 0;
}
static struct ast_cli_entry cli_options[] = {
AST_CLI_DEFINE(cli_qualify, "Send an OPTIONS request to a PJSIP endpoint")
};
static int sched_qualifies_hash_fn(const void *obj, int flags)
{
const struct sched_data *object;
const struct ast_sip_contact *key;
switch (flags & OBJ_SEARCH_MASK) {
case OBJ_SEARCH_KEY:
key = obj;
break;
case OBJ_SEARCH_OBJECT:
object = obj;
key = object->contact;
break;
default:
/* Hash can only work on something with a full key. */
ast_assert(0);
return 0;
}
return ast_str_hash(ast_sorcery_object_get_id(key));
}
static int sched_qualifies_cmp_fn(void *obj, void *arg, int flags)
{
const struct sched_data *object_left = obj;
const struct sched_data *object_right = arg;
struct ast_sip_contact *right_key = arg;
int cmp;
switch (flags & OBJ_SEARCH_MASK) {
case OBJ_SEARCH_OBJECT:
right_key = object_right->contact;
/* Fall through */
case OBJ_SEARCH_KEY:
cmp = strcmp(ast_sorcery_object_get_id(object_left->contact),
ast_sorcery_object_get_id(right_key));
break;
case OBJ_SEARCH_PARTIAL_KEY:
/* Not supported by container. */
ast_assert(0);
return 0;
default:
/*
* What arg points to is specific to this traversal callback
* and has no special meaning to astobj2.
*/
cmp = 0;
break;
}
if (cmp) {
return 0;
}
/*
* At this point the traversal callback is identical to a sorted
* container.
*/
return CMP_MATCH;
}
static int rtt_start_handler(const struct aco_option *opt,
struct ast_variable *var, void *obj)
{
struct ast_sip_contact_status *status = obj;
long int sec, usec;
if (sscanf(var->value, "%ld.%06ld", &sec, &usec) != 2) {
return -1;
}
status->rtt_start = ast_tv(sec, usec);
return 0;
}
static int rtt_start_to_str(const void *obj, const intptr_t *args, char **buf)
{
const struct ast_sip_contact_status *status = obj;
if (ast_asprintf(buf, "%ld.%06ld", (long)status->rtt_start.tv_sec, (long)status->rtt_start.tv_usec) == -1) {
return -1;
}
return 0;
}
static char status_value_unknown[2];
static char status_value_created[2];
int ast_sip_initialize_sorcery_qualify(void)
{
struct ast_sorcery *sorcery = ast_sip_get_sorcery();
/* initialize sorcery ast_sip_contact_status resource */
ast_sorcery_apply_default(sorcery, CONTACT_STATUS, "memory", NULL);
ast_sorcery_object_set_congestion_levels(sorcery, CONTACT_STATUS, -1,
3 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);
if (ast_sorcery_internal_object_register(sorcery, CONTACT_STATUS,
contact_status_alloc, NULL, NULL)) {
ast_log(LOG_ERROR, "Unable to register ast_sip_contact_status in sorcery\n");
return -1;
}
snprintf(status_value_unknown, sizeof(status_value_unknown), "%u", UNKNOWN);
ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "last_status",
status_value_unknown, OPT_UINT_T, 0, FLDSET(struct ast_sip_contact_status, last_status));
snprintf(status_value_created, sizeof(status_value_created), "%u", CREATED);
ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "status",
status_value_created, OPT_UINT_T, 0, FLDSET(struct ast_sip_contact_status, status));
ast_sorcery_object_field_register_custom_nodoc(sorcery, CONTACT_STATUS, "rtt_start",
"0.0", rtt_start_handler, rtt_start_to_str, NULL, 0, 0);
ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt",
"0", OPT_UINT_T, 0, FLDSET(struct ast_sip_contact_status, rtt));
return 0;
}
static void qualify_and_schedule_contact(struct ast_sip_contact *contact)
{
int initial_interval;
int max_time = ast_sip_get_max_initial_qualify_time();
/* Delay initial qualification by a random fraction of the specified interval */
if (max_time && max_time < contact->qualify_frequency) {
initial_interval = max_time;
} else {
initial_interval = contact->qualify_frequency;
}
initial_interval = (int)((initial_interval * 1000) * ast_random_double());
unschedule_qualify(contact);
if (contact->qualify_frequency) {
schedule_qualify(contact, initial_interval);
} else {
update_contact_status(contact, UNKNOWN, 0);
}
}
static int qualify_and_schedule_cb_with_aor(void *obj, void *arg, int flags)
{
struct ast_sip_contact *contact = obj;
struct ast_sip_aor *aor = arg;
contact->qualify_frequency = aor->qualify_frequency;
contact->qualify_timeout = aor->qualify_timeout;
contact->authenticate_qualify = aor->authenticate_qualify;
qualify_and_schedule_contact(contact);
return 0;
}
static int qualify_and_schedule_cb_without_aor(void *obj, void *arg, int flags)
{
qualify_and_schedule_contact((struct ast_sip_contact *) obj);
return 0;
}
/*!
* \internal
* \brief Qualify and schedule an aor's contacts
*
* \details For the given aor check if it has permanent contacts,
* qualify all contacts and schedule for checks if configured.
*/
static int qualify_and_schedule_all_cb(void *obj, void *arg, int flags)
{
sorcery/res_pjsip: Refactor for realtime performance There were a number of places in the res_pjsip stack that were getting all endpoints or all aors, and then filtering them locally. A good example is pjsip_options which, on startup, retrieves all endpoints, then the aors for those endpoints, then tests the aors to see if the qualify_frequency is > 0. One issue was that it never did anything with the endpoints other than retrieve the aors so we probably could have skipped a step and just retrieved all aors. But nevermind. This worked reasonably well with local config files but with a realtime backend and thousands of objects, this was a nightmare. The issue really boiled down to the fact that while realtime supports predicates that are passed to the database engine, the non-realtime sorcery backends didn't. They do now. The realtime engines have a scheme for doing simple comparisons. They take in an ast_variable (or list) for matching, and the name of each variable can contain an operator. For instance, a name of "qualify_frequency >" and a value of "0" would create a SQL predicate that looks like "where qualify_frequency > '0'". If there's no operator after the name, the engines add an '=' so a simple name of "qualify_frequency" and a value of "10" would return exact matches. The non-realtime backends decide whether to include an object in a result set by calling ast_sorcery_changeset_create on every object in the internal container. However, ast_sorcery_changeset_create only does exact string matches though so a name of "qualify_frequency >" and a value of "0" returns nothing because the literal "qualify_frequency >" doesn't match any name in the objset set. So, the real task was to create a generic string matcher that can take a left value, operator and a right value and perform the match. To that end, strings.c has a new ast_strings_match(left, operator, right) function. Left and right are the strings to operate on and the operator can be a string containing any of the following: = (or NULL or ""), !=, >, >=, <, <=, like or regex. If the operator is like or regex, the right string should be a %-pattern or a regex expression. If both left and right can be converted to float, then a numeric comparison is performed, otherwise a string comparison is performed. To use this new function on ast_variables, 2 new functions were added to config.c. One that compares 2 ast_variables, and one that compares 2 ast_variable lists. The former is useful when you want to compare 2 ast_variables that happen to be in a list but don't want to traverse the list. The latter will traverse the right list and return true if all the variables in it match the left list. Now, the backends' fields_cmp functions call ast_variable_lists_match instead of ast_sorcery_changeset_create and they can now process the same syntax as the realtime engines. The realtime backend just passes the variable list unaltered to the engine. The only gotcha is that there's no common realtime engine support for regex so that's been noted in the api docs for ast_sorcery_retrieve_by_fields. Only one more change to sorcery was done... A new config flag "allow_unqualified_fetch" was added to reg_sorcery_realtime. "no": ignore fetches if no predicate fields were supplied. "error": same as no but emit an error. (good for testing) "yes": allow (the default); "warn": allow but emit a warning. (good for testing) Now on to res_pjsip... pjsip_options was modified to retrieve aors with qualify_frequency > 0 rather than all endpoints then all aors. Not only was this a big improvement in realtime retrieval but even for config files there's an improvement because we're not going through endpoints anymore. res_pjsip_mwi was modified to retieve only endpoints with something in the mailboxes field instead of all endpoints then testing mailboxes. res_pjsip_registrar_expire was completely refactored. It was retrieving all contacts then setting up scheduler entries to check for expiration. Now, it's a single thread (like keepalive) that periodically retrieves only contacts whose expiration time is < now and deletes them. A new contact_expiration_check_interval was added to global with a default of 30 seconds. Ross Beer reports that with this patch, his Asterisk startup time dropped from around an hour to under 30 seconds. There are still objects that can't be filtered at the database like identifies, transports, and registrations. These are not going to be anywhere near as numerous as endpoints, aors, auths, contacts however. Back to allow_unqualified_fetch. If this is set to yes and you have a very large number of objects in the database, the pjsip CLI commands will attempt to retrive ALL of them if not qualified with a LIKE. Worse, if you type "pjsip show endpoint <tab>" guess what's going to happen? :) Having a cache helps but all the objects will have to be retrieved at least once to fill the cache. Setting allow_unqualified_fetch=no prevents the mass retrieve and should be used on endpoints, auths, aors, and contacts. It should NOT be used for identifies, registrations and transports since these MUST be retrieved in bulk. Example sorcery.conf: [res_pjsip] endpoint=config,pjsip.conf,criteria=type=endpoint endpoint=realtime,ps_endpoints,allow_unqualified_fetch=error ASTERISK-25826 #close Reported-by: Ross Beer Tested-by: Ross Beer Change-Id: Id2691e447db90892890036e663aaf907b2dc1c67
2016-03-08 21:55:30 +00:00
struct ast_sip_aor *aor = obj;
struct ao2_container *contacts;
if (aor->permanent_contacts) {
contacts = ast_sip_location_retrieve_aor_contacts(aor);
if (contacts) {
ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_with_aor, aor);
ao2_ref(contacts, -1);
}
}
return 0;
}
/*!
* \internal
* \brief Unschedule all existing contacts
*/
static int unschedule_all_cb(void *obj, void *arg, int flags)
{
struct sched_data *data = obj;
AST_SCHED_DEL_UNREF(sched, data->id, ao2_ref(data, -1));
return CMP_MATCH;
}
static void qualify_and_schedule_all(void)
{
sorcery/res_pjsip: Refactor for realtime performance There were a number of places in the res_pjsip stack that were getting all endpoints or all aors, and then filtering them locally. A good example is pjsip_options which, on startup, retrieves all endpoints, then the aors for those endpoints, then tests the aors to see if the qualify_frequency is > 0. One issue was that it never did anything with the endpoints other than retrieve the aors so we probably could have skipped a step and just retrieved all aors. But nevermind. This worked reasonably well with local config files but with a realtime backend and thousands of objects, this was a nightmare. The issue really boiled down to the fact that while realtime supports predicates that are passed to the database engine, the non-realtime sorcery backends didn't. They do now. The realtime engines have a scheme for doing simple comparisons. They take in an ast_variable (or list) for matching, and the name of each variable can contain an operator. For instance, a name of "qualify_frequency >" and a value of "0" would create a SQL predicate that looks like "where qualify_frequency > '0'". If there's no operator after the name, the engines add an '=' so a simple name of "qualify_frequency" and a value of "10" would return exact matches. The non-realtime backends decide whether to include an object in a result set by calling ast_sorcery_changeset_create on every object in the internal container. However, ast_sorcery_changeset_create only does exact string matches though so a name of "qualify_frequency >" and a value of "0" returns nothing because the literal "qualify_frequency >" doesn't match any name in the objset set. So, the real task was to create a generic string matcher that can take a left value, operator and a right value and perform the match. To that end, strings.c has a new ast_strings_match(left, operator, right) function. Left and right are the strings to operate on and the operator can be a string containing any of the following: = (or NULL or ""), !=, >, >=, <, <=, like or regex. If the operator is like or regex, the right string should be a %-pattern or a regex expression. If both left and right can be converted to float, then a numeric comparison is performed, otherwise a string comparison is performed. To use this new function on ast_variables, 2 new functions were added to config.c. One that compares 2 ast_variables, and one that compares 2 ast_variable lists. The former is useful when you want to compare 2 ast_variables that happen to be in a list but don't want to traverse the list. The latter will traverse the right list and return true if all the variables in it match the left list. Now, the backends' fields_cmp functions call ast_variable_lists_match instead of ast_sorcery_changeset_create and they can now process the same syntax as the realtime engines. The realtime backend just passes the variable list unaltered to the engine. The only gotcha is that there's no common realtime engine support for regex so that's been noted in the api docs for ast_sorcery_retrieve_by_fields. Only one more change to sorcery was done... A new config flag "allow_unqualified_fetch" was added to reg_sorcery_realtime. "no": ignore fetches if no predicate fields were supplied. "error": same as no but emit an error. (good for testing) "yes": allow (the default); "warn": allow but emit a warning. (good for testing) Now on to res_pjsip... pjsip_options was modified to retrieve aors with qualify_frequency > 0 rather than all endpoints then all aors. Not only was this a big improvement in realtime retrieval but even for config files there's an improvement because we're not going through endpoints anymore. res_pjsip_mwi was modified to retieve only endpoints with something in the mailboxes field instead of all endpoints then testing mailboxes. res_pjsip_registrar_expire was completely refactored. It was retrieving all contacts then setting up scheduler entries to check for expiration. Now, it's a single thread (like keepalive) that periodically retrieves only contacts whose expiration time is < now and deletes them. A new contact_expiration_check_interval was added to global with a default of 30 seconds. Ross Beer reports that with this patch, his Asterisk startup time dropped from around an hour to under 30 seconds. There are still objects that can't be filtered at the database like identifies, transports, and registrations. These are not going to be anywhere near as numerous as endpoints, aors, auths, contacts however. Back to allow_unqualified_fetch. If this is set to yes and you have a very large number of objects in the database, the pjsip CLI commands will attempt to retrive ALL of them if not qualified with a LIKE. Worse, if you type "pjsip show endpoint <tab>" guess what's going to happen? :) Having a cache helps but all the objects will have to be retrieved at least once to fill the cache. Setting allow_unqualified_fetch=no prevents the mass retrieve and should be used on endpoints, auths, aors, and contacts. It should NOT be used for identifies, registrations and transports since these MUST be retrieved in bulk. Example sorcery.conf: [res_pjsip] endpoint=config,pjsip.conf,criteria=type=endpoint endpoint=realtime,ps_endpoints,allow_unqualified_fetch=error ASTERISK-25826 #close Reported-by: Ross Beer Tested-by: Ross Beer Change-Id: Id2691e447db90892890036e663aaf907b2dc1c67
2016-03-08 21:55:30 +00:00
struct ast_variable *var = ast_variable_new("qualify_frequency >", "0", "");
struct ao2_container *aors;
struct ao2_container *contacts;
sorcery/res_pjsip: Refactor for realtime performance There were a number of places in the res_pjsip stack that were getting all endpoints or all aors, and then filtering them locally. A good example is pjsip_options which, on startup, retrieves all endpoints, then the aors for those endpoints, then tests the aors to see if the qualify_frequency is > 0. One issue was that it never did anything with the endpoints other than retrieve the aors so we probably could have skipped a step and just retrieved all aors. But nevermind. This worked reasonably well with local config files but with a realtime backend and thousands of objects, this was a nightmare. The issue really boiled down to the fact that while realtime supports predicates that are passed to the database engine, the non-realtime sorcery backends didn't. They do now. The realtime engines have a scheme for doing simple comparisons. They take in an ast_variable (or list) for matching, and the name of each variable can contain an operator. For instance, a name of "qualify_frequency >" and a value of "0" would create a SQL predicate that looks like "where qualify_frequency > '0'". If there's no operator after the name, the engines add an '=' so a simple name of "qualify_frequency" and a value of "10" would return exact matches. The non-realtime backends decide whether to include an object in a result set by calling ast_sorcery_changeset_create on every object in the internal container. However, ast_sorcery_changeset_create only does exact string matches though so a name of "qualify_frequency >" and a value of "0" returns nothing because the literal "qualify_frequency >" doesn't match any name in the objset set. So, the real task was to create a generic string matcher that can take a left value, operator and a right value and perform the match. To that end, strings.c has a new ast_strings_match(left, operator, right) function. Left and right are the strings to operate on and the operator can be a string containing any of the following: = (or NULL or ""), !=, >, >=, <, <=, like or regex. If the operator is like or regex, the right string should be a %-pattern or a regex expression. If both left and right can be converted to float, then a numeric comparison is performed, otherwise a string comparison is performed. To use this new function on ast_variables, 2 new functions were added to config.c. One that compares 2 ast_variables, and one that compares 2 ast_variable lists. The former is useful when you want to compare 2 ast_variables that happen to be in a list but don't want to traverse the list. The latter will traverse the right list and return true if all the variables in it match the left list. Now, the backends' fields_cmp functions call ast_variable_lists_match instead of ast_sorcery_changeset_create and they can now process the same syntax as the realtime engines. The realtime backend just passes the variable list unaltered to the engine. The only gotcha is that there's no common realtime engine support for regex so that's been noted in the api docs for ast_sorcery_retrieve_by_fields. Only one more change to sorcery was done... A new config flag "allow_unqualified_fetch" was added to reg_sorcery_realtime. "no": ignore fetches if no predicate fields were supplied. "error": same as no but emit an error. (good for testing) "yes": allow (the default); "warn": allow but emit a warning. (good for testing) Now on to res_pjsip... pjsip_options was modified to retrieve aors with qualify_frequency > 0 rather than all endpoints then all aors. Not only was this a big improvement in realtime retrieval but even for config files there's an improvement because we're not going through endpoints anymore. res_pjsip_mwi was modified to retieve only endpoints with something in the mailboxes field instead of all endpoints then testing mailboxes. res_pjsip_registrar_expire was completely refactored. It was retrieving all contacts then setting up scheduler entries to check for expiration. Now, it's a single thread (like keepalive) that periodically retrieves only contacts whose expiration time is < now and deletes them. A new contact_expiration_check_interval was added to global with a default of 30 seconds. Ross Beer reports that with this patch, his Asterisk startup time dropped from around an hour to under 30 seconds. There are still objects that can't be filtered at the database like identifies, transports, and registrations. These are not going to be anywhere near as numerous as endpoints, aors, auths, contacts however. Back to allow_unqualified_fetch. If this is set to yes and you have a very large number of objects in the database, the pjsip CLI commands will attempt to retrive ALL of them if not qualified with a LIKE. Worse, if you type "pjsip show endpoint <tab>" guess what's going to happen? :) Having a cache helps but all the objects will have to be retrieved at least once to fill the cache. Setting allow_unqualified_fetch=no prevents the mass retrieve and should be used on endpoints, auths, aors, and contacts. It should NOT be used for identifies, registrations and transports since these MUST be retrieved in bulk. Example sorcery.conf: [res_pjsip] endpoint=config,pjsip.conf,criteria=type=endpoint endpoint=realtime,ps_endpoints,allow_unqualified_fetch=error ASTERISK-25826 #close Reported-by: Ross Beer Tested-by: Ross Beer Change-Id: Id2691e447db90892890036e663aaf907b2dc1c67
2016-03-08 21:55:30 +00:00
if (!var) {
return;
}
aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
"aor", AST_RETRIEVE_FLAG_MULTIPLE, var);
ao2_callback(sched_qualifies, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, unschedule_all_cb, NULL);
if (aors) {
ao2_callback(aors, OBJ_NODATA, qualify_and_schedule_all_cb, NULL);
ao2_ref(aors, -1);
}
contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
"contact", AST_RETRIEVE_FLAG_MULTIPLE, var);
if (contacts) {
ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_without_aor, NULL);
ao2_ref(contacts, -1);
}
ast_variables_destroy(var);
}
static int format_contact_status(void *obj, void *arg, int flags)
{
struct ast_sip_contact_wrapper *wrapper = obj;
struct ast_sip_contact *contact = wrapper->contact;
struct ast_sip_ami *ami = arg;
struct ast_sip_contact_status *status;
struct ast_str *buf;
const struct ast_sip_endpoint *endpoint = ami->arg;
buf = ast_sip_create_ami_event("ContactStatusDetail", ami);
if (!buf) {
return -1;
}
status = ast_sorcery_retrieve_by_id(
ast_sip_get_sorcery(), CONTACT_STATUS,
ast_sorcery_object_get_id(contact));
ast_str_append(&buf, 0, "AOR: %s\r\n", wrapper->aor_id);
ast_str_append(&buf, 0, "URI: %s\r\n", contact->uri);
ast_str_append(&buf, 0, "UserAgent: %s\r\n", contact->user_agent);
ast_str_append(&buf, 0, "RegExpire: %ld\r\n", contact->expiration_time.tv_sec);
if (!ast_strlen_zero(contact->via_addr)) {
ast_str_append(&buf, 0, "ViaAddress: %s", contact->via_addr);
if (contact->via_port) {
ast_str_append(&buf, 0, ":%d", contact->via_port);
}
ast_str_append(&buf, 0, "\r\n");
}
if (!ast_strlen_zero(contact->call_id)) {
ast_str_append(&buf, 0, "CallID: %s\r\n", contact->call_id);
}
ast_str_append(&buf, 0, "Status: %s\r\n", ast_sip_get_contact_status_label(status->status));
if (status->status == UNKNOWN) {
ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n");
} else {
ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt);
}
ast_str_append(&buf, 0, "EndpointName: %s\r\n",
ast_sorcery_object_get_id(endpoint));
astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
ami->count++;
ast_free(buf);
ao2_cleanup(status);
return 0;
}
static int format_contact_status_for_aor(void *obj, void *arg, int flags)
{
struct ast_sip_aor *aor = obj;
return ast_sip_for_each_contact(aor, format_contact_status, arg);
}
static int format_ami_contact_status(const struct ast_sip_endpoint *endpoint,
struct ast_sip_ami *ami)
{
ami->arg = (void *)endpoint;
return ast_sip_for_each_aor(endpoint->aors, format_contact_status_for_aor, ami);
}
static struct ast_sip_endpoint_formatter contact_status_formatter = {
.format_ami = format_ami_contact_status
};
static void aor_observer_modified(const void *obj)
{
struct ast_sip_aor *aor = (void *)obj;
struct ao2_container *contacts;
contacts = ast_sip_location_retrieve_aor_contacts(aor);
if (contacts) {
ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_with_aor, aor);
ao2_ref(contacts, -1);
}
}
static int unschedule_contact_cb(void *obj, void *arg, int flags)
{
unschedule_qualify(obj);
return CMP_MATCH;
}
static void aor_observer_deleted(const void *obj)
{
const struct ast_sip_aor *aor = obj;
struct ao2_container *contacts;
contacts = ast_sip_location_retrieve_aor_contacts(aor);
if (contacts) {
ao2_callback(contacts, OBJ_NODATA, unschedule_contact_cb, NULL);
ao2_ref(contacts, -1);
}
}
static const struct ast_sorcery_observer observer_callbacks_options = {
.created = aor_observer_modified,
.updated = aor_observer_modified,
.deleted = aor_observer_deleted
};
static int aor_update_endpoint_state(void *obj, void *arg, int flags)
{
struct ast_sip_endpoint *endpoint = obj;
const char *endpoint_name = ast_sorcery_object_get_id(endpoint);
char *aor = arg;
char *endpoint_aor;
char *endpoint_aors;
if (ast_strlen_zero(aor) || ast_strlen_zero(endpoint->aors)) {
return 0;
}
endpoint_aors = ast_strdupa(endpoint->aors);
while ((endpoint_aor = ast_strip(strsep(&endpoint_aors, ",")))) {
if (!strcmp(aor, endpoint_aor)) {
if (ast_sip_persistent_endpoint_update_state(endpoint_name, AST_ENDPOINT_ONLINE) == -1) {
ast_log(LOG_WARNING, "Unable to find persistent endpoint '%s' for aor '%s'\n",
endpoint_name, aor);
}
}
}
return 0;
}
static int on_aor_update_endpoint_state(void *obj, void *arg, int flags)
{
struct ast_sip_aor *aor = obj;
struct ao2_container *endpoints;
RAII_VAR(struct ast_variable *, var, NULL, ast_variables_destroy);
const char *aor_name = ast_sorcery_object_get_id(aor);
char *aor_like;
if (ast_strlen_zero(aor_name)) {
return -1;
}
if (aor->permanent_contacts && ((int)(aor->qualify_frequency * 1000)) <= 0) {
aor_like = ast_alloca(strlen(aor_name) + 3);
sprintf(aor_like, "%%%s%%", aor_name);
var = ast_variable_new("aors LIKE", aor_like, "");
if (!var) {
return -1;
}
endpoints = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
"endpoint", AST_RETRIEVE_FLAG_MULTIPLE, var);
if (endpoints) {
/*
* Because aors are a string list, we have to use a pattern match but since a simple
* pattern match could return an endpoint that has an aor of "aaabccc" when searching
* for "abc", we still have to iterate over them to find an exact aor match.
*/
ao2_callback(endpoints, 0, aor_update_endpoint_state, (char *)aor_name);
ao2_ref(endpoints, -1);
}
}
return 0;
}
static int contact_update_endpoint_state(void *obj, void *arg, int flags)
{
const struct ast_sip_contact *contact = obj;
struct timeval tv = ast_tvnow();
if (!ast_strlen_zero(contact->endpoint_name) && ((int)(contact->qualify_frequency * 1000)) <= 0 &&
contact->expiration_time.tv_sec > tv.tv_sec) {
if (ast_sip_persistent_endpoint_update_state(contact->endpoint_name, AST_ENDPOINT_ONLINE) == -1) {
ast_log(LOG_WARNING, "Unable to find persistent endpoint '%s' for contact '%s/%s'\n",
contact->endpoint_name, contact->aor, contact->uri);
return -1;
}
}
return 0;
}
static void update_all_unqualified_endpoints(void)
{
struct ao2_container *aors;
struct ao2_container *contacts;
RAII_VAR(struct ast_variable *, var_aor, NULL, ast_variables_destroy);
RAII_VAR(struct ast_variable *, var_contact, NULL, ast_variables_destroy);
RAII_VAR(char *, time_now, NULL, ast_free);
struct timeval tv = ast_tvnow();
if (!(var_aor = ast_variable_new("contact !=", "", ""))) {
return;
}
if (!(var_aor->next = ast_variable_new("qualify_frequency <=", "0", ""))) {
return;
}
if (ast_asprintf(&time_now, "%ld", tv.tv_sec) == -1) {
return;
}
if (!(var_contact = ast_variable_new("expiration_time >", time_now, ""))) {
return;
}
if (!(var_contact->next = ast_variable_new("qualify_frequency <=", "0", ""))) {
return;
}
aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
"aor", AST_RETRIEVE_FLAG_MULTIPLE, var_aor);
if (aors) {
ao2_callback(aors, OBJ_NODATA, on_aor_update_endpoint_state, NULL);
ao2_ref(aors, -1);
}
contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
"contact", AST_RETRIEVE_FLAG_MULTIPLE, var_contact);
if (contacts) {
ao2_callback(contacts, OBJ_NODATA, contact_update_endpoint_state, NULL);
ao2_ref(contacts, -1);
}
}
int ast_res_pjsip_init_options_handling(int reload)
{
static const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
if (reload) {
qualify_and_schedule_all();
return 0;
}
sched_qualifies = ao2_t_container_alloc(QUALIFIED_BUCKETS,
sched_qualifies_hash_fn, sched_qualifies_cmp_fn,
"Create container for scheduled qualifies");
if (!sched_qualifies) {
return -1;
}
if (pjsip_endpt_register_module(ast_sip_get_pjsip_endpoint(), &options_module) != PJ_SUCCESS) {
ao2_cleanup(sched_qualifies);
sched_qualifies = NULL;
return -1;
}
if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW,
NULL, 1, &STR_OPTIONS) != PJ_SUCCESS) {
pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
ao2_cleanup(sched_qualifies);
sched_qualifies = NULL;
return -1;
}
if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "aor", &observer_callbacks_options)) {
pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
ao2_cleanup(sched_qualifies);
sched_qualifies = NULL;
return -1;
}
res_pjsip: make it unloadable (take 2) Due to the original patch causing memory corruptions it was removed until the problem could be resolved. This patch is the original patch plus some added locking around stasis router subcription that was needed to avoid the memory corruption. Description of the original problem and patch (still applicable): The res_pjsip module was previously unloadable. With this patch it can now be unloaded. This patch is based off the original patch on the issue (listed below) by Corey Farrell with a few modifications. Namely, removed a few changes not required to make the module unloadable and also fixed a bug that would cause asterisk to crash on unloading. This patch is the first step (should hopefully be followed by another/others at some point) in allowing res_pjsip and the modules that depend on it to be unloadable. At this time, res_pjsip and some of the modules that depend on res_pjsip cannot be unloaded without causing problems of some sort. The goal of this patch is to get res_pjsip and only res_pjsip to be able to unload successfully and/or shutdown without incident (crashes, leaks, etc...). Other dependent modules may still cause problems on unload. Basically made sure, with the patch applied, that res_pjsip (with no other dependent modules loaded) could be succesfully unloaded and Asterisk could shutdown without any leaks or crashes that pertained directly to res_pjsip. ASTERISK-24485 #close Reported by: Corey Farrell Review: https://reviewboard.asterisk.org/r/4363/ patches: pjsip_unload-broken-r1.patch submitted by Corey Farrell (license 5909) ........ Merged revisions 431179 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431180 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2015-01-27 19:12:56 +00:00
internal_sip_register_endpoint_formatter(&contact_status_formatter);
ast_manager_register_xml("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify);
ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
update_all_unqualified_endpoints();
qualify_and_schedule_all();
return 0;
}
void ast_res_pjsip_cleanup_options_handling(void)
{
ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
ast_manager_unregister("PJSIPQualify");
res_pjsip: make it unloadable (take 2) Due to the original patch causing memory corruptions it was removed until the problem could be resolved. This patch is the original patch plus some added locking around stasis router subcription that was needed to avoid the memory corruption. Description of the original problem and patch (still applicable): The res_pjsip module was previously unloadable. With this patch it can now be unloaded. This patch is based off the original patch on the issue (listed below) by Corey Farrell with a few modifications. Namely, removed a few changes not required to make the module unloadable and also fixed a bug that would cause asterisk to crash on unloading. This patch is the first step (should hopefully be followed by another/others at some point) in allowing res_pjsip and the modules that depend on it to be unloadable. At this time, res_pjsip and some of the modules that depend on res_pjsip cannot be unloaded without causing problems of some sort. The goal of this patch is to get res_pjsip and only res_pjsip to be able to unload successfully and/or shutdown without incident (crashes, leaks, etc...). Other dependent modules may still cause problems on unload. Basically made sure, with the patch applied, that res_pjsip (with no other dependent modules loaded) could be succesfully unloaded and Asterisk could shutdown without any leaks or crashes that pertained directly to res_pjsip. ASTERISK-24485 #close Reported by: Corey Farrell Review: https://reviewboard.asterisk.org/r/4363/ patches: pjsip_unload-broken-r1.patch submitted by Corey Farrell (license 5909) ........ Merged revisions 431179 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431180 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2015-01-27 19:12:56 +00:00
internal_sip_unregister_endpoint_formatter(&contact_status_formatter);
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &observer_callbacks_options);
pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
ao2_cleanup(sched_qualifies);
sched_qualifies = NULL;
}