summaryrefslogtreecommitdiffstats
path: root/tinyNET/src/tnet_nat.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyNET/src/tnet_nat.c')
-rw-r--r--tinyNET/src/tnet_nat.c570
1 files changed, 570 insertions, 0 deletions
diff --git a/tinyNET/src/tnet_nat.c b/tinyNET/src/tnet_nat.c
new file mode 100644
index 0000000..cd39b68
--- /dev/null
+++ b/tinyNET/src/tnet_nat.c
@@ -0,0 +1,570 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tnet_nat.c
+ * @brief NAT Traversal helper functions using STUN, TURN and ICE.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tnet_nat.h"
+
+#include "tnet_endianness.h"
+
+#include "tsk_string.h"
+#include "tsk_memory.h"
+
+#include "tsk_debug.h"
+
+/**@defgroup tnet_nat_group NAT Traversal API (STUN, TURN and ICE).
+*/
+
+
+/**@ingroup tnet_nat_group
+* Creates new NAT context.
+*/
+tnet_nat_context_handle_t* tnet_nat_context_create(tnet_socket_type_t socket_type, const char* username, const char* password)
+{
+ return tsk_object_new(tnet_nat_context_def_t, socket_type, username, password);
+}
+
+/**
+ * Predicate function to find turn allocation by id.
+ *
+ * @param [in,out] item The current list item.
+ * @param [in,out] id A pointer to the allocation identifier.
+ *
+ * @return Zero if current list item hold an allocation with the same id and -1 otherwise.
+**/
+int __pred_find_turn_allocation(const tsk_list_item_t* item, const void* id)
+{
+ if(item)
+ {
+ tnet_turn_allocation_t *allocation = item->data;
+ if(allocation)
+ {
+ tnet_turn_allocation_id_t alloc_id = *((tnet_turn_allocation_id_t*)id);
+ return (allocation->id == alloc_id) ? 0 : -1;
+ }
+ }
+ return -1;
+}
+
+/** Predicate function to find stun binding by id.
+ *
+ * @param [in,out] item The current list item.
+ * @param [in,out] id A pointer to the binding identifier.
+ *
+ * @return Zero if current list item hold a binding with the same id and -1 otherwise.
+**/
+int __pred_find_stun_binding(const tsk_list_item_t* item, const void* id)
+{
+ if(item)
+ {
+ tnet_stun_binding_t *binding = item->data;
+ if(binding)
+ {
+ tnet_stun_binding_id_t binding_id = *((tnet_stun_binding_id_t*)id);
+ return (binding->id == binding_id) ? 0 : -1;
+ }
+ }
+ return -1;
+}
+
+/** Predicate function to find TURN channel binding by id.
+ *
+ * @param [in,out] item The current list item.
+ * @param [in,out] id A pointer to the TURN channel binding identifier.
+ *
+ * @return Zero if current list item hold a TURN channel binding with the same id and -1 otherwise.
+**/
+int __pred_find_turn_channel_binding(const tsk_list_item_t* item, const void* id)
+{
+ if(item)
+ {
+ tnet_turn_channel_binding_t *binding = item->data;
+ if(binding)
+ {
+ tnet_turn_channel_binding_id_t binding_id = *((tnet_turn_channel_binding_id_t*)id);
+ return (binding->id == binding_id) ? 0 : -1;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Formats binary IP address as string.
+ *
+ * @param in_ip The binary IP address to format (in Host byte order).
+ * @param family The address family.
+ * @param [in,out] out_ip The output string
+ *
+ * @return Zero if current list item hold a binding with the same id and -1 otherwise.
+**/
+int tnet_stun_address_tostring(const uint8_t in_ip[16], tnet_stun_addr_family_t family, char** out_ip)
+{
+ /*if(family == stun_ipv6){
+ tsk_sprintf(out_ip, "%x:%x:%x:%x:%x:%x:%x:%x",
+ tnet_ntohs_2(&in_ip[0]), tnet_ntohs_2(&in_ip[2]), tnet_ntohs_2(&in_ip[4]), tnet_ntohs_2(&in_ip[6]),
+ tnet_ntohs_2(&in_ip[8]), tnet_ntohs_2(&in_ip[10]), tnet_ntohs_2(&in_ip[12]), tnet_ntohs_2(&in_ip[14]));
+ }
+ else if(family == stun_ipv4){
+ tsk_sprintf(out_ip, "%u.%u.%u.%u", (address>>24)&0xFF, (address>>16)&0xFF, (address>>8)&0xFF, (address>>0)&0xFF);
+
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Unsupported address family: %u.", family);
+ }*/
+ if(family == stun_ipv6){
+ tsk_sprintf(out_ip, "%x:%x:%x:%x:%x:%x:%x:%x",
+ TSK_TO_UINT16(&in_ip[0]), TSK_TO_UINT16(&in_ip[2]), TSK_TO_UINT16(&in_ip[4]), TSK_TO_UINT16(&in_ip[6]),
+ TSK_TO_UINT16(&in_ip[8]), TSK_TO_UINT16(&in_ip[10]), TSK_TO_UINT16(&in_ip[12]), TSK_TO_UINT16(&in_ip[14]));
+ }
+ else if(family == stun_ipv4){
+ tsk_sprintf(out_ip, "%u.%u.%u.%u", in_ip[0], in_ip[1], in_ip[2], in_ip[3]);
+
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Unsupported address family: %u.", family);
+ }
+
+ return -1;
+}
+
+
+/**@ingroup tnet_nat_group
+ *
+ * Sets the address of the STUN/TURN server.
+ *
+ * @param [in,out] self The NAT context.
+ * @param [in,out] server_address The address of server.
+ *
+ * @return Zero if succeed and non zero error code otherwise.
+**/
+int tnet_nat_set_server_address(tnet_nat_context_handle_t* self, const char* server_address)
+{
+ tnet_nat_context_t* context = self;
+
+ if(context){
+ tsk_strupdate(&(context->server_address), server_address);
+ return 0;
+ }
+ return -1;
+}
+
+/**@ingroup tnet_nat_group
+ *
+ * Sets the address and port of the STUN/TURN server.
+ *
+ * @param [in,out] self The NAT context.
+ * @param [in,out] server_address The address of server.
+ * @param server_port The server port.
+ *
+ * @return Zero if succeed and non zero error code otherwise.
+**/
+int tnet_nat_set_server(tnet_nat_context_handle_t* self, const char* server_address, tnet_port_t server_port)
+{
+ tnet_nat_context_t* context = self;
+
+ if(context){
+ tsk_strupdate(&(context->server_address), server_address);
+ context->server_port = server_port;
+
+ return 0;
+ }
+ return -1;
+}
+
+/**@ingroup tnet_nat_group
+ *
+ * Creates and sends a STUN2 binding request to the STUN/TURN server in order to get the server reflexive
+ * address associated to this file descriptor (or socket). The caller should call @ref tnet_nat_stun_unbind to destroy the binding.
+ *
+ * @param [in,out] self The NAT context.
+ * @param localFD The local file descriptor (or socket) for which to get the reflexive server address.
+ *
+ * @return A valid binding id if succeed and @ref TNET_STUN_INVALID_BINDING_ID otherwise. If the returned id is valid then
+ * the newly created binding will contain the server-reflexive address associated to the local file descriptor.
+ *
+ * @sa @ref tnet_nat_stun_unbind.
+**/
+tnet_stun_binding_id_t tnet_nat_stun_bind(const tnet_nat_context_handle_t* self, const tnet_fd_t localFD)
+{
+ const tnet_nat_context_t* context = self;
+ if(context){
+ return tnet_stun_bind(context, localFD);
+ }
+ return TNET_STUN_INVALID_BINDING_ID;
+}
+
+/**@ingroup tnet_nat_group
+ * Gets the server reflexive address associated to this STUN2 binding.
+ *
+ *
+ * @param [in,out] self The NAT context.
+ * @param id The id of the STUN2 binding conetxt (obtained using @ref tnet_nat_stun_bind) holding the server-reflexive address.
+ * @param [in,out] ipaddress The reflexive IP address. It is up the the caller to free the returned string
+ * @param [in,out] port The reflexive port.
+ *
+ * @return Zero if succeed and non zero error code otherwise.
+**/
+int tnet_nat_stun_get_reflexive_address(const tnet_nat_context_handle_t* self, tnet_stun_binding_id_t id, char** ipaddress, tnet_port_t *port)
+{
+ const tnet_nat_context_t* context = self;
+ if(context){
+ const tsk_list_item_t* item = tsk_list_find_item_by_pred(context->stun_bindings, __pred_find_stun_binding, &id);
+ if(item && item->data){
+ tnet_stun_binding_t *binding = item->data;
+ /*STUN2: XOR-MAPPED-ADDRESS */
+ if(binding->xmaddr){
+ int ret = 0;
+ if(ipaddress){
+ ret = tnet_stun_address_tostring(binding->xmaddr->xaddress, binding->xmaddr->family, ipaddress);
+ }
+ if(port){
+ *port = /*tnet_ntohs*/(binding->xmaddr->xport);
+ }
+ return ret;
+ }
+
+ /*STUN1: MAPPED-ADDRESS*/
+ if(binding->maddr){
+ int ret = 0;
+ if(ipaddress){
+ ret = tnet_stun_address_tostring(binding->maddr->address, binding->maddr->family, ipaddress);
+ }
+ if(port){
+ *port = /*tnet_ntohs*/(binding->maddr->port);
+ }
+ return ret;
+ }
+ }
+ }
+ return -1;
+}
+
+/**@ingroup tnet_nat_group
+ *
+ * Removes a STUN2 binding from the NAT context.
+ *
+ * @param [in,out] self The NAT context from which to remove the STUN2 binding.
+ * @param id The id of the STUN2 binding to remove.
+ *
+ * @return Zero if succeed and non zero error code otherwise.
+ *
+ *
+ * @sa @ref tnet_nat_stun_bind.
+**/
+int tnet_nat_stun_unbind(const tnet_nat_context_handle_t* self, tnet_stun_binding_id_t id)
+{
+ const tnet_nat_context_t* context = self;
+ if(context)
+ {
+ tsk_list_remove_item_by_pred(context->stun_bindings, __pred_find_stun_binding, &id);
+ return 0;
+ }
+ return -1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**@ingroup tnet_nat_group
+ *
+ * Creates TURN allocation as per draft-ietf-behave-turn-16 subclause 6. This function will also
+ * send an allocation request to the server (subclause 6.1).
+ *
+ * @param [in,out] self The NAT context.
+ * @param localFD The local file descriptor.
+ *
+ * @return A valid TURN allocation id if succeed and @ref TNET_TURN_INVALID_ALLOCATION_ID otherwise.
+ *
+ * @sa @ref tnet_nat_turn_unallocate.
+**/
+tnet_turn_allocation_id_t tnet_nat_turn_allocate(const tnet_nat_context_handle_t* self, const tnet_fd_t localFD)
+{
+ const tnet_nat_context_t* context = self;
+
+ if(self)
+ {
+ return tnet_turn_allocate(self, localFD, context->socket_type);
+ }
+
+ return TNET_TURN_INVALID_ALLOCATION_ID;
+}
+
+/**@ingroup tnet_nat_group
+ * Gets the STUN server-refelexive IP address and port associated to this TURN allocation.
+ *
+ * @param [in,out] self The NAT context.
+ * @param id The id of the TURN allocation for which to to get server-reflexive IP address and port.
+ * @param [in,out] ipaddress The server-reflexive IP address.
+ * @param [in,out] port The server-reflexive port.
+ *
+ * @return Zero if succeed and non zero error code otherwise.
+**/
+int tnet_nat_turn_get_reflexive_address(const tnet_nat_context_handle_t* self, tnet_turn_allocation_id_t id, char** ipaddress, tnet_port_t *port)
+{
+ const tnet_nat_context_t* context = self;
+ if(context)
+ {
+ const tsk_list_item_t* item = tsk_list_find_item_by_pred(context->allocations, __pred_find_turn_allocation, &id);
+ if(item && item->data)
+ {
+ tnet_turn_allocation_t *allocation = item->data;
+ /*STUN2: XOR-MAPPED-ADDRESS */
+ if(allocation->xmaddr)
+ {
+ int ret = tnet_stun_address_tostring(allocation->xmaddr->xaddress, allocation->xmaddr->family, ipaddress);
+ *port = /*tnet_ntohs*/(allocation->xmaddr->xport);
+ return ret;
+ }
+
+ /*STUN1: MAPPED-ADDRESS*/
+ if(allocation->maddr)
+ {
+ int ret = tnet_stun_address_tostring(allocation->maddr->address, allocation->maddr->family, ipaddress);
+ *port = /*tnet_ntohs*/(allocation->maddr->port);
+ return ret;
+ }
+ }
+ }
+ return -1;
+}
+
+/**@ingroup tnet_nat_group
+ *
+ * Refresh a TURN allocation previously created using @ref tnet_nat_turn_allocate.
+ *
+ * @param [in,out] self The NAT context.
+ * @param id The id of the TURN allocation to refresh.
+ *
+ * @return Zero if succeed and non zero error code otherwise.
+**/
+int tnet_nat_turn_allocation_refresh(const tnet_nat_context_handle_t* self, tnet_turn_allocation_id_t id)
+{
+ const tnet_nat_context_t* context = self;
+
+ if(context)
+ {
+ const tsk_list_item_t* item = tsk_list_find_item_by_pred(context->allocations, __pred_find_turn_allocation, &id);
+ if(item && item->data)
+ {
+ tnet_turn_allocation_t *allocation = item->data;
+ return tnet_turn_allocation_refresh(self, allocation);
+ }
+ }
+ return -1;
+}
+
+/**@ingroup tnet_nat_group
+ *
+ * Unallocate/remove a TURN allocation from the server.
+ *
+ * @param [in,out] self The NAT context from which to remove the allocation.
+ * @param id The id of the TURN allocation to remove.
+ *
+ * @return Zero if succeed and non zero error code otherwise.
+ *
+ * @sa @ref tnet_nat_turn_allocate.
+**/
+int tnet_nat_turn_unallocate(const tnet_nat_context_handle_t* self, tnet_turn_allocation_id_t id)
+{
+ const tnet_nat_context_t* context = self;
+
+ if(context)
+ {
+ const tsk_list_item_t* item = tsk_list_find_item_by_pred(context->allocations, __pred_find_turn_allocation, &id);
+ if(item && item->data)
+ {
+ tnet_turn_allocation_t *allocation = item->data;
+ return tnet_turn_unallocate(self, allocation);
+ }
+ }
+ return -1;
+}
+
+/**@ingroup tnet_nat_group
+ * Creates TURN channel binding as per draft-ietf-behave-turn-16 sublause 11 and send it to the
+ * server as per subclause 11.1.
+ *
+ *
+ * @param [in,out] self The NAT context.
+ * @param id The id of the TURN allocation associated to this binding.
+ * @param [in,out] peer The XOR remote peer for the channel binding.
+ *
+ * @return A valid TURN channel binding id if succeed and @ref TNET_TURN_INVALID_CHANNEL_BINDING_ID otherwise.
+**/
+tnet_turn_channel_binding_id_t tnet_nat_turn_channel_bind(const tnet_nat_context_handle_t* self, tnet_turn_allocation_id_t id, struct sockaddr_storage *peer)
+{
+ const tnet_nat_context_t* context = self;
+
+ if(context)
+ {
+ const tsk_list_item_t* item = tsk_list_find_item_by_pred(context->allocations, __pred_find_turn_allocation, &id);
+ if(item && item->data)
+ {
+ tnet_turn_allocation_t *allocation = item->data;
+ return tnet_turn_channel_bind(self, allocation, peer);
+ }
+ }
+ return TNET_TURN_INVALID_CHANNEL_BINDING_ID;
+}
+
+/**@ingroup tnet_nat_group
+*/
+int tnet_nat_turn_channel_refresh(const tnet_nat_context_handle_t* self, tnet_turn_channel_binding_id_t id)
+{
+ const tnet_nat_context_t* context = self;
+
+ if(context)
+ {
+ tsk_list_item_t* curr;
+ tsk_list_foreach(curr, context->allocations)
+ {
+ const tsk_list_item_t* item = tsk_list_find_item_by_pred(((tnet_turn_allocation_t *)curr->data)->channel_bindings, __pred_find_turn_channel_binding, &id);
+ if(item && item->data)
+ {
+ return tnet_turn_channel_refresh(context, (tnet_turn_channel_binding_t *)item->data);
+ }
+ }
+ }
+ return -1;
+}
+
+/**@ingroup tnet_nat_group
+*/
+int tnet_nat_turn_channel_send(const tnet_nat_context_handle_t* self, tnet_turn_channel_binding_id_t id, const void* data, tsk_size_t size, int indication)
+{
+ const tnet_nat_context_t* context = self;
+
+ if(context && data && size)
+ {
+ tsk_list_item_t* curr;
+ tsk_list_foreach(curr, context->allocations)
+ {
+ const tsk_list_item_t* item = tsk_list_find_item_by_pred(((tnet_turn_allocation_t *)curr->data)->channel_bindings, __pred_find_turn_channel_binding, &id);
+ if(item && item->data)
+ {
+ return tnet_turn_channel_senddata(context, (tnet_turn_channel_binding_t *)item->data, data, size, indication);
+ }
+ }
+ }
+ return -1;
+}
+
+/**@ingroup tnet_nat_group
+*/
+int tnet_nat_turn_add_permission(const tnet_nat_context_handle_t* self, tnet_turn_allocation_id_t id, const char* ipaddress, uint32_t timeout)
+{
+ const tnet_nat_context_t* context = self;
+
+ if(self)
+ {
+ const tsk_list_item_t* item = tsk_list_find_item_by_pred(context->allocations, __pred_find_turn_allocation, &id);
+ if(item && item->data)
+ {
+ tnet_turn_allocation_t *allocation = item->data;
+ return tnet_turn_add_permission(self, allocation, ipaddress, timeout);
+ }
+ }
+ return -1;
+}
+
+
+//=================================================================================================
+// NAT CONTEXT object definition
+//
+static tsk_object_t* tnet_nat_context_ctor(tsk_object_t * self, va_list * app)
+{
+ tnet_nat_context_t *context = self;
+ if(context){
+ context->socket_type = va_arg(*app, tnet_socket_type_t);
+
+ context->username = tsk_strdup(va_arg(*app, const char*));
+ context->password = tsk_strdup(va_arg(*app, const char*));
+
+ context->server_port = TNET_NAT_TCP_UDP_DEFAULT_PORT;
+
+ /* 7.2.1. Sending over UDP
+ In fixed-line access links, a value of 500 ms is RECOMMENDED.
+ */
+ context->RTO = TNET_NAT_DEFAULT_RTO;
+
+ /* 7.2.1. Sending over UDP
+ Rc SHOULD be configurable and SHOULD have a default of 7.
+ */
+ context->Rc = TNET_NAT_DEFAULT_RC;
+
+ context->software = tsk_strdup(TNET_SOFTWARE);
+ //context->enable_evenport = 1;
+ //context->enable_fingerprint = 1;
+ context->enable_integrity = 0;
+ context->enable_dontfrag = 0;//TNET_SOCKET_TYPE_IS_DGRAM(context->socket_type) ? 1 : 0;
+
+ context->allocations = tsk_list_create();
+ context->stun_bindings = tsk_list_create();
+ }
+ return self;
+}
+
+static tsk_object_t* tnet_nat_context_dtor(tsk_object_t * self)
+{
+ tnet_nat_context_t *context = self;
+ if(context){
+ TSK_FREE(context->username);
+ TSK_FREE(context->password);
+ TSK_FREE(context->software);
+ TSK_FREE(context->server_address);
+
+ TSK_OBJECT_SAFE_FREE(context->allocations);
+ TSK_OBJECT_SAFE_FREE(context->stun_bindings);
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tnet_nat_context_def_s =
+{
+ sizeof(tnet_nat_context_t),
+ tnet_nat_context_ctor,
+ tnet_nat_context_dtor,
+ tsk_null,
+};
+const tsk_object_def_t *tnet_nat_context_def_t = &tnet_nat_context_def_s;
OpenPOWER on IntegriCloud