Merge "res_pjsip_registrar: Improve performance on inbound handling."

This commit is contained in:
Joshua Colp 2018-08-08 12:08:49 -05:00 committed by Gerrit Code Review
commit b0ac1ecc29

View file

@ -122,25 +122,28 @@ static int registrar_find_contact(void *obj, void *arg, int flags)
{
struct ast_sip_contact *contact = obj;
const struct registrar_contact_details *details = arg;
pjsip_uri *contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0);
pjsip_uri *contact_uri;
if (ast_tvzero(contact->expiration_time)) {
return 0;
}
contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0);
return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH : 0;
}
/*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */
static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_container *contacts, struct ast_sip_aor *aor, int *added, int *updated, int *deleted)
static int registrar_validate_contacts(const pjsip_rx_data *rdata, pj_pool_t *pool, struct ao2_container *contacts,
struct ast_sip_aor *aor, int permanent, int *added, int *updated, int *deleted)
{
pjsip_contact_hdr *previous = NULL;
pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
struct registrar_contact_details details = {
.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256),
.pool = pool,
};
if (!details.pool) {
return -1;
}
while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) {
for (; (contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next)); pj_pool_reset(pool)) {
int expiration = registrar_get_expiration(aor, contact, rdata);
struct ast_sip_contact *existing;
char contact_uri[pjsip_max_url_size];
@ -148,16 +151,14 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_co
if (contact->star) {
/* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */
if (expiration != 0 || previous) {
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
return -1;
}
/* Count all contacts to delete */
*deleted = ao2_container_count(contacts);
*deleted = ao2_container_count(contacts) - permanent;
previous = contact;
continue;
} else if (previous && previous->star) {
/* If there is a previous contact and it is a '*' this is a deal breaker */
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
return -1;
}
previous = contact;
@ -171,13 +172,11 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_co
/* pjsip_uri_print returns -1 if there's not enough room in the buffer */
if (pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)) < 0) {
/* If the total length of the uri is greater than pjproject can handle, go no further */
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
return -1;
}
if (details.uri->host.slen >= pj_max_hostname) {
/* If the length of the hostname is greater than pjproject can handle, go no further */
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
return -1;
}
@ -195,25 +194,20 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_co
}
}
/* The provided contacts are acceptable, huzzah! */
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
return 0;
}
/*! \brief Callback function which prunes static contacts */
static int registrar_prune_static(void *obj, void *arg, int flags)
{
struct ast_sip_contact *contact = obj;
return ast_tvzero(contact->expiration_time) ? CMP_MATCH : 0;
}
/*! \brief Internal function used to delete a contact from an AOR */
static int registrar_delete_contact(void *obj, void *arg, int flags)
{
struct ast_sip_contact *contact = obj;
const char *aor_name = arg;
/* Permanent contacts can't be deleted */
if (ast_tvzero(contact->expiration_time)) {
return 0;
}
ast_sip_location_delete_contact(contact);
if (!ast_strlen_zero(aor_name)) {
ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact->uri, aor_name);
@ -270,7 +264,7 @@ static int build_path_data(pjsip_rx_data *rdata, struct ast_str **path_str)
}
*path_str = ast_str_create(64);
if (!path_str) {
if (!*path_str) {
return -1;
}
@ -442,7 +436,8 @@ static int vec_contact_add(void *obj, void *arg, int flags)
*
* \return Nothing
*/
static void remove_excess_contacts(struct ao2_container *contacts, unsigned int to_remove)
static void remove_excess_contacts(struct ao2_container *contacts, struct ao2_container *response_contacts,
unsigned int to_remove)
{
struct excess_contact_vector contact_vec;
@ -482,11 +477,28 @@ static void remove_excess_contacts(struct ao2_container *contacts, unsigned int
contact->uri,
contact->aor,
contact->user_agent);
ao2_unlink(response_contacts, contact);
}
AST_VECTOR_FREE(&contact_vec);
}
/*! \brief Callback function which adds non-permanent contacts to a container */
static int registrar_add_non_permanent(void *obj, void *arg, int flags)
{
struct ast_sip_contact *contact = obj;
struct ao2_container *container = arg;
if (ast_tvzero(contact->expiration_time)) {
return 0;
}
ao2_link(container, contact);
return 0;
}
struct aor_core_response {
/*! Tx data to use for statefull response. NULL for stateless response. */
pjsip_tx_data *tdata;
@ -506,8 +518,10 @@ static void register_aor_core(pjsip_rx_data *rdata,
int added = 0;
int updated = 0;
int deleted = 0;
int permanent = 0;
int contact_count;
pjsip_contact_hdr *contact_hdr = NULL;
struct ao2_container *existing_contacts = NULL;
pjsip_contact_hdr *contact_hdr = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
struct registrar_contact_details details = { 0, };
pjsip_tx_data *tdata;
RAII_VAR(struct ast_str *, path_str, NULL, ast_free);
@ -523,15 +537,28 @@ static void register_aor_core(pjsip_rx_data *rdata,
char *call_id = NULL;
size_t alloc_size;
/* So we don't count static contacts against max_contacts we prune them out from the container */
ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL);
/* We create a single pool and use it throughout this function where we need one */
details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
"Contact Comparison", 1024, 256);
if (!details.pool) {
response->code = 500;
return;
}
if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) {
/* If there are any permanent contacts configured on the AOR we need to take them
* into account when counting contacts.
*/
if (aor->permanent_contacts) {
permanent = ao2_container_count(aor->permanent_contacts);
}
if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted)) {
/* The provided Contact headers do not conform to the specification */
ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
ast_sorcery_object_get_id(endpoint));
response->code = 400;
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
return;
}
@ -540,15 +567,29 @@ static void register_aor_core(pjsip_rx_data *rdata,
ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n",
ast_sorcery_object_get_id(endpoint));
response->code = 420;
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
return;
}
if (aor->remove_existing) {
/* Cumulative number of contacts affected by this registration */
contact_count = MAX(updated + added - deleted, 0);
/* We need to keep track of only existing contacts so we can later
* remove them if need be.
*/
existing_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
NULL, ast_sorcery_object_id_compare);
if (!existing_contacts) {
response->code = 500;
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
return;
}
ao2_callback(contacts, OBJ_NODATA, registrar_add_non_permanent, existing_contacts);
} else {
/* Total contacts after this registration */
contact_count = ao2_container_count(contacts) + added - deleted;
contact_count = ao2_container_count(contacts) - permanent + added - deleted;
}
if (contact_count > aor->max_contacts) {
/* Enforce the maximum number of contacts */
@ -556,13 +597,8 @@ static void register_aor_core(pjsip_rx_data *rdata,
ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n",
ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts);
response->code = 403;
return;
}
details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
"Contact Comparison", 256, 256);
if (!details.pool) {
response->code = 500;
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
ao2_cleanup(existing_contacts);
return;
}
@ -595,7 +631,7 @@ static void register_aor_core(pjsip_rx_data *rdata,
}
/* Iterate each provided Contact header and add, update, or delete */
while ((contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
for (; (contact_hdr = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next)); pj_pool_reset(details.pool)) {
int expiration;
char contact_uri[pjsip_max_url_size];
RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
@ -604,6 +640,13 @@ static void register_aor_core(pjsip_rx_data *rdata,
/* A star means to unregister everything, so do so for the possible contacts */
ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
registrar_delete_contact, (void *)aor_name);
/* If we are keeping track of existing contacts for removal then, well, there is
* absolutely nothing left so no need to try to remove any.
*/
if (existing_contacts) {
ao2_ref(existing_contacts, -1);
existing_contacts = NULL;
}
break;
}
@ -617,6 +660,14 @@ static void register_aor_core(pjsip_rx_data *rdata,
pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details);
/* If a contact was returned and we need to keep track of existing contacts then it
* should be removed.
*/
if (contact && existing_contacts) {
ao2_unlink(existing_contacts, contact);
}
if (!contact) {
int prune_on_boot;
@ -672,6 +723,8 @@ static void register_aor_core(pjsip_rx_data *rdata,
aor_name,
expiration,
user_agent);
ao2_link(contacts, contact);
} else if (expiration) {
struct ast_sip_contact *contact_update;
@ -712,6 +765,7 @@ static void register_aor_core(pjsip_rx_data *rdata,
aor_name,
expiration,
contact_update->user_agent);
ao2_link(contacts, contact_update);
ao2_cleanup(contact_update);
} else {
if (contact->prune_on_boot) {
@ -749,24 +803,20 @@ static void register_aor_core(pjsip_rx_data *rdata,
* that have not been updated/added/deleted as a result of this
* REGISTER do so.
*
* The contacts container currently holds the existing contacts that
* were not affected by this REGISTER.
* The existing contacts container holds all contacts that were not
* involved in this REGISTER.
* The contacts container holds the current contacts of the AOR.
*/
if (aor->remove_existing) {
if (aor->remove_existing && existing_contacts) {
/* Total contacts after this registration */
contact_count = ao2_container_count(contacts) + updated + added;
contact_count = ao2_container_count(existing_contacts) + updated + added;
if (contact_count > aor->max_contacts) {
/* Remove excess existing contacts that expire the soonest */
remove_excess_contacts(contacts, contact_count - aor->max_contacts);
remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts);
}
ao2_ref(existing_contacts, -1);
}
/* Re-retrieve contacts. Caller will clean up the original container. */
contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor);
if (!contacts) {
response->code = 500;
return;
}
response_contact = ao2_callback(contacts, 0, NULL, NULL);
/* Send a response containing all of the contacts (including static) that are present on this AOR */
@ -782,7 +832,6 @@ static void register_aor_core(pjsip_rx_data *rdata,
registrar_add_date_header(tdata);
ao2_callback(contacts, 0, registrar_add_contact, tdata);
ao2_cleanup(contacts);
if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata));