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

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Mar 15 08:46:59 CDT 2013


Author: file
Date: Fri Mar 15 08:46:54 2013
New Revision: 383209

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=383209
Log:
Add support for handling NAT.

This commit adds the following:
1. External signaling and media address capability at a transport level using localnet setting
2. ICE support to res_sip_sdp_audio
3. A force_rport option on endpoints (this defaults to on but is an option as some devices are incompatible with it)
4. A rewrite_contact option on endpoints which uses the source IP address/port
5. External media address capability at an endpoint level

(closes issue ASTERISK-21074)

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

Added:
    team/group/pimp_my_sip/res/res_sip_nat.c   (with props)
Modified:
    team/group/pimp_my_sip/include/asterisk/res_sip.h
    team/group/pimp_my_sip/include/asterisk/res_sip_session.h
    team/group/pimp_my_sip/res/res_sip/config_transport.c
    team/group/pimp_my_sip/res/res_sip/sip_configuration.c
    team/group/pimp_my_sip/res/res_sip/sip_distributor.c
    team/group/pimp_my_sip/res/res_sip_sdp_audio.c
    team/group/pimp_my_sip/res/res_sip_session.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=383209&r1=383208&r2=383209
==============================================================================
--- team/group/pimp_my_sip/include/asterisk/res_sip.h (original)
+++ team/group/pimp_my_sip/include/asterisk/res_sip.h Fri Mar 15 08:46:54 2013
@@ -28,6 +28,8 @@
 #include "asterisk/channel.h"
 /* Needed for ast_sorcery */
 #include "asterisk/sorcery.h"
+/* Needed for ast_dnsmgr */
+#include "asterisk/dnsmgr.h"
 /* Needed for pj_sockaddr */
 #include <pjlib.h>
 
@@ -92,6 +94,10 @@
 		AST_STRING_FIELD(privkey_file);
 		/*! Password to open the private key */
 		AST_STRING_FIELD(password);
+		/*! External signaling address */
+		AST_STRING_FIELD(external_signaling_address);
+		/*! External media address */
+		AST_STRING_FIELD(external_media_address);
 		);
 	/*! Type of transport */
 	enum ast_sip_transport_type type;
@@ -99,12 +105,30 @@
 	pj_sockaddr host;
 	/*! Number of simultaneous asynchronous operations */
 	unsigned int async_operations;
+	/*! Optional external port for signaling */
+	unsigned int external_signaling_port;
 	/*! TLS settings */
 	pjsip_tls_setting tls;
 	/*! Configured TLS ciphers */
 	pj_ssl_cipher ciphers[SIP_TLS_MAX_CIPHERS];
+	/*! Optional local network information, used for NAT purposes */
+	struct ast_ha *localnet;
+	/*! DNS manager for refreshing the external address */
+	struct ast_dnsmgr_entry *external_address_refresher;
+	/*! Optional external address information */
+	struct ast_sockaddr external_address;
 	/*! Transport state information */
 	struct ast_sip_transport_state *state;
+};
+
+/*!
+ * \brief Structure for SIP nat hook information
+ */
+struct ast_sip_nat_hook {
+	/*! Sorcery object details */
+	SORCERY_OBJECT(details);
+	/*! Callback for when a message is going outside of our local network */
+	void (*outgoing_external_message)(struct pjsip_tx_data *tdata, struct ast_sip_transport *transport);
 };
 
 /*!
@@ -227,6 +251,8 @@
 		AST_STRING_FIELD(aors);
                 /*! Musiconhold class to suggest that the other side use when placing on hold */
                 AST_STRING_FIELD(mohsuggest);
+		/*! Optional external media address to use in SDP */
+		AST_STRING_FIELD(external_media_address);       
 	);
 	/*! Identification information for this endpoint */
 	struct ast_party_id id;
@@ -248,8 +274,14 @@
 	unsigned int rtp_ipv6;
 	/*! Whether symmetric RTP is enabled or not */
 	unsigned int rtp_symmetric;
+	/*! Whether ICE support is enabled or not */
+	unsigned int ice_support;
 	/*! Whether to use the "ptime" attribute received from the endpoint or not */
 	unsigned int use_ptime;
+	/*! Whether to force using the source IP address/port for sending responses */
+	unsigned int force_rport;
+	/*! Whether to rewrite the Contact header with the source IP address/port or not */
+	unsigned int rewrite_contact;
 	/*! Enabled SIP extensions */
 	unsigned int extensions;
 	/*! Minimum session expiration period, in seconds */

