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/stun/tnet_stun.c | 440 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 440 insertions(+) create mode 100644 tinyNET/src/stun/tnet_stun.c (limited to 'tinyNET/src/stun/tnet_stun.c') diff --git a/tinyNET/src/stun/tnet_stun.c b/tinyNET/src/stun/tnet_stun.c new file mode 100644 index 0000000..5faa974 --- /dev/null +++ b/tinyNET/src/stun/tnet_stun.c @@ -0,0 +1,440 @@ +/* +* 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_stun.c + * @brief Session Traversal Utilities for NAT (STUN) implementation as per RFC 5389 and RFC 3489(Obsolete). + * + * @author Mamadou Diop + * + * @date Created: Sat Nov 8 16:54:58 2009 mdiop + */ +#include "tnet_stun.h" + +#include "../tnet_nat.h" +#include "../tnet_utils.h" + +#include "tsk_md5.h" +#include "tsk_string.h" +#include "tsk_memory.h" +#include "tsk_buffer.h" +#include "tsk_debug.h" + +#include + +/**@defgroup tnet_stun_group STUN2 (RFC 5389) implementation. +*/ + + +/**@ingroup tnet_stun_group +* Creates new @ref tnet_stun_binding_t object. +*/ +tnet_stun_binding_t* tnet_stun_binding_create(tnet_fd_t fd, tnet_socket_type_t socket_type, const char* server_address, tnet_port_t server_port, const char* username, const char* password) +{ + return tsk_object_new(tnet_stun_binding_def_t, fd, socket_type, server_address, server_port, username, password); +} + +/**@ingroup tnet_stun_group + * + * Create generic STUN2 request with all mandatory headers and attributes. + * + * @param [in,out] binding The binding object from which to create the request. + * + * @retval STUN2 request if succeed and NULL otherwise. +**/ +tnet_stun_message_t *tnet_stun_create_request(const tnet_stun_binding_t* binding) +{ + tnet_stun_message_t *message = tnet_stun_message_create(binding->username, binding->password); + message->realm = tsk_strdup(binding->realm); + message->nonce = tsk_strdup(binding->nonce); + + if(message){ + /* Set the request type (RFC 5389 defines only one type) */ + message->type = stun_binding_request; + + { /* Create random transaction id */ + tsk_istr_t random; + tsk_md5digest_t digest; + + tsk_strrandom(&random); + TSK_MD5_DIGEST_CALC(random, sizeof(random), digest); + + memcpy(message->transaction_id, digest, TNET_STUN_TRANSACID_SIZE); + } + + /* Add software attribute */ + if(binding->software){ + tnet_stun_attribute_t* attribute = (tnet_stun_attribute_t*)tnet_stun_attribute_software_create(binding->software, tsk_strlen(binding->software)); + tnet_stun_message_add_attribute(message, &attribute); + } + } + + return message; +} + +int tnet_stun_send_reliably(const tnet_stun_message_t* message) +{ + return -1; +} + + +/**@ingroup tnet_stun_group + * + * Internal function to send a STUN message using unrealiable protocol such as UDP. + * + * + * @param localFD The local file descriptor. + * @param RTO The Retransmission TimeOut. + * @param Rc The Number of retransmissions. + * @param [in,out] message The message to send. + * @param [in,out] server The destination STUN server. + * + * @return The response from the server or NULL if transport error. +**/ +tnet_stun_response_t* tnet_stun_send_unreliably(tnet_fd_t localFD, uint16_t RTO, uint16_t Rc, const tnet_stun_message_t* message, struct sockaddr* server) +{ + /* RFC 5389 - 7.2.1. Sending over UDP + STUN indications are not retransmitted; thus, indication transactions over UDP + are not reliable. + */ + //int retransmit = (message->type == stun_binding_request); + + int ret = -1; + uint16_t i, rto = RTO; + struct timeval tv; + fd_set set; + + tsk_buffer_t *buffer = tnet_stun_message_serialize(message); + tnet_stun_response_t *response = 0; + + if(!buffer) + { + goto bail; + } + + { +//#ifndef SIO_UDP_CONNRESET +//# ifndef IOC_VENDOR +//# define IOC_VENDOR 0x18000000 +//# endif +//# ifndef _WSAIOW +//# define _WSAIOW(x,y) (IOC_IN|(x)|(y)) +//# endif +//# define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) +//#endif +// DWORD dwBytesReturned = 0; +// BOOL bNewBehavior = TRUE; +// DWORD status; +// +// // disable new behavior using +// // IOCTL: SIO_UDP_CONNRESET +// status = WSAIoctl(localFD, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior), +// NULL, 0, &dwBytesReturned, NULL, NULL); + } + + tv.tv_sec = 0; + tv.tv_usec = 0; + + /* RFC 5389 - 7.2.1. Sending over UDP + A client SHOULD retransmit a STUN request message starting with an + interval of RTO ("Retransmission TimeOut"), doubling after each + retransmission. + + e.g. 0 ms, 500 ms, 1500 ms, 3500 ms, 7500ms, 15500 ms, and 31500 ms + */ + for(i=0; idata, buffer->size); + + if((ret = select(localFD+1, &set, NULL, NULL, &tv))<0){ + goto bail; + } + else if(ret == 0){ + /* timeout */ + TSK_DEBUG_INFO("STUN request timedout at %d", i); + rto *= 2; + continue; + } + else if(FD_ISSET(localFD, &set)){ + /* there is data to read */ + + tsk_size_t len = 0; + void* data = 0; + + TSK_DEBUG_INFO("STUN request got response"); + + /* Check how how many bytes are pending */ + if((ret = tnet_ioctlt(localFD, FIONREAD, &len))<0){ + goto bail; + } + + if(len==0){ + TSK_DEBUG_INFO("tnet_ioctlt() returent zero bytes"); + continue; + } + + /* Receive pending data */ + data = tsk_calloc(len, sizeof(uint8_t)); + if((ret = tnet_sockfd_recvfrom(localFD, data, len, 0, server))<0){ + TSK_FREE(data); + + TSK_DEBUG_ERROR("Recving STUN dgrams failed with error code:%d", tnet_geterrno()); + goto bail; + } + + /* Parse the incoming response. */ + response = tnet_stun_message_deserialize(data, (tsk_size_t)ret); + TSK_FREE(data); + + if(response){ + if(tnet_stun_transacid_cmp(message->transaction_id, response->transaction_id)){ + /* Not same transaction id */ + TSK_OBJECT_SAFE_FREE(response); + continue; + } + } + + goto bail; + } + else{ + continue; + } + } + +bail: + TSK_OBJECT_SAFE_FREE(buffer); + + return response; +} + +/**@ingroup tnet_stun_group + * Internal function to send a STUN2 binding request over the network. + * + * @param [in,out] context The NAT context holding the user preferences. + * @param [in,out] binding The STUN binding object used to create the message to send. + * + * @return Zero if succeed and non-zero error code otherwise. +**/ +int tnet_stun_send_bind(const tnet_nat_context_t* context, tnet_stun_binding_t *binding) +{ + int ret = -1; + tnet_stun_response_t *response = 0; + tnet_stun_request_t *request = 0; + + + goto stun_phase0; + + /* RFC 5389 - 10.2.1.1. First Request + If the client has not completed a successful request/response + transaction with the server (as identified by hostname, if the DNS + procedures of Section 9 are used, else IP address if not), it SHOULD + omit the USERNAME, MESSAGE-INTEGRITY, REALM, and NONCE attributes. + In other words, the very first request is sent as if there were no + authentication or message integrity applied. + */ +stun_phase0: + { + if(!(request = tnet_stun_create_request(binding))){ + goto bail; + } + + if(TNET_SOCKET_TYPE_IS_DGRAM(context->socket_type)){ + response = tnet_stun_send_unreliably(binding->localFD, context->RTO, context->Rc, request, (struct sockaddr*)&binding->server); + } + + if(response){ + if(TNET_STUN_RESPONSE_IS_ERROR(response)){ + short code = tnet_stun_message_get_errorcode(response); + const char* realm = tnet_stun_message_get_realm(response); + const char* nonce = tnet_stun_message_get_nonce(response); + + if(code == 401 && realm && nonce){ + if(!binding->nonce){ + /* First time we get a nonce */ + tsk_strupdate(&binding->nonce, nonce); + tsk_strupdate(&binding->realm, realm); + + /* Delete the message and response before retrying*/ + TSK_OBJECT_SAFE_FREE(response); + TSK_OBJECT_SAFE_FREE(request); + + // Send again using new transaction identifier + return tnet_stun_send_bind(context, binding); + } + else{ + ret = -3; + } + } + else{ + ret = -2; + } + } + else{ + const tnet_stun_attribute_t *attribute; + if((attribute= tnet_stun_message_get_attribute(response, stun_xor_mapped_address))){ + ret = 0; + binding->xmaddr = tsk_object_ref((void*)attribute); + } + else if((attribute= tnet_stun_message_get_attribute(response, stun_mapped_address))){ + ret = 0; + binding->maddr = tsk_object_ref((void*)attribute); + } + } + } + } + /* END OF stun_phase0 */ + +bail: + TSK_OBJECT_SAFE_FREE(response); + TSK_OBJECT_SAFE_FREE(request); + + return ret; +} + +/**@ingroup tnet_stun_group + * + * Public function to create a binding context. + * + * @param [in,out] nat_context The NAT context. + * @param localFD The local file descriptor for which to create the binding context. + * + * @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. +**/ +tnet_stun_binding_id_t tnet_stun_bind(const tnet_nat_context_t* nat_context, tnet_fd_t localFD) +{ + tnet_stun_binding_id_t id = TNET_STUN_INVALID_BINDING_ID; + + tnet_stun_binding_t *binding = 0; + + if(nat_context && localFD != TNET_INVALID_FD){ + if(!(binding = tnet_stun_binding_create(localFD, nat_context->socket_type, nat_context->server_address, nat_context->server_port, nat_context->username, nat_context->password))){ + goto bail; + } + + if(tnet_stun_send_bind(nat_context, binding)){ + TSK_OBJECT_SAFE_FREE(binding); + goto bail; + } + + id = binding->id; + tsk_list_push_back_data(nat_context->stun_bindings, (void**)&binding); + } + +bail: + return id; +} + +/**@ingroup tnet_stun_group + * Compares two transaction ids. + * + * @param id1 The first transaction identifier. + * @param id2 The second transaction identifier. + * + * @return Zero if the two identifiers are equal and non-zero value otherwise. +**/ +int tnet_stun_transacid_cmp(const tnet_stun_transacid_t id1, const tnet_stun_transacid_t id2) +{ + tsk_size_t i; + for(i=0; iid = ++__binding_unique_id; + + binding->localFD = va_arg(*app, tnet_fd_t); + binding->socket_type = va_arg(*app, tnet_socket_type_t); + + server_address = tsk_strdup(va_arg(*app, const char*)); +#if defined(__GNUC__) + server_port = (tnet_port_t)va_arg(*app, unsigned); +#else + server_port = va_arg(*app, tnet_port_t); +#endif + + binding->username = tsk_strdup(va_arg(*app, const char*)); + binding->password = tsk_strdup(va_arg(*app, const char*)); + + tnet_sockaddr_init(server_address, server_port, binding->socket_type, &binding->server); + + binding->software = tsk_strdup(TNET_SOFTWARE); + } + return self; +} + +static tsk_object_t* tnet_stun_binding_dtor(tsk_object_t * self) +{ + tnet_stun_binding_t *binding = self; + if(binding){ + TSK_FREE(binding->username); + TSK_FREE(binding->password); + TSK_FREE(binding->realm); + TSK_FREE(binding->nonce); + + TSK_FREE(binding->software); + + TSK_OBJECT_SAFE_FREE(binding->maddr); + TSK_OBJECT_SAFE_FREE(binding->xmaddr); + } + + return self; +} + +static const tsk_object_def_t tnet_stun_binding_def_s = +{ + sizeof(tnet_stun_binding_t), + tnet_stun_binding_ctor, + tnet_stun_binding_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_binding_def_t = &tnet_stun_binding_def_s; + + -- cgit v1.1