[asterisk-commits] file: branch group/pimp_my_sip r383707 - in /team/group/pimp_my_sip: include/...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Mar 25 09:08:19 CDT 2013


Author: file
Date: Mon Mar 25 09:08:15 2013
New Revision: 383707

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=383707
Log:
Add support for domains, domain aliases, inbound registrations, and outbound registrations.

Contacts can now be dynamically manipulated on AORs using the registrar module and by registering
from a device. Outbound registrations can now be configured and are refreshed as expected. Since
outbound authentication has not yet been merged, it is not yet possible to authenticate.

Review: https://reviewboard.asterisk.org/r/2398/

Added:
    team/group/pimp_my_sip/res/res_sip_outbound_registration.c   (with props)
    team/group/pimp_my_sip/res/res_sip_registrar.c   (with props)
Modified:
    team/group/pimp_my_sip/include/asterisk/res_sip.h
    team/group/pimp_my_sip/res/res_sip/config_transport.c
    team/group/pimp_my_sip/res/res_sip/location.c
    team/group/pimp_my_sip/res/res_sip/sip_configuration.c
    team/group/pimp_my_sip/res/res_sip_endpoint_identifier_user.c

Modified: team/group/pimp_my_sip/include/asterisk/res_sip.h
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/include/asterisk/res_sip.h?view=diff&rev=383707&r1=383706&r2=383707
==============================================================================
--- team/group/pimp_my_sip/include/asterisk/res_sip.h (original)
+++ team/group/pimp_my_sip/include/asterisk/res_sip.h Mon Mar 25 09:08:15 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);
 	);
 };
 
@@ -79,7 +81,7 @@
 /*! \brief Maximum number of ciphers supported for a TLS transport */
 #define SIP_TLS_MAX_CIPHERS 64
 