Modified: team/group/pimp_my_sip/include/asterisk/res_sip_session.h
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/include/asterisk/res_sip_session.h?view=diff&rev=383209&r1=383208&r2=383209
==============================================================================
--- team/group/pimp_my_sip/include/asterisk/res_sip_session.h (original)
+++ team/group/pimp_my_sip/include/asterisk/res_sip_session.h Fri Mar 15 08:46:54 2013
@@ -25,6 +25,7 @@
 
 /* Forward declarations */
 struct ast_sip_endpoint;
+struct ast_sip_transport;
 struct pjsip_inv_session;
 struct ast_channel;
 struct ast_datastore;
@@ -189,6 +190,13 @@
 	 */
 	int (*create_outgoing_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct pjmedia_sdp_session *sdp);
 	/*!
+	 * \brief Update media stream with external address if applicable
+	 * \param tdata The outgoing message itself
+	 * \param stream The stream on which to operate
+	 * \param transport The transport the SDP is going out on
+	 */
+	void (*change_outgoing_sdp_stream_media_address)(struct pjsip_tx_data *tdata, struct pjmedia_sdp_media *stream, struct ast_sip_transport *transport);
+	/*!
 	 * \brief Apply a negotiated SDP media stream
 	 * \param session The session for which media is being applied
 	 * \param session_media The media to be setup for this session

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=383209&r1=383208&r2=383209
==============================================================================
--- team/group/pimp_my_sip/res/res_sip/config_transport.c (original)
+++ team/group/pimp_my_sip/res/res_sip/config_transport.c Fri Mar 15 08:46:54 2013
@@ -25,6 +25,7 @@
 #include "asterisk/logger.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/sorcery.h"
+#include "asterisk/acl.h"
 
 static int destroy_transport_state(void *data)
 {
@@ -49,6 +50,11 @@
 	struct ast_sip_transport *transport = obj;
 
 	ast_string_field_free_memory(transport);
+	ast_free_ha(transport->localnet);
+
+	if (transport->external_address_refresher) {
+		ast_dnsmgr_release(transport->external_address_refresher);
+	}
 
 	ao2_cleanup(transport->state);
 }
@@ -98,6 +104,24 @@
 	/* Set default port if not present */
 	if (!pj_sockaddr_get_port(&transport->host)) {
 		pj_sockaddr_set_port(&transport->host, (transport->type == AST_SIP_TRANSPORT_TLS) ? 5061 : 5060);
+	}
+
+	/* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */
+	if (!ast_strlen_zero(transport->external_signaling_address)) {
+		if (transport->host.addr.sa_family == pj_AF_INET()) {
+			transport->external_address.ss.ss_family = AF_INET;
+		} else if (transport->host.addr.sa_family == pj_AF_INET6()) {
+			transport->external_address.ss.ss_family = AF_INET6;
+		} else {
+			ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
+					ast_sorcery_object_get_id(obj));
+			return -1;
+		}
+
+		if (ast_dnsmgr_lookup(transport->external_signaling_address, &transport->external_address, &transport->external_address_refresher, NULL) < 0) {
+			ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", ast_sorcery_object_get_id(obj));
+			return -1;
+		}
 	}
 
 	if (transport->type == AST_SIP_TRANSPORT_UDP) {
@@ -230,6 +254,19 @@
 	}
 }
 
+/*! \brief Custom handler for localnet setting */
+static int transport_localnet_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	struct ast_sip_transport *transport = obj;
+	int error = 0;
+
+	if (!(transport->localnet = ast_append_ha("d", var->value, transport->localnet, &error))) {
+		return -1;
+	}
+
+	return error;
+}
+
 /*! \brief Initialize sorcery with transport support */
 int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery)
 {
@@ -247,11 +284,15 @@
 	ast_sorcery_object_field_register(sorcery, "transport", "cert_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, cert_file));
 	ast_sorcery_object_field_register(sorcery, "transport", "privkey_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, privkey_file));
 	ast_sorcery_object_field_register(sorcery, "transport", "password", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, password));
+	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_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);
 	ast_sorcery_object_field_register_custom(sorcery, "transport", "method", "", transport_tls_method_handler, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sorcery, "transport", "cipher", "", transport_tls_cipher_handler, NULL, 0, 0);
-
-	return 0;
-}
+	ast_sorcery_object_field_register_custom(sorcery, "transport", "localnet", "", transport_localnet_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=383209&r1=383208&r2=383209
==============================================================================
--- team/group/pimp_my_sip/res/res_sip/sip_configuration.c (original)
+++ team/group/pimp_my_sip/res/res_sip/sip_configuration.c Fri Mar 15 08:46:54 2013
@@ -176,6 +176,11 @@
 	return 0;
 }
 
