[svn-commits] file: branch file/pimp_sip_nat r381845 - in /team/file/pimp_sip_nat: include/...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Feb 21 08:37:49 CST 2013


Author: file
Date: Thu Feb 21 08:37:45 2013
New Revision: 381845

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=381845
Log:
Add support for specifying localnet and external signaling address information on a per-transport basis.

This also adds support for specifying a media address on a per-endpoint basis.

Modified:
    team/file/pimp_sip_nat/include/asterisk/res_sip.h
    team/file/pimp_sip_nat/res/res_sip/config_transport.c
    team/file/pimp_sip_nat/res/res_sip/sip_configuration.c
    team/file/pimp_sip_nat/res/res_sip_nat.c
    team/file/pimp_sip_nat/res/res_sip_sdp_audio.c

Modified: team/file/pimp_sip_nat/include/asterisk/res_sip.h
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_nat/include/asterisk/res_sip.h?view=diff&rev=381845&r1=381844&r2=381845
==============================================================================
--- team/file/pimp_sip_nat/include/asterisk/res_sip.h (original)
+++ team/file/pimp_sip_nat/include/asterisk/res_sip.h Thu Feb 21 08:37:45 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"
 
@@ -96,6 +98,8 @@
 		AST_STRING_FIELD(privkey_file);
 		/*! Password to open the private key */
 		AST_STRING_FIELD(password);
+		/*! External signaling address */
+		AST_STRING_FIELD(external_signaling_address);
 		);
 	/*! Type of transport */
 	enum ast_sip_transport_type type;
@@ -103,10 +107,18 @@
 	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;
 };
@@ -218,8 +230,10 @@
 		AST_STRING_FIELD(context);
 		/*! Name of an explicit transport to use */
 		AST_STRING_FIELD(transport);
-                /*! Musiconhold class to suggest that the other side use when placing on hold */
-                AST_STRING_FIELD(mohsuggest);
+		/*! 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;

Modified: team/file/pimp_sip_nat/res/res_sip/config_transport.c
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_nat/res/res_sip/config_transport.c?view=diff&rev=381845&r1=381844&r2=381845
==============================================================================
--- team/file/pimp_sip_nat/res/res_sip/config_transport.c (original)
+++ team/file/pimp_sip_nat/res/res_sip/config_transport.c Thu Feb 21 08:37:45 2013
@@ -26,6 +26,7 @@
 #include "asterisk/logger.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/sorcery.h"
+#include "asterisk/acl.h"
 
 static int destroy_transport_state(void *data)
 {
@@ -55,6 +56,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);
 }
@@ -104,6 +110,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;
+		}
 	}
 
 	/* TODO: Upgrade pjproject so we get IPv6 TCP and TLS */
@@ -235,6 +259,22 @@
 	}
 }
 