-/*!
+/*
  * \brief Transport to bind to
  */
 struct ast_sip_transport {
@@ -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,18 @@
 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;
+	/*! Whether to remove any existing contacts not related to an incoming REGISTER when it comes in */
+	unsigned int remove_existing;
 	/*! 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;
 };
 
 /*!
@@ -543,6 +537,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/group/pimp_my_sip/res/res_sip/config_transport.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip/config_transport.c?view=diff&rev=383707&r1=383706&r2=383707
==============================================================================
--- team/group/pimp_my_sip/res/res_sip/config_transport.c (original)
+++ team/group/pimp_my_sip/res/res_sip/config_transport.c Mon Mar 25 09:08:15 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/group/pimp_my_sip/res/res_sip/location.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip/location.c?view=diff&rev=383707&r1=383706&r2=383707
==============================================================================
--- team/group/pimp_my_sip/res/res_sip/location.c (original)
+++ team/group/pimp_my_sip/res/res_sip/location.c Mon Mar 25 09:08:15 2013
@@ -195,8 +195,11 @@
 	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(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
 	ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, NULL, 0, 0);
 
 	return 0;

Modified: team/group/pimp_my_sip/res/res_sip/sip_configuration.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip/sip_configuration.c?view=diff&rev=383707&r1=383706&r2=383707
==============================================================================
--- team/group/pimp_my_sip/res/res_sip/sip_configuration.c (original)
+++ team/group/pimp_my_sip/res/res_sip/sip_configuration.c Mon Mar 25 09:08:15 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/group/pimp_my_sip/res/res_sip_endpoint_identifier_user.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip_endpoint_identifier_user.c?view=diff&rev=383707&r1=383706&r2=383707
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_endpoint_identifier_user.c (original)
+++ team/group/pimp_my_sip/res/res_sip_endpoint_identifier_user.c Mon Mar 25 09:08:15 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);

Added: team/group/pimp_my_sip/res/res_sip_outbound_registration.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip_outbound_registration.c?view=auto&rev=383707
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_outbound_registration.c (added)
+++ team/group/pimp_my_sip/res/res_sip_outbound_registration.c Mon Mar 25 09:08:15 2013
@@ -1,0 +1,580 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_ua.h>
+
+#include "asterisk/res_sip.h"
+#include "asterisk/module.h"
+#include "asterisk/taskprocessor.h"
+
+/*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */
+#define REREGISTER_BUFFER_TIME 10
+
+/*! \brief Various states that an outbound registration may be in */
+enum sip_outbound_registration_status {
+	/*! \brief Currently unregistered */
+	SIP_REGISTRATION_UNREGISTERED = 0,
+	/*! \brief Registered, yay! */
+	SIP_REGISTRATION_REGISTERED,
+	/*! \brief Registration was rejected, but response was temporal */
+	SIP_REGISTRATION_REJECTED_TEMPORARY,
+	/*! \brief Registration was rejected, permanently */
+	SIP_REGISTRATION_REJECTED_PERMANENT,
+	/*! \brief Registration has been stopped */
+	SIP_REGISTRATION_STOPPED,
+};
+
+/*! \brief Outbound registration client state information (persists for lifetime of regc) */
+struct sip_outbound_registration_client_state {
+	/*! \brief Current status of this registration */
+	enum sip_outbound_registration_status status;
+	/*! \brief Outbound registration client */
+	pjsip_regc *client;
+	/*! \brief Timer entry for retrying on temporal responses */
+	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;
+	/*! \brief Serializer for stuff and things */
+	struct ast_taskprocessor *serializer;
+	/*! \brief Registration should be destroyed after completion of transaction */
+	unsigned int destroy:1;
+};
+
+/*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
+struct sip_outbound_registration_state {
+	/*! \brief Client state information */
+	struct sip_outbound_registration_client_state *client_state;
+};
+
+/*! \brief Outbound registration information */
+struct sip_outbound_registration {
+	/*! \brief Sorcery object details */
+	SORCERY_OBJECT(details);
+	/*! \brief Stringfields */
+	AST_DECLARE_STRING_FIELDS(
+		/*! \brief URI for the registrar */
+		AST_STRING_FIELD(server_uri);
+		/*! \brief URI for the AOR */
+		AST_STRING_FIELD(client_uri);
+		/*! \brief Optional user for contact header */
+		AST_STRING_FIELD(contact_user);
+		/*! \brief Explicit transport to use for registration */
+		AST_STRING_FIELD(transport);
+		/*! \brief Outbound proxy to use */
+		AST_STRING_FIELD(outbound_proxy);
+	);
+	/*! \brief Requested expiration time */
+	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 Helper function which cancels the timer on a client */
+static void cancel_registration(struct sip_outbound_registration_client_state *client_state)
+{
+	if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &client_state->timer)) {
+		/* The timer was successfully cancelled, drop the refcount of client_state */
+		ao2_ref(client_state, -1);
+	}
+}
+
+/*! \brief Callback function for registering */
+static int handle_client_registration(void *data)
+{
+	RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
+	pjsip_tx_data *tdata;
+
+	cancel_registration(client_state);
+
+	if ((client_state->status == SIP_REGISTRATION_STOPPED) ||
+		(pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS)) {
+		return 0;
+	}
+
+	/* Due to the registration the callback may now get called, so bump the ref count */
+	ao2_ref(client_state, +1);
+	if (pjsip_regc_send(client_state->client, tdata) != PJ_SUCCESS) {
+		ao2_ref(client_state, -1);
+		pjsip_tx_data_dec_ref(tdata);
+	}
+
+	return 0;
+}
+
+/*! \brief Timer callback function, used just for registrations */
+static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
+{
+	RAII_VAR(struct sip_outbound_registration_client_state *, client_state, entry->user_data, ao2_cleanup);
+
+	ao2_ref(client_state, +1);
+	if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
+		ast_log(LOG_WARNING, "Failed to pass outbound registration to threadpool\n");
+		ao2_ref(client_state, -1);
+	}
+
+	entry->id = 0;
+}
+
+/*! \brief Helper function which sets up the timer to re-register in a specific amount of time */
+static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds)
+{
+	pj_time_val delay = { .sec = seconds, };
+
+	cancel_registration(client_state);
+
+	ao2_ref(client_state, +1);
+	if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay) != PJ_SUCCESS) {
+		ast_log(LOG_WARNING, "Failed to pass timed registration to scheduler\n");
+		ao2_ref(client_state, -1);
+	}
+}
+
+/*! \brief Callback function for unregistering (potentially) and destroying state */
+static int handle_client_state_destruction(void *data)
+{
+	RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
+	pjsip_regc_info info;
+
+	cancel_registration(client_state);
+
+	pjsip_regc_get_info(client_state->client, &info);
+
+	if (info.is_busy == PJ_TRUE) {
+		/* If a client transaction is in progress we defer until it is complete */
+		client_state->destroy = 1;
+		return 0;
+	}
+
+	if (client_state->status != SIP_REGISTRATION_UNREGISTERED && client_state->status != SIP_REGISTRATION_REJECTED_PERMANENT) {
+		pjsip_tx_data *tdata;
+
+		if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS) {
+			pjsip_regc_send(client_state->client, tdata);
+		}
+	}
+
+	pjsip_regc_destroy(client_state->client);
+
+	client_state->status = SIP_REGISTRATION_STOPPED;
+
+	return 0;
+}
+
+/*! \brief Structure for registration response */
+struct registration_response {
+	/*! \brief Response code for the registration attempt */
+	int code;
+	/*! \brief Expiration time for registration */
+	int expiration;
+	/*! \brief Retry-After value */
+	int retry_after;
+	/*! \brief Outbound registration client state */
+	struct sip_outbound_registration_client_state *client_state;
+};
+
+/*! \brief Registration response structure destructor */
+static void registration_response_destroy(void *obj)
+{
+	struct registration_response *response = obj;
+
+	ao2_cleanup(response->client_state);
+}
+
+/* \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 handling a response to a registration attempt */
+static int handle_registration_response(void *data)
+{
+	RAII_VAR(struct registration_response *, response, data, ao2_cleanup);
+	pjsip_regc_info info;
+	char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
+
+	if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
+		return 0;
+	}
+
+	pjsip_regc_get_info(response->client_state->client, &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(response->code, 200)) {
+		/* If the registration went fine simply reschedule registration for the future */
+		response->client_state->status = SIP_REGISTRATION_REGISTERED;
+		response->client_state->retries = 0;
+		schedule_registration(response->client_state, response->expiration - REREGISTER_BUFFER_TIME);
+	} else if (response->retry_after) {
+		/* If we have been instructed to retry after a period of time, schedule it as such */
+		response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
+		schedule_registration(response->client_state, response->retry_after);
+		ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on registration attempt to '%s', instructed to retry in '%d'\n",
+			response->code, server_uri, client_uri, response->retry_after);
+	} else if (response->client_state->retry_interval && sip_outbound_registration_is_temporal(response->code)) {
+		if (response->client_state->retries == response->client_state->max_retries) {
+			/* If we received enough temporal responses to exceed our maximum give up permanently */
+			response->client_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 {
+			/* On the other hand if we can still try some more do so */
+			response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
+			response->client_state->retries++;
+			schedule_registration(response->client_state, response->client_state->retry_interval);
+			ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on registration attempt to '%s', retrying in '%d' seconds\n",
+				response->code, server_uri, client_uri, response->client_state->retry_interval);
+		}
+	} else {
+		/* Finally if there's no hope of registering give up */
+		response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
+		ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
+			response->code, server_uri, client_uri);
+	}
+
+	/* If deferred destruction is in use see if we need to destroy now */
+	if (response->client_state->destroy) {
+		handle_client_state_destruction(response->client_state);
+	}
+
+	return 0;
+}
+
+/*! \brief Callback function for outbound registration client */
+static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
+{
+	RAII_VAR(struct sip_outbound_registration_client_state *, client_state, param->token, ao2_cleanup);
+	struct registration_response *response = ao2_alloc(sizeof(*response), registration_response_destroy);
+	struct pjsip_retry_after_hdr *retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER, NULL);
+
+	response->code = param->code;
+	response->expiration = param->expiration;
+	response->retry_after = retry_after ? retry_after->ivalue : 0;
+	response->client_state = client_state;
+	ao2_ref(response->client_state, +1);
+
+	if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) {
+		ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
+		ao2_cleanup(response);
+	}
+}
+
+/*! \brief Destructor function for registration state */
+static void sip_outbound_registration_state_destroy(void *obj)
+{
+	struct sip_outbound_registration_state *state = obj;
+
+	if (!state->client_state) {
+		return;
+	}
+
+	if (state->client_state->serializer && ast_sip_push_task(state->client_state->serializer, handle_client_state_destruction, state->client_state)) {
+		ast_log(LOG_WARNING, "Failed to pass outbound registration client destruction to threadpool\n");
+		ao2_ref(state->client_state, -1);
+	}
+}
+
+/*! \brief Destructor function for client registration state */
+static void sip_outbound_registration_client_state_destroy(void *obj)
+{
+	struct sip_outbound_registration_client_state *client_state = obj;
+
+	ast_taskprocessor_unreference(client_state->serializer);
+}
+
+/*! \brief Allocator function for registration state */
+static struct sip_outbound_registration_state *sip_outbound_registration_state_alloc(void)
+{
+	struct sip_outbound_registration_state *state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy);
+
+	if (!state || !(state->client_state = ao2_alloc(sizeof(*state->client_state), sip_outbound_registration_client_state_destroy))) {
+		ao2_cleanup(state);
+		return NULL;
+	}
+
+	if ((pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state, sip_outbound_registration_response_cb, &state->client_state->client) != PJ_SUCCESS) ||
+		!(state->client_state->serializer = ast_sip_create_serializer())) {
+		/* This is on purpose, normal operation will have it be deallocated within the serializer */
+		pjsip_regc_destroy(state->client_state->client);
+		ao2_cleanup(state->client_state);
+		ao2_cleanup(state);
+		return NULL;
+	}
+
+	state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
+	state->client_state->timer.user_data = state->client_state;
+	state->client_state->timer.cb = sip_outbound_registration_timer_cb;
+
+	return state;
+}
+
+/*! \brief Destructor function for registration information */
+static void sip_outbound_registration_destroy(void *obj)
+{
+	struct sip_outbound_registration *registration = obj;
+
+	ao2_cleanup(registration->state);
+
+	ast_string_field_free_memory(registration);
+}
+
+/*! \brief Allocator function for registration information */
+static void *sip_outbound_registration_alloc(const char *name)
+{
+	struct sip_outbound_registration *registration = ao2_alloc(sizeof(*registration), sip_outbound_registration_destroy);
+
+	if (!registration || ast_string_field_init(registration, 256)) {
+		ao2_cleanup(registration);
+		return NULL;
+	}
+
+	return registration;
+}
+
+/*! \brief Helper function which populates a pj_str_t with a contact header */
+static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const char *user, const pj_str_t *target, pjsip_tpselector *selector)
+{
+	pj_str_t tmp, local_addr;
+	pjsip_uri *uri;
+	pjsip_sip_uri *sip_uri;
+	pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
+	int local_port;
+
+	pj_strdup_with_null(pool, &tmp, target);
+
+	if (!(uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0)) ||
+	    (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) {
+		return -1;
+	}
+
+	sip_uri = pjsip_uri_get_uri(uri);
+
+	if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) {
+		type = PJSIP_TRANSPORT_TLS;
+	} else if (!sip_uri->transport_param.slen) {
+		type = PJSIP_TRANSPORT_UDP;
+	} else {
+		type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
+	}
+
+	if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
+		return -1;
+	}
+
+	if (pj_strchr(&sip_uri->host, ':')) {
+		type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
+	}
+
+	if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), pool, type, selector,
+							      &local_addr, &local_port) != PJ_SUCCESS) {
+		return -1;
+	}
+
+	if (!pj_strchr(&sip_uri->host, ':') && pj_strchr(&local_addr, ':')) {
+		type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
+	}
+
+	contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+	contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
+				      "<%s:%s@%s%.*s%s:%d%s%s>",
+				      (pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip",
+				      user,
+				      (type & PJSIP_TRANSPORT_IPV6) ? "[" : "",
+				      (int)local_addr.slen,
+				      local_addr.ptr,
+				      (type & PJSIP_TRANSPORT_IPV6) ? "]" : "",
+				      local_port,
+				      (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
+				      (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "");
+
+	return 0;
+}
+
+/*! \brief Apply function which finds or allocates a state structure */
+static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj)
+{
+	RAII_VAR(struct sip_outbound_registration *, existing, ast_sorcery_retrieve_by_id(sorcery, "registration", ast_sorcery_object_get_id(obj)), ao2_cleanup);
+	struct sip_outbound_registration *applied = obj;
+	pj_str_t server_uri, client_uri, contact_uri;
+	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+
+	if (!existing) {
+		/* If no existing registration exists we can just start fresh easily */
+		applied->state = sip_outbound_registration_state_alloc();
+	} else {
+		/* If there is an existing registration things are more complicated, we can immediately reuse this state if most stuff remains unchanged */
+		if (!strcmp(existing->server_uri, applied->server_uri) && !strcmp(existing->client_uri, applied->client_uri) &&
+			!strcmp(existing->transport, applied->transport) && !strcmp(existing->contact_user, applied->contact_user) &&
+			!strcmp(existing->outbound_proxy, applied->outbound_proxy)) {
+			applied->state = existing->state;
+			ao2_ref(applied->state, +1);
+			return 0;
+		}
+		applied->state = sip_outbound_registration_state_alloc();
+	}
+
+	if (!applied->state) {
+		return -1;
+	}
+
+	if (!ast_strlen_zero(applied->transport)) {
+		RAII_VAR(struct ast_sip_transport *, transport, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", applied->transport), ao2_cleanup);
+
+		if (!transport || !transport->state) {
+			return -1;
+		}
+
+		if (transport->type == AST_SIP_TRANSPORT_UDP) {
+			selector.type = PJSIP_TPSELECTOR_TRANSPORT;
+			selector.u.transport = transport->state->transport;
+		} else if (transport->type == AST_SIP_TRANSPORT_TCP || transport->type == AST_SIP_TRANSPORT_TLS) {
+			selector.type = PJSIP_TPSELECTOR_LISTENER;
+			selector.u.listener = transport->state->factory;
+		} else {
+			return -1;
+		}
+	}
+
+	pjsip_regc_set_transport(applied->state->client_state->client, &selector);
+
+	if (!ast_strlen_zero(applied->outbound_proxy)) {
+		pjsip_route_hdr route_set, *route;
+		static const pj_str_t ROUTE_HNAME = { "Route", 5 };
+		pj_str_t tmp;
+
+		pj_list_init(&route_set);
+
+		pj_strdup2_with_null(pjsip_regc_get_pool(applied->state->client_state->client), &tmp, applied->outbound_proxy);
+		if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(applied->state->client_state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) {
+			return -1;
+		}
+		pj_list_push_back(&route_set, route);
+
+		pjsip_regc_set_route_set(applied->state->client_state->client, &route_set);
+	}
+
+	pj_cstr(&server_uri, applied->server_uri);
+
+	if (sip_dialog_create_contact(pjsip_regc_get_pool(applied->state->client_state->client), &contact_uri, S_OR(applied->contact_user, "s"), &server_uri, &selector)) {
+		return -1;
+	}
+
+	pj_cstr(&client_uri, applied->client_uri);
+
+	if (pjsip_regc_init(applied->state->client_state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, applied->expiration) != PJ_SUCCESS) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*! \brief Helper function which performs a single registration */
+static int sip_outbound_registration_perform(void *obj, void *arg, int flags)
+{
+	struct sip_outbound_registration *registration = obj;
+
+	registration->state->client_state->retry_interval = registration->retry_interval;
+	registration->state->client_state->max_retries = registration->max_retries;
+	registration->state->client_state->retries = 0;
+
+	pjsip_regc_update_expires(registration->state->client_state->client, registration->expiration);
+
+	schedule_registration(registration->state->client_state, (ast_random() % 10) + 1);
+
+	return 0;
+}
+
+/*! \brief Helper function which performs all registrations */
+static void sip_outbound_registration_perform_all(void)
+{
+	RAII_VAR(struct ao2_container *, registrations, ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup);
+
+	if (!registrations) {
+		return;
+	}
+
+	ao2_callback(registrations, OBJ_NODATA, sip_outbound_registration_perform, NULL);
+}
+
+static int load_module(void)
+{
+	ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "res_sip.conf,criteria=type=registration");
+
+	if (ast_sorcery_object_register(ast_sip_get_sorcery(), "registration", sip_outbound_registration_alloc, NULL, sip_outbound_registration_apply)) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "type", "", OPT_NOOP_T, 0, 0);
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, server_uri));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport));
+	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();
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int reload_module(void)
+{
+	ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
+	sip_outbound_registration_perform_all();
+	return 0;
+}
+
+static int unload_module(void)
+{
+	return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP Outbound Registration Support",
+		.load = load_module,
+		.reload = reload_module,
+		.unload = unload_module,
+		.load_pri = AST_MODPRI_APP_DEPEND,
+	       );