+static void *sip_nat_hook_alloc(const char *name)
+{
+	return ao2_alloc(sizeof(struct ast_sip_nat_hook), NULL);
+}
+
 int ast_res_sip_initialize_configuration(void)
 {
 	if (ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands))) {
@@ -198,12 +203,16 @@
 
 	ast_sorcery_apply_default(sip_sorcery, "endpoint", "config", "res_sip.conf,criteria=type=endpoint");
 
+	ast_sorcery_apply_default(sip_sorcery, "nat_hook", "memory", NULL);
+
 	if (ast_sorcery_object_register(sip_sorcery, "endpoint", ast_sip_endpoint_alloc, NULL, NULL)) {
 		ast_log(LOG_ERROR, "Failed to register SIP endpoint object with sorcery\n");
 		ast_sorcery_unref(sip_sorcery);
 		sip_sorcery = NULL;
 		return -1;
 	}
+
+	ast_sorcery_object_register(sip_sorcery, "nat_hook", sip_nat_hook_alloc, NULL, NULL);
 
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "context", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, context));
@@ -213,7 +222,10 @@
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtmfmode", "rfc4733", dtmf_handler, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtp_ipv6));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_symmetric", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtp_symmetric));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ice_support", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, ice_support));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_ptime", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, use_ptime));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "force_rport", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, force_rport));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rewrite_contact", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rewrite_contact));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_proxy));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mohsuggest", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mohsuggest));
@@ -223,6 +235,7 @@
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_sess_expires", "1800", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, sess_expires));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aors", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, aors));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "auth", "", auth_handler, NULL, 0, 0);
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "external_media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, external_media_address));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username,location", ident_handler, NULL, 0, 0);
 
 	if (ast_sip_initialize_sorcery_transport(sip_sorcery)) {

Modified: team/group/pimp_my_sip/res/res_sip/sip_distributor.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip/sip_distributor.c?view=diff&rev=383209&r1=383208&r2=383209
==============================================================================
--- team/group/pimp_my_sip/res/res_sip/sip_distributor.c (original)
+++ team/group/pimp_my_sip/res/res_sip/sip_distributor.c Fri Mar 15 08:46:54 2013
@@ -27,7 +27,7 @@
 
 static pjsip_module distributor_mod = {
 	.name = {"Request Distributor", 19},
-	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 1,
+	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 6,
 	.on_rx_request = distributor,
 	.on_rx_response = distributor,
 };
@@ -63,7 +63,7 @@
 
 static pjsip_module endpoint_mod = {
 	.name = {"Endpoint Identifier", 19},
-	.priority = PJSIP_MOD_PRIORITY_APPLICATION - 2,
+	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 3,
 	.on_rx_request = endpoint_lookup,
 };
 

Added: team/group/pimp_my_sip/res/res_sip_nat.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip_nat.c?view=auto&rev=383209
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_nat.c (added)
+++ team/group/pimp_my_sip/res/res_sip_nat.c Fri Mar 15 08:46:54 2013
@@ -1,0 +1,237 @@
+/*
+ * 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"
+#include "asterisk/acl.h"
+
+static pj_bool_t nat_on_rx_request(pjsip_rx_data *rdata)
+{
+	RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
+	pjsip_contact_hdr *contact;
+
+	if (!endpoint) {
+		return PJ_FALSE;
+	}
+
+	if (endpoint->rewrite_contact && (contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) &&
+		(PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
+		pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
+
+		pj_cstr(&uri->host, rdata->pkt_info.src_name);
+		uri->port = rdata->pkt_info.src_port;
+	}
+
+	if (endpoint->force_rport) {
+		rdata->msg_info.via->rport_param = 0;
+	}
+
+	return PJ_FALSE;
+}
+
+/*! \brief Structure which contains information about a transport */
+struct request_transport_details {
+	/*! \brief Type of transport */
+	enum ast_sip_transport_type type;
+	/*! \brief Potential pointer to the transport itself, if UDP */
+	pjsip_transport *transport;
+	/*! \brief Potential pointer to the transport factory itself, if TCP/TLS */
+	pjsip_tpfactory *factory;
+	/*! \brief Local address for transport */
+	pj_str_t local_address;
+	/*! \brief Local port for transport */
+	int local_port;
+};
+
+/*! \brief Callback function for finding the transport the request is going out on */
+static int find_transport_in_use(void *obj, void *arg, int flags)
+{
+	struct ast_sip_transport *transport = obj;
+	struct request_transport_details *details = arg;
+
+	/* If an explicit transport or factory matches then this is what is in use, if we are unavailable
+	 * to compare based on that we make sure that the type is the same and the source IP address/port are the same
+	 */
+	if ((details->transport && details->transport == transport->state->transport) ||
+		(details->factory && details->factory == transport->state->factory) ||
+		((details->type == transport->type) && (transport->state->factory) &&
+			!pj_strcmp(&transport->state->factory->addr_name.host, &details->local_address) &&
+			transport->state->factory->addr_name.port == details->local_port)) {
+		return CMP_MATCH | CMP_STOP;
+	}
+
+	return 0;
+}
+
+/*! \brief Helper function which returns the SIP URI of a Contact header */
+static pjsip_sip_uri *nat_get_contact_sip_uri(pjsip_tx_data *tdata)
+{
+	pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
+
+	if (!contact || (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
+		return NULL;
+	}
+
+	return pjsip_uri_get_uri(contact->uri);
+}
+
+/*! \brief Structure which contains hook details */
+struct nat_hook_details {
+	/*! \brief Outgoing message itself */
+	pjsip_tx_data *tdata;
+	/*! \brief Chosen transport */
+	struct ast_sip_transport *transport;
+};
+
+/*! \brief Callback function for invoking hooks */
+static int nat_invoke_hook(void *obj, void *arg, int flags)
+{
+	struct ast_sip_nat_hook *hook = obj;
+	struct nat_hook_details *details = arg;
+
+	if (hook->outgoing_external_message) {
+		hook->outgoing_external_message(details->tdata, details->transport);
+	}
+
+	return 0;
+}
+
+static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
+{
+	RAII_VAR(struct ao2_container *, transports, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
+	struct request_transport_details details = { 0, };
+	pjsip_via_hdr *via = NULL;
+	struct ast_sockaddr addr = { { 0, } };
+	pjsip_sip_uri *uri = NULL;
+	RAII_VAR(struct ao2_container *, hooks, NULL, ao2_cleanup);
+
+	/* If a transport selector is in use we know the transport or factory, so explicitly find it */
+	if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) {
+		details.transport = tdata->tp_sel.u.transport;
+	} else if (tdata->tp_sel.type == PJSIP_TPSELECTOR_LISTENER) {
+		details.factory = tdata->tp_sel.u.listener;
+	} else if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP || tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
+		/* Connectionless uses the same transport for all requests */
+		details.type = AST_SIP_TRANSPORT_UDP;
+		details.transport = tdata->tp_info.transport;
+	} else {
+		if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_TCP) {
+			details.type = AST_SIP_TRANSPORT_TCP;
+		} else if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_TLS) {
+			details.type = AST_SIP_TRANSPORT_TLS;
+		} else {
+			/* Unknown transport type, we can't map and thus can't apply NAT changes */
+			return PJ_SUCCESS;
+		}
+
+		if ((uri = nat_get_contact_sip_uri(tdata))) {
+			details.local_address = uri->host;
+			details.local_port = uri->port;
+		} else if ((tdata->msg->type == PJSIP_REQUEST_MSG) &&
+			(via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) {
+			details.local_address = via->sent_by.host;
+			details.local_port = via->sent_by.port;
+		} else {
+			return PJ_SUCCESS;
+		}
+
+		if (!details.local_port) {
+			details.local_port = (details.type == AST_SIP_TRANSPORT_TLS) ? 5061 : 5060;
+		}
+	}
+
+	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, &details)) || !transport->localnet ||
+		ast_sockaddr_isnull(&transport->external_address)) {
+		return PJ_SUCCESS;
+	}
+
+	ast_sockaddr_parse(&addr, tdata->tp_info.dst_name, PARSE_PORT_FORBID);
+	ast_sockaddr_set_port(&addr, tdata->tp_info.dst_port);
+
+	/* See if where we are sending this request is local or not, and if not that we can get a Contact URI to modify */
+	if (ast_apply_ha(transport->localnet, &addr) != AST_SENSE_ALLOW) {
+		return PJ_SUCCESS;
+	}
+
+	/* Update the contact header with the external address */
+	if (uri || (uri = nat_get_contact_sip_uri(tdata))) {
+		pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport->external_address));
+		if (transport->external_signaling_port) {
+			uri->port = transport->external_signaling_port;
+		}
+	}
+
+	/* Update the via header if relevant */
+	if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via || (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL)))) {
+		pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport->external_address));
+		if (transport->external_signaling_port) {
+			via->sent_by.port = transport->external_signaling_port;
+		}
+	}
+
+	/* Invoke any additional hooks that may be registered */
+	if ((hooks = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "nat_hook", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
+		struct nat_hook_details hook_details = {
+			.tdata = tdata,
+			.transport = transport,
+		};
+		ao2_callback(hooks, 0, nat_invoke_hook, &hook_details);
+	}
+
+	return PJ_SUCCESS;
+}
+
+static pjsip_module nat_module = {
+	.name = { "NAT", 3 },
+	.id = -1,
+	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,
+	.on_rx_request = nat_on_rx_request,
+	.on_tx_request = nat_on_tx_message,
+	.on_tx_response = nat_on_tx_message,
+};
+
+static int load_module(void)
+{
+	ast_sip_register_service(&nat_module);
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+	ast_sip_unregister_service(&nat_module);
+	return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP NAT Support",
+		.load = load_module,
+		.unload = unload_module,
+		.load_pri = AST_MODPRI_APP_DEPEND,
+	       );

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

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

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

Modified: team/group/pimp_my_sip/res/res_sip_sdp_audio.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip_sdp_audio.c?view=diff&rev=383209&r1=383208&r2=383209
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_sdp_audio.c (original)
+++ team/group/pimp_my_sip/res/res_sip_sdp_audio.c Fri Mar 15 08:46:54 2013
@@ -45,18 +45,26 @@
 #include "asterisk/channel.h"
 #include "asterisk/causes.h"
 #include "asterisk/sched.h"
+#include "asterisk/acl.h"
 
 #include "asterisk/res_sip.h"
 #include "asterisk/res_sip_session.h"
 
 /*! \brief Scheduler for RTCP purposes */
 static struct ast_sched_context *sched;
+
+/*! \brief Address for IPv4 RTP */
+static struct ast_sockaddr address_ipv4;
+
+/*! \brief Address for IPv6 RTP */
+static struct ast_sockaddr address_ipv6;
 
 /*! \brief Forward declarations for SDP handler functions */
 static int audio_negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream);
 static int audio_create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct pjmedia_sdp_session *sdp);
 static int audio_apply_negotiated_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,
 	const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream);
+static void audio_change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struct pjmedia_sdp_media *stream, struct ast_sip_transport *transport);
 
 /*! \brief Forward declaration for session supplement functions */
 static void audio_stream_destroy(struct ast_sip_session_media *session_media);
@@ -67,27 +75,16 @@
 	.negotiate_incoming_sdp_stream = audio_negotiate_incoming_sdp_stream,
 	.create_outgoing_sdp_stream = audio_create_outgoing_sdp_stream,
 	.apply_negotiated_sdp_stream = audio_apply_negotiated_sdp_stream,
+	.change_outgoing_sdp_stream_media_address = audio_change_outgoing_sdp_stream_media_address,
 	.stream_destroy = audio_stream_destroy,
 };
 
 /*! \brief Internal function which creates an RTP instance */
 static int audio_create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media, unsigned int ipv6)
 {
-	pj_sockaddr addr;
-	char hostip[PJ_INET6_ADDRSTRLEN+2];
-	struct ast_sockaddr tmp;
-
-	if (pj_gethostip(ipv6 ? pj_AF_INET6() : pj_AF_INET(), &addr) != PJ_SUCCESS) {
-		return -1;
-	}
-
-	pj_sockaddr_print(&addr, hostip, sizeof(hostip), 2);
-
-	if (!ast_sockaddr_parse(&tmp, hostip, 0)) {
-		return -1;
-	}
-
-	if (!(session_media->rtp = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
+	struct ast_rtp_engine_ice *ice;
+
+	if (!(session_media->rtp = ast_rtp_instance_new("asterisk", sched, ipv6 ? &address_ipv6 : &address_ipv4, NULL))) {
 		return -1;
 	}
 
@@ -96,6 +93,10 @@
 
 	ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session_media->rtp),
 					 session_media->rtp, &session->endpoint->prefs);
+
+	if (!session->endpoint->ice_support && (ice = ast_rtp_instance_get_ice(session_media->rtp))) {
+		ice->stop(session_media->rtp);
+	}
 
 	return 0;
 }
@@ -126,6 +127,66 @@
 
 	/* pjmedia takes care of the formats and such */
 	return 1;
