summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
Diffstat (limited to 'res')
-rw-r--r--res/res_pjsip.c16
-rw-r--r--res/res_pjsip/location.c59
-rw-r--r--res/res_pjsip/pjsip_configuration.c2
-rw-r--r--res/res_pjsip_registrar.c107
-rw-r--r--res/res_pjsip_transport_websocket.c16
5 files changed, 183 insertions, 17 deletions
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 2917df3c5..ca0c30126 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -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">
@@ -1364,6 +1367,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>
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 6213046e3..557aeb6b9 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -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,
@@ -441,6 +456,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)
{
@@ -1221,6 +1259,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));
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index d3ff1f32b..715ffe8eb 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -2057,6 +2057,8 @@ int ast_res_pjsip_initialize_configuration(void)
load_all_endpoints();
+ ast_sip_location_prune_boot_contacts();
+
return 0;
}
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index a4ce54769..ba1c074b3 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -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_sip_aor *aor;
+
+ aor = ast_sip_location_retrieve_aor(monitor->aor_name);
+ if (!aor) {
+ return;
+ }
+
+ ao2_lock(aor);
+ 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(aor);
+ ao2_ref(aor, -1);
+}
+
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",
@@ -885,6 +983,7 @@ static int unload_module(void)
ast_manager_unregister(AMI_SHOW_REGISTRATIONS);
ast_manager_unregister(AMI_SHOW_REGISTRATION_CONTACT_STATUSES);
ast_sip_unregister_service(&registrar_module);
+ ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb);
return 0;
}
diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c
index 1429cceed..22ec19540 100644
--- a/res/res_pjsip_transport_websocket.c
+++ b/res/res_pjsip_transport_websocket.c
@@ -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: