[asterisk-commits] qwell: branch qwell/fun_with_transports r383513 - in /team/qwell/fun_with_tra...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu Mar 21 10:46:53 CDT 2013
Author: qwell
Date: Thu Mar 21 10:46:50 2013
New Revision: 383513
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=383513
Log:
Merge revisions 382380,382539,382785,382786,382898,383035 from team/file/pimp_sip_registration/, since I need it here.
Added:
team/qwell/fun_with_transports/res/res_sip/config_domain_aliases.c
- copied unchanged from r382380, team/file/pimp_sip_registration/res/res_sip/config_domain_aliases.c
team/qwell/fun_with_transports/res/res_sip_outbound_registration.c
- copied, changed from r382785, team/file/pimp_sip_registration/res/res_sip_outbound_registration.c
team/qwell/fun_with_transports/res/res_sip_registrar.c
- copied, changed from r382539, team/file/pimp_sip_registration/res/res_sip_registrar.c
Modified:
team/qwell/fun_with_transports/include/asterisk/res_sip.h
team/qwell/fun_with_transports/res/res_sip/config_transport.c
team/qwell/fun_with_transports/res/res_sip/location.c
team/qwell/fun_with_transports/res/res_sip/sip_configuration.c
team/qwell/fun_with_transports/res/res_sip_endpoint_identifier_user.c
Modified: team/qwell/fun_with_transports/include/asterisk/res_sip.h
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/include/asterisk/res_sip.h?view=diff&rev=383513&r1=383512&r2=383513
==============================================================================
--- team/qwell/fun_with_transports/include/asterisk/res_sip.h (original)
+++ team/qwell/fun_with_transports/include/asterisk/res_sip.h Thu Mar 21 10:46:50 2013
@@ -54,15 +54,17 @@
struct pjsip_tpfactory *factory;
};
-/*!
- * Details about a SIP domain
- */
-struct ast_sip_domain {
+#define SIP_SORCERY_DOMAIN_ALIAS_TYPE "domain_alias"
+
+/*!
+ * Details about a SIP domain alias
+ */
+struct ast_sip_domain_alias {
+ /*! Sorcery object details */
+ SORCERY_OBJECT(details);
AST_DECLARE_STRING_FIELDS(
- /*! The name of the domain */
- AST_STRING_FIELD(name);
- /*! Incoming context for authenticated calls from the domain */
- AST_STRING_FIELD(context);
+ /*! Domain to be aliased to */
+ AST_STRING_FIELD(domain);
);
};
@@ -98,6 +100,8 @@
AST_STRING_FIELD(external_signaling_address);
/*! External media address */
AST_STRING_FIELD(external_media_address);
+ /*! Optional domain to use for messages if provided could not be found */
+ AST_STRING_FIELD(domain);
);
/*! Type of transport */
enum ast_sip_transport_type type;
@@ -151,28 +155,20 @@
struct ast_sip_aor {
/*! Sorcery object details, the id is the AOR name */
SORCERY_OBJECT(details);
+ /*! Minimum expiration time */
+ unsigned int minimum_expiration;
+ /*! Maximum expiration time */
+ unsigned int maximum_expiration;
/*! Default contact expiration if one is not provided in the contact */
unsigned int default_expiration;
/*! Maximum number of external contacts, 0 to disable */
unsigned int max_contacts;
/*! Any permanent configured contacts */
struct ao2_container *permanent_contacts;
-};
-
-/*!
- * \brief Outbound registration
- */
-struct ast_sip_registration {
- AST_DECLARE_STRING_FIELDS(
- /*! Username for the registration */
- AST_STRING_FIELD(username);
- /*! Password for the registration */
- AST_STRING_FIELD(password);
- );
- /*! Host to send registration to. Address and port */
- struct ast_sockaddr host;
- /*! Next registration in the list */
- AST_LIST_ENTRY(ast_sip_registration) next;
+ /*! Whether to remove any existing contacts not related to an incoming REGISTER when it comes in */
+ unsigned int remove_existing;
+ /*! Any statically configured contacts */
+ struct ao2_container *static_contacts;
};
/*!
@@ -543,6 +539,16 @@
int ast_sip_location_delete_contact(struct ast_sip_contact *contact);
/*!
+ * \brief Initialize domain aliases support on a sorcery instance
+ *
+ * \param sorcery The sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_initialize_sorcery_domain_alias(struct ast_sorcery *sorcery);
+
+/*!
* \brief Initialize authentication support on a sorcery instance
*
* \param sorcery The sorcery instance
Modified: team/qwell/fun_with_transports/res/res_sip/config_transport.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/res/res_sip/config_transport.c?view=diff&rev=383513&r1=383512&r2=383513
==============================================================================
--- team/qwell/fun_with_transports/res/res_sip/config_transport.c (original)
+++ team/qwell/fun_with_transports/res/res_sip/config_transport.c Thu Mar 21 10:46:50 2013
@@ -287,6 +287,7 @@
ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_signaling_address));
ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_port", "0", OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, external_signaling_port), 0, 65535);
ast_sorcery_object_field_register(sorcery, "transport", "external_media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_media_address));
+ ast_sorcery_object_field_register(sorcery, "transport", "domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, domain));
ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_server", "", transport_tls_bool_handler, NULL, 0, 0);
ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_client", "", transport_tls_bool_handler, NULL, 0, 0);
ast_sorcery_object_field_register_custom(sorcery, "transport", "require_client_cert", "", transport_tls_bool_handler, NULL, 0, 0);
Modified: team/qwell/fun_with_transports/res/res_sip/location.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/res/res_sip/location.c?view=diff&rev=383513&r1=383512&r2=383513
==============================================================================
--- team/qwell/fun_with_transports/res/res_sip/location.c (original)
+++ team/qwell/fun_with_transports/res/res_sip/location.c Thu Mar 21 10:46:50 2013
@@ -195,9 +195,12 @@
ast_sorcery_object_field_register_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, 0, 0);
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));
+ ast_sorcery_object_field_register(sorcery, "aor", "maximum_expiration", "7200", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, maximum_expiration));
ast_sorcery_object_field_register(sorcery, "aor", "default_expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, default_expiration));
ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts));
ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, NULL, 0, 0);
+ ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
return 0;
}
Modified: team/qwell/fun_with_transports/res/res_sip/sip_configuration.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/res/res_sip/sip_configuration.c?view=diff&rev=383513&r1=383512&r2=383513
==============================================================================
--- team/qwell/fun_with_transports/res/res_sip/sip_configuration.c (original)
+++ team/qwell/fun_with_transports/res/res_sip/sip_configuration.c Thu Mar 21 10:46:50 2013
@@ -252,6 +252,13 @@
return -1;
}
+ if (ast_sip_initialize_sorcery_domain_alias(sip_sorcery)) {
+ ast_log(LOG_ERROR, "Failed to register SIP domain aliases support with sorcery\n");
+ ast_sorcery_unref(sip_sorcery);
+ sip_sorcery = NULL;
+ return -1;
+ }
+
ast_sorcery_load(sip_sorcery);
return 0;
Modified: team/qwell/fun_with_transports/res/res_sip_endpoint_identifier_user.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/res/res_sip_endpoint_identifier_user.c?view=diff&rev=383513&r1=383512&r2=383513
==============================================================================
--- team/qwell/fun_with_transports/res/res_sip_endpoint_identifier_user.c (original)
+++ team/qwell/fun_with_transports/res/res_sip_endpoint_identifier_user.c Thu Mar 21 10:46:50 2013
@@ -28,7 +28,7 @@
#include "asterisk/res_sip.h"
#include "asterisk/module.h"
-static int get_endpoint_name(pjsip_rx_data *rdata, char *endpoint, size_t size)
+static int get_endpoint_details(pjsip_rx_data *rdata, char *endpoint, size_t endpoint_size, char *domain, size_t domain_size)
{
pjsip_uri *from = rdata->msg_info.from->uri;
pjsip_sip_uri *sip_from;
@@ -36,18 +36,65 @@
return -1;
}
sip_from = (pjsip_sip_uri *) pjsip_uri_get_uri(from);
- ast_copy_pj_str(endpoint, &sip_from->user, size);
+ ast_copy_pj_str(endpoint, &sip_from->user, endpoint_size);
+ ast_copy_pj_str(domain, &sip_from->host, domain_size);
+ return 0;
+}
+
+static int find_transport_in_use(void *obj, void *arg, int flags)
+{
+ struct ast_sip_transport *transport = obj;
+ pjsip_rx_data *rdata = arg;
+
+ if ((transport->state->transport == rdata->tp_info.transport) ||
+ (transport->state->factory && !pj_strcmp(&transport->state->factory->addr_name.host, &rdata->tp_info.transport->local_name.host) &&
+ transport->state->factory->addr_name.port == rdata->tp_info.transport->local_name.port)) {
+ return CMP_MATCH | CMP_STOP;
+ }
+
return 0;
}
static struct ast_sip_endpoint *username_identify(pjsip_rx_data *rdata)
{
- char endpoint_name[64];
+ char endpoint_name[64], domain_name[64], id[AST_UUID_STR_LEN];
struct ast_sip_endpoint *endpoint;
- if (get_endpoint_name(rdata, endpoint_name, sizeof(endpoint_name))) {
+ RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup);
+ RAII_VAR(struct ao2_container *, transports, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
+
+ if (get_endpoint_details(rdata, endpoint_name, sizeof(endpoint_name), domain_name, sizeof(domain_name))) {
return NULL;
}
+
+ /* Attempt to find the endpoint given the name and domain provided */
+ snprintf(id, sizeof(id), "%s@%s", endpoint_name, domain_name);
+ if ((endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id))) {
+ goto done;
+ }
+
+ /* See if an alias exists for the domain provided */
+ if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
+ snprintf(id, sizeof(id), "%s@%s", endpoint_name, alias->domain);
+ if ((endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id))) {
+ goto done;
+ }
+ }
+
+ /* See if the transport this came in on has a provided domain */
+ if ((transports = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL)) &&
+ (transport = ao2_callback(transports, 0, find_transport_in_use, rdata)) &&
+ !ast_strlen_zero(transport->domain)) {
+ snprintf(id, sizeof(id), "%s@%s", endpoint_name, transport->domain);
+ if ((endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id))) {
+ goto done;
+ }
+ }
+
+ /* Fall back to no domain */
endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name);
+
+done:
if (endpoint) {
if (!(endpoint->ident_method & AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME)) {
ao2_ref(endpoint, -1);
Copied: team/qwell/fun_with_transports/res/res_sip_outbound_registration.c (from r382785, team/file/pimp_sip_registration/res/res_sip_outbound_registration.c)
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/res/res_sip_outbound_registration.c?view=diff&rev=383513&p1=team/file/pimp_sip_registration/res/res_sip_outbound_registration.c&r1=382785&p2=team/qwell/fun_with_transports/res/res_sip_outbound_registration.c&r2=383513
==============================================================================
--- team/file/pimp_sip_registration/res/res_sip_outbound_registration.c (original)
+++ team/qwell/fun_with_transports/res/res_sip_outbound_registration.c Thu Mar 21 10:46:50 2013
@@ -49,7 +49,11 @@
/*! \brief Outbound registration client */
pjsip_regc *client;
/*! \brief Timer entry for retrying on temporal responses */
- pj_timer_entry timer;
+ pj_timer_entry *timer;
+ /*! \brief Current number of retries */
+ unsigned int retries;
+ /*! \brief Maximum number of retries permitted */
+ unsigned int max_retries;
/*! \brief Interval at which retries should occur for temporal responses */
unsigned int retry_interval;
};
@@ -75,20 +79,44 @@
unsigned int expiration;
/*! \brief Interval at which retries should occur for temporal responses */
unsigned int retry_interval;
+ /*! \brief Maximum number of retries permitted */
+ unsigned int max_retries;
/*! \brief Outbound registration state */
struct sip_outbound_registration_state *state;
};
-/*! \brief Callback function for outbound registration attempt */
+/*! \brief Callback function for outbound registration attempt or destruction */
static void sip_outbound_registration_attempt_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
{
pjsip_tx_data *tdata;
- if (pjsip_regc_register(entry->user_data, PJ_TRUE, &tdata) != PJ_SUCCESS) {
- return;
- }
-
- pjsip_regc_send(entry->user_data, tdata);
+ if (entry->id == 1) {
+ if (pjsip_regc_register(entry->user_data, PJ_TRUE, &tdata) != PJ_SUCCESS) {
+ return;
+ }
+ pjsip_regc_send(entry->user_data, tdata);
+ } else if (entry->id == 2) {
+ if (pjsip_regc_unregister(entry->user_data, &tdata) == PJ_SUCCESS) {
+ pjsip_regc_send(entry->user_data, tdata);
+ }
+ pjsip_regc_destroy(entry->user_data);
+ }
+}
+
+/* \brief Helper funtion which determines if a response code is temporal or not */
+static int sip_outbound_registration_is_temporal(unsigned int code)
+{
+ /* Shamelessly taken from pjsua */
+ if (code == PJSIP_SC_REQUEST_TIMEOUT ||
+ code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
+ code == PJSIP_SC_BAD_GATEWAY ||
+ code == PJSIP_SC_SERVICE_UNAVAILABLE ||
+ code == PJSIP_SC_SERVER_TIMEOUT ||
+ PJSIP_IS_STATUS_IN_CLASS(code, 600)) {
+ return 1;
+ } else {
+ return 0;
+ }
}
/*! \brief Callback function for outbound registration client */
@@ -96,18 +124,33 @@
{
struct sip_outbound_registration_state *state = param->token;
pjsip_regc_info info;
+ char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
+
pjsip_regc_get_info(param->regc, &info);
+ ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
+ ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
/* Registration was accepted, yahoo! */
state->status = SIP_REGISTRATION_REGISTERED;
+ } else if (state->retry_interval && sip_outbound_registration_is_temporal(param->code)) {
+ if (state->retries == state->max_retries) {
+ state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
+ ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
+ server_uri, client_uri);
+ } else {
+ pj_time_val delay = { .sec = state->retry_interval, };
+
+ state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
+ state->retries++;
+ pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), state->timer, &delay);
+ ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on registration attempt to '%s', retrying in '%d' seconds\n",
+ param->code, server_uri, client_uri, state->retry_interval);
+ }
} else {
- pj_time_val delay = { .sec = 5, };
-
state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
-
- ast_log(LOG_NOTICE, "Registration failed\n");
- pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &state->timer, &delay);
+ ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
+ param->code, server_uri, client_uri);
}
}
@@ -115,23 +158,19 @@
static void sip_outbound_registration_state_destroy(void *obj)
{
struct sip_outbound_registration_state *state = obj;
+ pj_time_val delay = { .sec = 1, };
if (!state->client) {
return;
}
- pjsip_endpt_cancel_timer(ast_sip_get_pjsip_endpoint(), &state->timer);
-
- /* If we have at least attempted registering once send an unregister */
- if (state->timer.id) {
- pjsip_tx_data *tdata;
-
- if (pjsip_regc_unregister(state->client, &tdata) == PJ_SUCCESS) {
- pjsip_regc_send(state->client, tdata);
- }
- }
-
- pjsip_regc_destroy(state->client);
+ if (state->status != SIP_REGISTRATION_UNREGISTERED) {
+ pjsip_endpt_cancel_timer(ast_sip_get_pjsip_endpoint(), state->timer);
+ state->timer->id = 2;
+ pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), state->timer, &delay);
+ } else {
+ pjsip_regc_destroy(state->client);
+ }
}
/*! \brief Allocator function for registration state */
@@ -141,15 +180,16 @@
if (!state) {
return NULL;
- } else if ((pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state, sip_outbound_registration_response_cb, &state->client) != PJ_SUCCESS)) {
+ } else if ((pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state, sip_outbound_registration_response_cb, &state->client) != PJ_SUCCESS) ||
+ !(state->timer = PJ_POOL_ZALLOC_T(pjsip_regc_get_pool(state->client), struct pj_timer_entry))) {
ao2_cleanup(state);
return NULL;
}
state->status = SIP_REGISTRATION_UNREGISTERED;
- state->timer.id = 0;
- state->timer.user_data = state->client;
- state->timer.cb = sip_outbound_registration_attempt_cb;
+ state->timer->id = 1;
+ state->timer->user_data = state->client;
+ state->timer->cb = sip_outbound_registration_attempt_cb;
return state;
}
@@ -319,16 +359,15 @@
struct sip_outbound_registration *registration = obj;
pj_time_val delay = { .sec = 1, };
- /* We update the retry interval in case it has changed, as it can be */
registration->state->retry_interval = registration->retry_interval;
+ registration->state->max_retries = registration->max_retries;
+ registration->state->retries = 0;
pjsip_regc_update_expires(registration->state->client, registration->expiration);
/* Cancel retry attept if present and try a new registration */
- pjsip_endpt_cancel_timer(ast_sip_get_pjsip_endpoint(), ®istration->state->timer);
-
- registration->state->timer.id = 1;
- pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), ®istration->state->timer, &delay);
+ pjsip_endpt_cancel_timer(ast_sip_get_pjsip_endpoint(), registration->state->timer);
+ pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), registration->state->timer, &delay);
return 0;
}
@@ -361,6 +400,7 @@
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy));
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "60", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval));
+ ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries));
ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
sip_outbound_registration_perform_all();
Copied: team/qwell/fun_with_transports/res/res_sip_registrar.c (from r382539, team/file/pimp_sip_registration/res/res_sip_registrar.c)
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/res/res_sip_registrar.c?view=diff&rev=383513&p1=team/file/pimp_sip_registration/res/res_sip_registrar.c&r1=382539&p2=team/qwell/fun_with_transports/res/res_sip_registrar.c&r2=383513
==============================================================================
--- team/file/pimp_sip_registration/res/res_sip_registrar.c (original)
+++ team/qwell/fun_with_transports/res/res_sip_registrar.c Thu Mar 21 10:46:50 2013
@@ -30,68 +30,90 @@
#include "asterisk/res_sip.h"
#include "asterisk/module.h"
+/*! \brief Internal function which returns the expiration time for a contact */
+static int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata)
+{
+ pjsip_expires_hdr *expires;
+ int expiration = aor->default_expiration;
+
+ if (contact->expires != -1) {
+ /* Expiration was provided with the contact itself */
+ expiration = contact->expires;
+ }
+ else if ((expires = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
+ /* Expiration was provided using the Expires header */
+ expiration = expires->ivalue;
+ }
+
+ /* If the value has explicitly been set to 0, do not enforce */
+ if (!expiration) {
+ return expiration;
+ }
+
+ /* Enforce the range that we will allow for expiration */
+ if (expiration < aor->minimum_expiration) {
+ expiration = aor->minimum_expiration;
+ }
+ else if (expiration > aor->maximum_expiration) {
+ expiration = aor->maximum_expiration;
+ }
+
+ return expiration;
+}
+
+/*! \brief Callback function for finding a contact */
+static int registrar_find_contact(void *obj, void *arg, int flags)
+{
+ struct ast_sip_contact *contact = obj;
+ const char *uri = arg;
+
+ return !strcmp(contact->uri, uri) ? CMP_MATCH | CMP_STOP : 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)
+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)
{
pjsip_contact_hdr *previous = NULL, *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
- int count = 0;
while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) {
+ pjsip_sip_uri *uri;
+ char contact_uri[PJSIP_MAX_URL_SIZE];
+ int expiration;
+ RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup);
+
if (contact->star) {
/* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */
if ((contact->expires != 0) || previous) {
- return count;
+ return -1;
}
} else if (previous && previous->star) {
/* If there is a previous contact and it is a '*' this is a deal breaker */
- return count;
+ return -1;
}
previous = contact;
- count++;
+
+ if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
+ continue;
+ }
+
+ uri = pjsip_uri_get_uri(contact->uri);
+ pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, uri, contact_uri, sizeof(contact_uri));
+ expiration = registrar_get_expiration(aor, contact, rdata);
+
+ /* Determine if this is an add, update, or delete for policy enforcement purposes */
+ if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, contact_uri))) {
+ if (expiration) {
+ (*added)++;
+ }
+ } else if (expiration) {
+ (*updated)++;
+ } else {
+ (*deleted)++;
+ }
}
/* The provided contacts are acceptable, huzzah! */
- return count;
-}
-
-/*! \brief Internal function which returns the expiration time for a contact */
-static int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata)
-{
- pjsip_expires_hdr *expires;
- int expiration = aor->default_expiration;
-
- if (contact->expires != -1) {
- /* Expiration was provided with the contact itself */
- expiration = contact->expires;
- }
- else if ((expires = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
- /* Expiration was provided using the Expires header */
- expiration = expires->ivalue;
- }
-
- /* If the value has explicitly been set to 0, do not enforce */
- if (!expiration) {
- return expiration;
- }
-
- /* Enforce the range that we will allow for expiration */
- if (expiration < aor->minimum_expiration) {
- expiration = aor->minimum_expiration;
- }
- else if (expiration > aor->maximum_expiration) {
- expiration = aor->maximum_expiration;
- }
-
- return expiration;
-}
-
-/*! \brief Callback function for finding a contact */
-static int registrar_find_contact(void *obj, void *arg, int flags)
-{
- struct ast_sip_contact *contact = obj;
- const char *uri = arg;
-
- return !strcmp(contact->uri, uri) ? CMP_MATCH | CMP_STOP : 0;
+ return 0;
}
/*! \brief Callback function which prunes static contacts */
@@ -149,8 +171,8 @@
char user_name[64], domain_name[64];
char *configured_aors, *aor_name;
RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
- int count;
RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
+ int added = 0, updated = 0, deleted = 0;
pjsip_contact_hdr *contact_hdr = NULL;
pjsip_tx_data *tdata;
pjsip_response_addr addr;
@@ -202,20 +224,20 @@
/* Registration is not permitted for this AOR */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
return PJ_TRUE;
- } else if ((count = registrar_validate_contacts(rdata)) == -1) {
+ }
+
+ /* Retrieve the current contacts, we'll need to know whether to update or not */
+ contacts = ast_sip_location_retrieve_aor_contacts(aor);
+
+ /* 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);
+
+ if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) {
/* The provided Contact headers do not conform to the specification */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
return PJ_TRUE;
- }
-
- /* Retrieve the current contacts, we'll need to know whether to update or not */
- contacts = ast_sip_location_retrieve_aor_contacts(aor);
-
- /* 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);
-
- /* Don't let this REGISTER exceed the number of contacts permitted for the AOR */
- if ((ao2_container_count(contacts) + count) > aor->max_contacts) {
+ } else if ((MIN(0, added - deleted) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) {
+ /* Enforce the maximum number of contacts */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
return PJ_TRUE;
}
@@ -239,7 +261,7 @@
uri = pjsip_uri_get_uri(contact_hdr->uri);
pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, uri, contact_uri, sizeof(contact_uri));
- if (!(contact = ao2_callback(contacts, 0, registrar_find_contact, contact_uri))) {
+ if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, contact_uri))) {
/* 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",
@@ -264,6 +286,13 @@
}
}
+ /* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
+ * do so
+ */
+ if (aor->remove_existing) {
+ ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
+ }
+
/* Update the contacts as things will probably have changed */
ao2_cleanup(contacts);
contacts = ast_sip_location_retrieve_aor_contacts(aor);
More information about the asterisk-commits
mailing list