+}
+
+/*! \brief Function which adds ICE attributes to an 'audio' stream */
+static void audio_add_ice_to_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media)
+{
+	struct ast_rtp_engine_ice *ice;
+	struct ao2_container *candidates;
+	const char *username, *password;
+	pj_str_t stmp;
+	pjmedia_sdp_attr *attr;
+	struct ao2_iterator it_candidates;
+	struct ast_rtp_engine_ice_candidate *candidate;
+
+	if (!session->endpoint->ice_support || !(ice = ast_rtp_instance_get_ice(session_media->rtp)) ||
+		!(candidates = ice->get_local_candidates(session_media->rtp))) {
+		return;
+	}
+
+	if ((username = ice->get_ufrag(session_media->rtp))) {
+		attr = pjmedia_sdp_attr_create(pool, "ice-ufrag", pj_cstr(&stmp, username));
+		media->attr[media->attr_count++] = attr;
+	}
+
+	if ((password = ice->get_password(session_media->rtp))) {
+		attr = pjmedia_sdp_attr_create(pool, "ice-pwd", pj_cstr(&stmp, password));
+		media->attr[media->attr_count++] = attr;
+	}
+
+	it_candidates = ao2_iterator_init(candidates, 0);
+	for (; (candidate = ao2_iterator_next(&it_candidates)); ao2_ref(candidate, -1)) {
+		struct ast_str *attr_candidate = ast_str_create(128);
+
+		ast_str_set(&attr_candidate, -1, "%s %d %s %d %s ", candidate->foundation, candidate->id, candidate->transport,
+					candidate->priority, ast_sockaddr_stringify_host(&candidate->address));
+		ast_str_append(&attr_candidate, -1, "%s typ ", ast_sockaddr_stringify_port(&candidate->address));
+
+		switch (candidate->type) {
+			case AST_RTP_ICE_CANDIDATE_TYPE_HOST:
+				ast_str_append(&attr_candidate, -1, "host");
+				break;
+			case AST_RTP_ICE_CANDIDATE_TYPE_SRFLX:
+				ast_str_append(&attr_candidate, -1, "srflx");
+				break;
+			case AST_RTP_ICE_CANDIDATE_TYPE_RELAYED:
+				ast_str_append(&attr_candidate, -1, "relay");
+				break;
+		}
+
+		if (!ast_sockaddr_isnull(&candidate->relay_address)) {
+			ast_str_append(&attr_candidate, -1, " raddr %s rport ", ast_sockaddr_stringify_host(&candidate->relay_address));
+			ast_str_append(&attr_candidate, -1, " %s", ast_sockaddr_stringify_port(&candidate->relay_address));
+		}
+
+		attr = pjmedia_sdp_attr_create(pool, "candidate", pj_cstr(&stmp, ast_str_buffer(attr_candidate)));
+		media->attr[media->attr_count++] = attr;
+
+		ast_free(attr_candidate);
+	}
+
+	ao2_iterator_destroy(&it_candidates);
 }
 
 /*! \brief Function which creates an outgoing 'audio' stream */
@@ -139,8 +200,9 @@
 	static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 };
 	static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
 	pjmedia_sdp_media *media;
+	char hostip[PJ_INET6_ADDRSTRLEN+2];
 	struct ast_sockaddr addr;
-	char tmp[32];
+	char tmp[512];
 	pj_str_t stmp;
 	pjmedia_sdp_attr *attr;
 	int index = 0, min_packet_size = 0, noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733) ? AST_RTP_DTMF : 0;
@@ -153,7 +215,7 @@
 	}
 
 	if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) ||
