[asterisk-commits] file: branch file/pimp_sip_registration r382785 - /team/file/pimp_sip_registr...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Mar 11 07:23:27 CDT 2013


Author: file
Date: Mon Mar 11 07:23:24 2013
New Revision: 382785

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=382785
Log:
Add work so far on outbound registration.

Added:
    team/file/pimp_sip_registration/res/res_sip_outbound_registration.c   (with props)

Added: team/file/pimp_sip_registration/res/res_sip_outbound_registration.c
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_registration/res/res_sip_outbound_registration.c?view=auto&rev=382785
==============================================================================
--- team/file/pimp_sip_registration/res/res_sip_outbound_registration.c (added)
+++ team/file/pimp_sip_registration/res/res_sip_outbound_registration.c Mon Mar 11 07:23:24 2013
@@ -1,0 +1,387 @@
+/*
+ * 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"
+
+#undef bzero
+#define bzero bzero
+#include <pjsip.h>
+#include <pjsip_ua.h>
+
+#include "asterisk/res_sip.h"
+#include "asterisk/module.h"
+
+/*! \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 Outbound registration state information */
+struct sip_outbound_registration_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 Interval at which retries should occur for temporal responses */
+	unsigned int retry_interval;
+};
+
+/*! \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 Outbound registration state */
+	struct sip_outbound_registration_state *state;
+};
+
+/*! \brief Callback function for outbound registration attempt */
+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);
+}
+
+/*! \brief Callback function for outbound registration client */
+static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
+{
+	struct sip_outbound_registration_state *state = param->token;
+	pjsip_regc_info info;
+	pjsip_regc_get_info(param->regc, &info);
+
+	if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
+		/* Registration was accepted, yahoo! */
+		state->status = SIP_REGISTRATION_REGISTERED;
+	} 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);
+	}
+}
+
+/*! \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) {
+		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);
+}
+
+/*! \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) {
+		return NULL;
+	} else if ((pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state, sip_outbound_registration_response_cb, &state->client) != PJ_SUCCESS)) {
+		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;
+
+	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);
+}
+
+/*! \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 (applied->state) {
+		/* Registration state already exists, roll with it */
+		return 0;
+	} else 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;
+	} else 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, &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), &tmp, applied->outbound_proxy);
+		if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(applied->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, &route_set);
+	}
+
+	pj_cstr(&server_uri, applied->server_uri);
+
+	if (sip_dialog_create_contact(pjsip_regc_get_pool(applied->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, &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;
+	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;
+
+	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);
+
+	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 | OBJ_MULTIPLE, 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_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/file/pimp_sip_registration/res/res_sip_outbound_registration.c
------------------------------------------------------------------------------
    svn:eol-style = native

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

Propchange: team/file/pimp_sip_registration/res/res_sip_outbound_registration.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain




More information about the asterisk-commits mailing list