From 1ebf5a5fcda0c9154e22ed02404fd46525a7fd9f Mon Sep 17 00:00:00 2001 From: bossiel Date: Wed, 10 Aug 2011 22:59:15 +0000 Subject: Move deprecated v1.0 from trunk to branches --- tinyNET/src/tnet_nat.c | 570 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 tinyNET/src/tnet_nat.c (limited to 'tinyNET/src/tnet_nat.c') 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 +* +* 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 + * + * @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; -- cgit v1.1