-	    !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) {
+		!(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) {
 		return -1;
 	}
 
@@ -162,12 +224,26 @@
 	media->desc.transport = STR_RTP_AVP;
 
 	/* Add connection level details */
+	if (ast_strlen_zero(session->endpoint->external_media_address)) {
+		pj_sockaddr localaddr;
+
+		if (pj_gethostip(session->endpoint->rtp_ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr)) {
+			return -1;
+		}
+		pj_sockaddr_print(&localaddr, hostip, sizeof(hostip), 2);
+	} else {
+		ast_copy_string(hostip, session->endpoint->external_media_address, sizeof(hostip));
+	}
+
+	media->conn->net_type = STR_IN;
+	media->conn->addr_type = session->endpoint->rtp_ipv6 ? STR_IP6 : STR_IP4;
+	pj_strdup2(pool, &media->conn->addr, hostip);
 	ast_rtp_instance_get_local_address(session_media->rtp, &addr);
-	media->conn->net_type = STR_IN;
-	media->conn->addr_type = (ast_sockaddr_is_ipv6(&addr) && !ast_sockaddr_is_ipv4_mapped(&addr)) ? STR_IP6 : STR_IP4;
-	pj_strdup2(pool, &media->conn->addr, ast_sockaddr_stringify_addr_remote(&addr));
 	media->desc.port = (pj_uint16_t) ast_sockaddr_port(&addr);
 	media->desc.port_count = 1;
+
+	/* Add ICE attributes and candidates */
+	audio_add_ice_to_stream(session, session_media, pool, media);
 
 	/* Add formats */
 	for (index = 0; (index < AST_CODEC_PREF_SIZE); index++) {
@@ -246,6 +322,84 @@
 	sdp->media[sdp->media_count++] = media;
 
 	return 1;
+}
+
+/*! \brief Function which processes ICE attributes in an audio stream */
+static void audio_process_ice_attributes(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream)
+{
+	struct ast_rtp_engine_ice *ice;
+	const pjmedia_sdp_attr *attr;
+	char attr_value[256];
+	unsigned int attr_i;
+
+	/* If ICE support is not enabled or available exit early */
+	if (!session->endpoint->ice_support || !(ice = ast_rtp_instance_get_ice(session_media->rtp))) {
+		return;
+	}
+
+	if ((attr = pjmedia_sdp_media_find_attr2(remote_stream, "ice-ufrag", NULL))) {
+		ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value));
+		ice->set_authentication(session_media->rtp, attr_value, NULL);
+	}
+
+	if ((attr = pjmedia_sdp_media_find_attr2(remote_stream, "ice-pwd", NULL))) {
+		ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value));
+		ice->set_authentication(session_media->rtp, NULL, attr_value);
+	}
+
+	if (pjmedia_sdp_media_find_attr2(remote_stream, "ice-lite", NULL)) {
+		ice->ice_lite(session_media->rtp);
+	}
+
+	/* Find all of the candidates */
+	for (attr_i = 0; attr_i < remote_stream->attr_count; ++attr_i) {
+		char foundation[32], transport[32], address[PJ_INET6_ADDRSTRLEN + 1], cand_type[6], relay_address[PJ_INET6_ADDRSTRLEN + 1] = "";
+		int port, relay_port = 0;
+		struct ast_rtp_engine_ice_candidate candidate = { 0, };
+
+		attr = remote_stream->attr[attr_i];
+
+		/* If this is not a candidate line skip it */
+		if (pj_strcmp2(&attr->name, "candidate")) {
+			continue;
+		}
+
+		ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value));
+
+		if (sscanf(attr_value, "%31s %30u %31s %30u %46s %30u typ %5s %*s %23s %*s %30u", foundation, &candidate.id, transport,
+			&candidate.priority, address, &port, cand_type, relay_address, &relay_port) < 7) {
+			/* Candidate did not parse properly */
+			continue;
+		}
+
+		candidate.foundation = foundation;
+		candidate.transport = transport;
+
+		ast_sockaddr_parse(&candidate.address, address, PARSE_PORT_FORBID);
+		ast_sockaddr_set_port(&candidate.address, port);
+
+		if (!strcasecmp(cand_type, "host")) {
+			candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_HOST;
+		} else if (!strcasecmp(cand_type, "srflx")) {
+			candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX;
+		} else if (!strcasecmp(cand_type, "relay")) {
+			candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED;
+		} else {
+			continue;
+		}
+
+		if (!ast_strlen_zero(relay_address)) {
+			ast_sockaddr_parse(&candidate.relay_address, relay_address, PARSE_PORT_FORBID);
+		}
+
+		if (relay_port) {
+			ast_sockaddr_set_port(&candidate.relay_address, relay_port);
+		}
+
+		ice->add_remote_candidate(session_media->rtp, &candidate);
+	}
+
+	ice->start(session_media->rtp);
 }
 
 /*! \brief Function which applies a negotiated SDP stream */
@@ -281,7 +435,7 @@
 
 	/* To properly apply formats to the channel we need to keep track of capabilities */
 	if (!(cap = ast_format_cap_alloc_nolock()) ||
-	    !(peercap = ast_format_cap_alloc_nolock())) {
+		!(peercap = ast_format_cap_alloc_nolock())) {
 		ast_log(LOG_ERROR, "Failed to allocate audio capabilities\n");
 		return -1;
 	}
@@ -307,7 +461,7 @@
 
 				ast_copy_pj_str(name, &rtpmap->enc_name, sizeof(name));
 				ast_rtp_codecs_payloads_set_rtpmap_type_rate(&codecs, NULL, pj_strtoul(&local_stream->desc.fmt[format]),
-									     "audio", name, 0, rtpmap->clock_rate);
+										 "audio", name, 0, rtpmap->clock_rate);
 			}
 		}
 	}
@@ -370,6 +524,9 @@
 
 	ast_channel_set_fd(session->channel, 0, ast_rtp_instance_fd(session_media->rtp, 0));
 	ast_channel_set_fd(session->channel, 1, ast_rtp_instance_fd(session_media->rtp, 1));