+/*! \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;
+	struct ast_ha *ha;
+	int error = 0;
+
+	if (!(ha = ast_append_ha("d", var->value, transport->localnet, &error))) {
+		return -1;
+	}
+
+	transport->localnet = ha;
+
+	return error;
+}
+
 /*! \brief Initialize sorcery with transport support */
 int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery)
 {
@@ -252,11 +292,14 @@
 	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, 0, FLDSET(struct ast_sip_transport, external_signaling_port));
 	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/file/pimp_sip_nat/res/res_sip/sip_configuration.c
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_nat/res/res_sip/sip_configuration.c?view=diff&rev=381845&r1=381844&r2=381845
==============================================================================
--- team/file/pimp_sip_nat/res/res_sip/sip_configuration.c (original)
+++ team/file/pimp_sip_nat/res/res_sip/sip_configuration.c Thu Feb 21 08:37:45 2013
@@ -287,6 +287,7 @@
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_min_se", "90", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, min_se));
 	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_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));
 
 	if (ast_sip_initialize_sorcery_transport(sip_sorcery)) {
 		ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");

Modified: team/file/pimp_sip_nat/res/res_sip_nat.c
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_nat/res/res_sip_nat.c?view=diff&rev=381845&r1=381844&r2=381845
==============================================================================
--- team/file/pimp_sip_nat/res/res_sip_nat.c (original)
+++ team/file/pimp_sip_nat/res/res_sip_nat.c Thu Feb 21 08:37:45 2013
@@ -29,6 +29,7 @@
 
 #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)
 {
@@ -46,11 +47,116 @@
 	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);
+}
+
+static pj_status_t nat_on_tx_request(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_sip_uri *uri = NULL;
+	struct ast_sockaddr addr = { { 0, } };
+
+	/* 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))) {
+			return PJ_SUCCESS;
+		}
+
+		details.local_address = uri->host;
+		details.local_port = uri->port;
+	}
+
+	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) || (!uri && !(uri = nat_get_contact_sip_uri(tdata)))) {
+		return PJ_SUCCESS;
+	}
+
+	/* Update the contact header with the external address */
+	pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport->external_address));
+	if (transport->external_signaling_port) {
+		uri->port = transport->external_signaling_port;
+	}
+
+	return PJ_SUCCESS;
+}
+
 static pjsip_module nat_module = {
 	.name = { "NAT", 3 },
 	.id = -1,
-	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER,
+	.priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER + 4,
 	.on_rx_request = nat_on_rx_request,
+	.on_tx_request = nat_on_tx_request,
 };
 
 static int load_module(void)

Modified: team/file/pimp_sip_nat/res/res_sip_sdp_audio.c
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_nat/res/res_sip_sdp_audio.c?view=diff&rev=381845&r1=381844&r2=381845
==============================================================================
--- team/file/pimp_sip_nat/res/res_sip_sdp_audio.c (original)
+++ team/file/pimp_sip_nat/res/res_sip_sdp_audio.c Thu Feb 21 08:37:45 2013
@@ -177,7 +177,8 @@
 	ast_rtp_instance_get_local_address(session->media[AST_SIP_MEDIA_AUDIO].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));
+	pj_strdup2(pool, &media->conn->addr, !ast_sockaddr_is_ipv6(&addr) ? S_OR(session->endpoint->external_media_address, ast_sockaddr_stringify_addr_remote(&addr)) :
+			   ast_sockaddr_stringify_addr_remote(&addr));
 	media->desc.port = (pj_uint16_t) ast_sockaddr_port(&addr);
 	media->desc.port_count = 1;
 
@@ -451,7 +452,7 @@
 
 		/* Find all of the candidates */
 		for (attr_i = 0; attr_i < remote_stream->attr_count; ++attr_i) {
-			char foundation[32], transport[4], address[46], cand_type[6], relay_address[46] = "";
+			char foundation[32], transport[4], address[PJ_INET6_ADDRSTRLEN + 1], cand_type[6], relay_address[46] = "";
 			int port, relay_port = 0;
 			struct ast_rtp_engine_ice_candidate candidate = { 0, };
 
@@ -464,7 +465,7 @@
 
 			ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value));
 
-			if (sscanf(attr_value, "%31s %30u %3s %30u %23s %30u typ %5s %*s %23s %*s %30u", foundation, &candidate.id, transport,
+			if (sscanf(attr_value, "%31s %30u %3s %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;
@@ -497,6 +498,7 @@
 			ice->add_remote_candidate(session->media[AST_SIP_MEDIA_AUDIO].rtp, &candidate);
 		}
 
+		ice->start(session->media[AST_SIP_MEDIA_AUDIO].rtp);
 	}
 
 	if (session->media[AST_SIP_MEDIA_AUDIO].held && (!ast_sockaddr_isnull(addrs) ||
@@ -509,7 +511,6 @@
 		/* 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_rtp_instance_stop(session->media[AST_SIP_MEDIA_AUDIO].rtp);
 		ast_queue_frame(session->channel, &ast_null_frame);
 		session->media[AST_SIP_MEDIA_AUDIO].held = 1;
 	} else {




More information about the svn-commits mailing list