mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-12 15:45:18 +00:00
Merge "res_pjsip: Remove ephemeral registered contacts on transport shutdown." into 13
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
"""add_prune_on_boot
|
||||
|
||||
Revision ID: f3d1c5d38b56
|
||||
Revises: 164abbd708c
|
||||
Create Date: 2017-08-04 17:31:23.124767
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f3d1c5d38b56'
|
||||
down_revision = '164abbd708c'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
############################# Enums ##############################
|
||||
|
||||
# yesno_values have already been created, so use postgres enum object
|
||||
# type to get around "already created" issue - works okay with mysql
|
||||
yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
|
||||
|
||||
op.add_column('ps_contacts', sa.Column('prune_on_boot', yesno_values))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('ps_contacts', 'prune_on_boot')
|
@@ -270,6 +270,8 @@ struct ast_sip_contact {
|
||||
AST_STRING_FIELD_EXTENDED(call_id);
|
||||
/*! The name of the endpoint that added the contact */
|
||||
AST_STRING_FIELD_EXTENDED(endpoint_name);
|
||||
/*! If true delete the contact on Asterisk restart/boot */
|
||||
int prune_on_boot;
|
||||
};
|
||||
|
||||
#define CONTACT_STATUS "contact_status"
|
||||
@@ -1211,6 +1213,9 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na
|
||||
* \param expiration_time Optional expiration time of the contact
|
||||
* \param path_info Path information
|
||||
* \param user_agent User-Agent header from REGISTER request
|
||||
* \param via_addr
|
||||
* \param via_port
|
||||
* \param call_id
|
||||
* \param endpoint The endpoint that resulted in the contact being added
|
||||
*
|
||||
* \retval -1 failure
|
||||
@@ -1234,6 +1239,9 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
|
||||
* \param expiration_time Optional expiration time of the contact
|
||||
* \param path_info Path information
|
||||
* \param user_agent User-Agent header from REGISTER request
|
||||
* \param via_addr
|
||||
* \param via_port
|
||||
* \param call_id
|
||||
* \param endpoint The endpoint that resulted in the contact being added
|
||||
*
|
||||
* \retval -1 failure
|
||||
@@ -1247,6 +1255,31 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
|
||||
const char *via_addr, int via_port, const char *call_id,
|
||||
struct ast_sip_endpoint *endpoint);
|
||||
|
||||
/*!
|
||||
* \brief Create a new contact for an AOR without locking the AOR
|
||||
* \since 13.18.0
|
||||
*
|
||||
* \param aor Pointer to the AOR
|
||||
* \param uri Full contact URI
|
||||
* \param expiration_time Optional expiration time of the contact
|
||||
* \param path_info Path information
|
||||
* \param user_agent User-Agent header from REGISTER request
|
||||
* \param via_addr
|
||||
* \param via_port
|
||||
* \param call_id
|
||||
* \param prune_on_boot Non-zero if the contact cannot survive a restart/boot.
|
||||
* \param endpoint The endpoint that resulted in the contact being added
|
||||
*
|
||||
* \return The created contact or NULL on failure.
|
||||
*
|
||||
* \warning
|
||||
* This function should only be called if you already hold a named write lock on the aor.
|
||||
*/
|
||||
struct ast_sip_contact *ast_sip_location_create_contact(struct ast_sip_aor *aor,
|
||||
const char *uri, struct timeval expiration_time, const char *path_info,
|
||||
const char *user_agent, const char *via_addr, int via_port, const char *call_id,
|
||||
int prune_on_boot, struct ast_sip_endpoint *endpoint);
|
||||
|
||||
/*!
|
||||
* \brief Update a contact
|
||||
*
|
||||
@@ -1267,6 +1300,12 @@ int ast_sip_location_update_contact(struct ast_sip_contact *contact);
|
||||
*/
|
||||
int ast_sip_location_delete_contact(struct ast_sip_contact *contact);
|
||||
|
||||
/*!
|
||||
* \brief Prune the prune_on_boot contacts
|
||||
* \since 13.18.0
|
||||
*/
|
||||
void ast_sip_location_prune_boot_contacts(void);
|
||||
|
||||
/*!
|
||||
* \brief Callback called when an outbound request with authentication credentials is to be sent in dialog
|
||||
*
|
||||
|
@@ -367,9 +367,12 @@
|
||||
<configOption name="rewrite_contact">
|
||||
<synopsis>Allow Contact header to be rewritten with the source IP address-port</synopsis>
|
||||
<description><para>
|
||||
On inbound SIP messages from this endpoint, the Contact header or an appropriate Record-Route
|
||||
header will be changed to have the source IP address and port. This option does not affect
|
||||
outbound messages sent to this endpoint.
|
||||
On inbound SIP messages from this endpoint, the Contact header or an
|
||||
appropriate Record-Route header will be changed to have the source IP
|
||||
address and port. This option does not affect outbound messages sent to
|
||||
this endpoint. This option helps servers communicate with endpoints
|
||||
that are behind NATs. This option also helps reuse reliable transport
|
||||
connections such as TCP and TLS.
|
||||
</para></description>
|
||||
</configOption>
|
||||
<configOption name="rtp_ipv6" default="no">
|
||||
@@ -1324,6 +1327,13 @@
|
||||
in incoming SIP REGISTER requests and is not intended to be configured manually.
|
||||
</para></description>
|
||||
</configOption>
|
||||
<configOption name="prune_on_boot">
|
||||
<synopsis>A contact that cannot survive a restart/boot.</synopsis>
|
||||
<description><para>
|
||||
The option is set if the incoming SIP REGISTER contact is rewritten
|
||||
on a reliable transport and is not intended to be configured manually.
|
||||
</para></description>
|
||||
</configOption>
|
||||
</configObject>
|
||||
<configObject name="aor">
|
||||
<synopsis>The configuration for a location of an endpoint</synopsis>
|
||||
|
@@ -356,13 +356,12 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na
|
||||
return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name);
|
||||
}
|
||||
|
||||
int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri,
|
||||
struct timeval expiration_time, const char *path_info, const char *user_agent,
|
||||
const char *via_addr, int via_port, const char *call_id,
|
||||
struct ast_sip_endpoint *endpoint)
|
||||
struct ast_sip_contact *ast_sip_location_create_contact(struct ast_sip_aor *aor,
|
||||
const char *uri, struct timeval expiration_time, const char *path_info,
|
||||
const char *user_agent, const char *via_addr, int via_port, const char *call_id,
|
||||
int prune_on_boot, struct ast_sip_endpoint *endpoint)
|
||||
{
|
||||
struct ast_sip_contact *contact;
|
||||
int res;
|
||||
char name[MAX_OBJECT_FIELD * 2 + 3];
|
||||
char hash[33];
|
||||
|
||||
@@ -371,7 +370,7 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
|
||||
|
||||
contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name);
|
||||
if (!contact) {
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_string_field_set(contact, uri, uri);
|
||||
@@ -405,14 +404,30 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
|
||||
}
|
||||
|
||||
contact->endpoint = ao2_bump(endpoint);
|
||||
|
||||
if (endpoint) {
|
||||
ast_string_field_set(contact, endpoint_name, ast_sorcery_object_get_id(endpoint));
|
||||
}
|
||||
|
||||
res = ast_sorcery_create(ast_sip_get_sorcery(), contact);
|
||||
ao2_ref(contact, -1);
|
||||
return res;
|
||||
contact->prune_on_boot = prune_on_boot;
|
||||
|
||||
if (ast_sorcery_create(ast_sip_get_sorcery(), contact)) {
|
||||
ao2_ref(contact, -1);
|
||||
return NULL;
|
||||
}
|
||||
return contact;
|
||||
}
|
||||
|
||||
int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri,
|
||||
struct timeval expiration_time, const char *path_info, const char *user_agent,
|
||||
const char *via_addr, int via_port, const char *call_id,
|
||||
struct ast_sip_endpoint *endpoint)
|
||||
{
|
||||
struct ast_sip_contact *contact;
|
||||
|
||||
contact = ast_sip_location_create_contact(aor, uri, expiration_time, path_info,
|
||||
user_agent, via_addr, via_port, call_id, 0, endpoint);
|
||||
ao2_cleanup(contact);
|
||||
return contact ? 0 : -1;
|
||||
}
|
||||
|
||||
int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
|
||||
@@ -448,6 +463,29 @@ int ast_sip_location_delete_contact(struct ast_sip_contact *contact)
|
||||
return ast_sorcery_delete(ast_sip_get_sorcery(), contact);
|
||||
}
|
||||
|
||||
static int prune_boot_contacts_cb(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_contact *contact = obj;
|
||||
|
||||
if (contact->prune_on_boot) {
|
||||
ast_sip_location_delete_contact(contact);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ast_sip_location_prune_boot_contacts(void)
|
||||
{
|
||||
struct ao2_container *contacts;
|
||||
|
||||
contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact",
|
||||
AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
|
||||
if (contacts) {
|
||||
ao2_callback(contacts, 0, prune_boot_contacts_cb, NULL);
|
||||
ao2_ref(contacts, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Custom handler for translating from a string timeval to actual structure */
|
||||
static int expiration_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
@@ -1228,6 +1266,7 @@ int ast_sip_initialize_sorcery_location(void)
|
||||
ast_sorcery_object_field_register(sorcery, "contact", "via_addr", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, via_addr));
|
||||
ast_sorcery_object_field_register(sorcery, "contact", "via_port", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_contact, via_port));
|
||||
ast_sorcery_object_field_register(sorcery, "contact", "call_id", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, call_id));
|
||||
ast_sorcery_object_field_register(sorcery, "contact", "prune_on_boot", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_contact, prune_on_boot));
|
||||
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "type", "", OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration));
|
||||
|
@@ -2022,6 +2022,8 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
|
||||
|
||||
load_all_endpoints();
|
||||
|
||||
ast_sip_location_prune_boot_contacts();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -310,6 +310,47 @@ static int registrar_validate_path(pjsip_rx_data *rdata, struct ast_sip_aor *aor
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Transport monitor for incoming REGISTER contacts */
|
||||
struct contact_transport_monitor {
|
||||
/*!
|
||||
* \brief Sorcery contact name to remove on transport shutdown
|
||||
* \note Stored after aor_name in space reserved when struct allocated.
|
||||
*/
|
||||
char *contact_name;
|
||||
/*! AOR name the contact is associated */
|
||||
char aor_name[0];
|
||||
};
|
||||
|
||||
static void register_contact_transport_shutdown_cb(void *data)
|
||||
{
|
||||
struct contact_transport_monitor *monitor = data;
|
||||
struct ast_sip_contact *contact;
|
||||
struct ast_named_lock *lock;
|
||||
|
||||
lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_MUTEX, "aor", monitor->aor_name);
|
||||
if (!lock) {
|
||||
return;
|
||||
}
|
||||
|
||||
ao2_lock(lock);
|
||||
contact = ast_sip_location_retrieve_contact(monitor->contact_name);
|
||||
if (contact) {
|
||||
ast_sip_location_delete_contact(contact);
|
||||
ast_verb(3, "Removed contact '%s' from AOR '%s' due to transport shutdown\n",
|
||||
contact->uri, monitor->aor_name);
|
||||
ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
|
||||
"Contact: %s\r\n"
|
||||
"AOR: %s\r\n"
|
||||
"UserAgent: %s",
|
||||
contact->uri,
|
||||
monitor->aor_name,
|
||||
contact->user_agent);
|
||||
ao2_ref(contact, -1);
|
||||
}
|
||||
ao2_unlock(lock);
|
||||
ast_named_lock_put(lock);
|
||||
}
|
||||
|
||||
static int register_aor_core(pjsip_rx_data *rdata,
|
||||
struct ast_sip_endpoint *endpoint,
|
||||
struct ast_sip_aor *aor,
|
||||
@@ -419,6 +460,9 @@ static int register_aor_core(pjsip_rx_data *rdata,
|
||||
pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
|
||||
|
||||
if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) {
|
||||
int prune_on_boot = 0;
|
||||
pj_str_t host_name;
|
||||
|
||||
/* If they are actually trying to delete a contact that does not exist... be forgiving */
|
||||
if (!expiration) {
|
||||
ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
|
||||
@@ -426,14 +470,68 @@ static int register_aor_core(pjsip_rx_data *rdata,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ast_sip_location_add_contact_nolock(aor, contact_uri, ast_tvadd(ast_tvnow(),
|
||||
ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL,
|
||||
user_agent, via_addr, via_port, call_id, endpoint)) {
|
||||
/* Determine if the contact cannot survive a restart/boot. */
|
||||
if (details.uri->port == rdata->pkt_info.src_port
|
||||
&& !pj_strcmp(&details.uri->host,
|
||||
pj_cstr(&host_name, rdata->pkt_info.src_name))
|
||||
/* We have already checked if the URI scheme is sip: or sips: */
|
||||
&& PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport)) {
|
||||
pj_str_t type_name;
|
||||
|
||||
/* Determine the transport parameter value */
|
||||
if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
|
||||
/* WSS is special, as it needs to be ws. */
|
||||
pj_cstr(&type_name, "ws");
|
||||
} else {
|
||||
pj_cstr(&type_name, rdata->tp_info.transport->type_name);
|
||||
}
|
||||
|
||||
if (!pj_stricmp(&details.uri->transport_param, &type_name)
|
||||
&& (endpoint->nat.rewrite_contact
|
||||
/* Websockets are always rewritten */
|
||||
|| !pj_stricmp(&details.uri->transport_param,
|
||||
pj_cstr(&type_name, "ws")))) {
|
||||
/*
|
||||
* The contact was rewritten to the reliable transport's
|
||||
* source address. Disconnecting the transport for any
|
||||
* reason invalidates the contact.
|
||||
*/
|
||||
prune_on_boot = 1;
|
||||
}
|
||||
}
|
||||
|
||||
contact = ast_sip_location_create_contact(aor, contact_uri,
|
||||
ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)),
|
||||
path_str ? ast_str_buffer(path_str) : NULL,
|
||||
user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint);
|
||||
if (!contact) {
|
||||
ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
|
||||
contact_uri, aor_name);
|
||||
contact_uri, aor_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prune_on_boot) {
|
||||
const char *contact_name;
|
||||
struct contact_transport_monitor *monitor;
|
||||
|
||||
/*
|
||||
* Monitor the transport in case it gets disconnected because
|
||||
* the contact won't be valid anymore if that happens.
|
||||
*/
|
||||
contact_name = ast_sorcery_object_get_id(contact);
|
||||
monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name)
|
||||
+ strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
|
||||
if (monitor) {
|
||||
strcpy(monitor->aor_name, aor_name);/* Safe */
|
||||
monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1;
|
||||
strcpy(monitor->contact_name, contact_name);/* Safe */
|
||||
|
||||
ast_sip_transport_monitor_register(rdata->tp_info.transport,
|
||||
register_contact_transport_shutdown_cb, monitor);
|
||||
ao2_ref(monitor, -1);
|
||||
}
|
||||
}
|
||||
|
||||
ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
|
||||
contact_uri, aor_name, expiration);
|
||||
ast_test_suite_event_notify("AOR_CONTACT_ADDED",
|
||||
@@ -893,6 +991,7 @@ static int unload_module(void)
|
||||
ast_manager_unregister(AMI_SHOW_REGISTRATIONS);
|
||||
ast_manager_unregister(AMI_SHOW_REGISTRATION_CONTACT_STATUSES);
|
||||
ast_sip_unregister_service(®istrar_module);
|
||||
ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -145,6 +145,7 @@ static int transport_create(void *data)
|
||||
{
|
||||
struct transport_create_data *create_data = data;
|
||||
struct ws_transport *newtransport = NULL;
|
||||
pjsip_tp_state_callback state_cb;
|
||||
|
||||
pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
|
||||
struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt);
|
||||
@@ -161,6 +162,10 @@ static int transport_create(void *data)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
/* Give websocket transport a unique name for its lifetime */
|
||||
snprintf(newtransport->transport.obj_name, PJ_MAX_OBJ_NAME, "ws%p",
|
||||
&newtransport->transport);
|
||||
|
||||
newtransport->transport.endpt = endpt;
|
||||
|
||||
if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
|
||||
@@ -219,6 +224,7 @@ static int transport_create(void *data)
|
||||
newtransport->transport.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)newtransport->transport.key.type);
|
||||
newtransport->transport.info = (char *)pj_pool_alloc(newtransport->transport.pool, 64);
|
||||
|
||||
newtransport->transport.dir = PJSIP_TP_DIR_INCOMING;
|
||||
newtransport->transport.tpmgr = tpmgr;
|
||||
newtransport->transport.send_msg = &ws_send_msg;
|
||||
newtransport->transport.destroy = &ws_destroy;
|
||||
@@ -242,6 +248,16 @@ static int transport_create(void *data)
|
||||
}
|
||||
|
||||
create_data->transport = newtransport;
|
||||
|
||||
/* Notify application of transport state */
|
||||
state_cb = pjsip_tpmgr_get_state_cb(newtransport->transport.tpmgr);
|
||||
if (state_cb) {
|
||||
pjsip_transport_state_info state_info;
|
||||
|
||||
memset(&state_info, 0, sizeof(state_info));
|
||||
state_cb(&newtransport->transport, PJSIP_TP_STATE_CONNECTED, &state_info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
|
Reference in New Issue
Block a user