+
+	/* If ICE support is enabled find all the needed attributes */
+	audio_process_ice_attributes(session, session_media, remote, remote_stream);
 
 	if (session_media->held && (!ast_sockaddr_isnull(addrs) ||
 							  !pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL))) {
@@ -380,7 +537,7 @@
 	} else if (ast_sockaddr_isnull(addrs) || ast_sockaddr_is_any(addrs) || pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL)) {
 		/* The remote side has put us on hold */
 		ast_queue_control_data(session->channel, AST_CONTROL_HOLD, S_OR(session->endpoint->mohsuggest, NULL),
-				       !ast_strlen_zero(session->endpoint->mohsuggest) ? strlen(session->endpoint->mohsuggest) + 1 : 0);
+					   !ast_strlen_zero(session->endpoint->mohsuggest) ? strlen(session->endpoint->mohsuggest) + 1 : 0);
 		ast_rtp_instance_stop(session_media->rtp);
 		ast_queue_frame(session->channel, &ast_null_frame);
 		session_media->held = 1;
@@ -391,6 +548,23 @@
 
 	ast_rtp_codecs_payloads_destroy(&codecs);
 	return 1;
+}
+
+/*! \brief Function which updates the media stream with external media address, if applicable */
+static void audio_change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struct pjmedia_sdp_media *stream, struct ast_sip_transport *transport)
+{
+	char host[NI_MAXHOST];
+	struct ast_sockaddr addr = { { 0, } };
+
+	ast_copy_pj_str(host, &stream->conn->addr, sizeof(host));
+	ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID);
+
+	/* Is the address within the SDP inside the same network? */
+	if (ast_apply_ha(transport->localnet, &addr) == AST_SENSE_ALLOW) {
+		return;
+	}
+
+	pj_strdup2(tdata->pool, &stream->conn->addr, transport->external_media_address);
 }
 
 /*! \brief Function which destroys the audio RTP instance when session ends */
@@ -416,6 +590,9 @@
  */
 static int load_module(void)
 {
+	ast_sockaddr_parse(&address_ipv4, "0.0.0.0", 0);
+	ast_sockaddr_parse(&address_ipv6, "::", 0);
+
 	if (!(sched = ast_sched_context_create())) {
 		ast_log(LOG_ERROR, "Unable to create scheduler context.\n");
 		goto end;
@@ -452,4 +629,4 @@
 		.load = load_module,
 		.unload = unload_module,
 		.load_pri = AST_MODPRI_CHANNEL_DRIVER,
-	       );
+		   );

Modified: team/group/pimp_my_sip/res/res_sip_session.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip_session.c?view=diff&rev=383209&r1=383208&r2=383209
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_session.c (original)
+++ team/group/pimp_my_sip/res/res_sip_session.c Fri Mar 15 08:46:54 2013
@@ -49,6 +49,9 @@
 static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata);
 static void handle_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata);
 static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdata);
+
+/*! \brief NAT hook for modifying outgoing messages with SDP */
+static struct ast_sip_nat_hook *nat_hook;
 
 /*!
  * \brief Registered SDP stream handlers
@@ -1353,9 +1356,54 @@
 	.on_redirected = session_inv_on_redirected,
 };
 
+/*! \brief Hook for modifying outgoing messages with SDP to contain the proper address information */
+static void session_outgoing_nat_hook(pjsip_tx_data *tdata, struct ast_sip_transport *transport)
+{
+	struct ast_sip_nat_hook *hook = tdata->mod_data[session_module.id];
+	struct pjmedia_sdp_session *sdp;
+	int stream;
+
+	/* SDP produced by us directly will never be multipart */
+	if (hook || !tdata->msg->body || pj_stricmp2(&tdata->msg->body->content_type.type, "application") ||
+		pj_stricmp2(&tdata->msg->body->content_type.subtype, "sdp") || ast_strlen_zero(transport->external_media_address)) {
+		return;
+	}
+
+	sdp = tdata->msg->body->data;
+
+	for (stream = 0; stream < sdp->media_count; ++stream) {
+		/* See if there are registered handlers for this media stream type */
+		char media[20];
+		struct ast_sip_session_sdp_handler *handler;
+		RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup);
+
+		/* We need a null-terminated version of the media string */
+		ast_copy_pj_str(media, &sdp->media[stream]->desc.media, sizeof(media));
+
+		handler_list = ao2_find(sdp_handlers, media, OBJ_KEY);
+		if (!handler_list) {
+			ast_debug(1, "No registered SDP handlers for media type '%s'\n", media);
+			continue;
+		}
+		AST_LIST_TRAVERSE(&handler_list->list, handler, next) {
+			if (handler->change_outgoing_sdp_stream_media_address) {

[... 32 lines stripped ...]



More information about the asterisk-commits mailing list