[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(), &registration->state->timer);
-
-	registration->state->timer.id = 1;
-	pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &registration->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