Propchange: team/group/pimp_my_sip/res/res_sip_outbound_registration.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/pimp_my_sip/res/res_sip_outbound_registration.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/pimp_my_sip/res/res_sip_outbound_registration.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/group/pimp_my_sip/res/res_sip_registrar.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip_registrar.c?view=auto&rev=383707
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_registrar.c (added)
+++ team/group/pimp_my_sip/res/res_sip_registrar.c Mon Mar 25 09:08:15 2013
@@ -1,0 +1,382 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_ua.h>
+
+#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 Structure used for finding contact */
+struct registrar_contact_details {
+	/*! \brief Pool used for parsing URI */
+	pj_pool_t *pool;
+	/*! \brief URI being looked for */
+	pjsip_uri *uri;
+};
+
+/*! \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 struct registrar_contact_details *details = arg;
+	pjsip_uri *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 | 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, 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;
+	struct registrar_contact_details details = {
+		.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256),
+	};
+
+	if (!details.pool) {
+		return -1;
+	}
+
+	while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) {
+		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) {
+				pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
+				return -1;
+			}
+		} 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;
+
+		if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
+			continue;
+		}
+
+		details.uri = pjsip_uri_get_uri(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, &details))) {
+			if (expiration) {
+				(*added)++;
+			}
+		} else if (expiration) {

[... 276 lines stripped ...]



More information about the asterisk-commits mailing list