#if HAVE_CRT #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #endif //HAVE_CRT ///* //* Copyright (C) 2010-2011 Mamadou Diop. //* //* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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_stun.c // * @brief Session Traversal Utilities for NAT (STUN) implementation as per RFC 5389 and RFC 3489(Obsolete). // * // * @author Mamadou Diop <diopmamadou(at)doubango[dot]org> // * // // */ //#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 <string.h> // ///**@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_pkt_t *tnet_stun_create_request(const tnet_stun_binding_t* binding) //{ // tnet_stun_pkt_t *message = tnet_stun_message_create(binding->username, binding->password); // // if(message) { // message->realm = tsk_strdup(binding->realm); // message->nonce = tsk_strdup(binding->nonce); // // /* 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->transac_id, digest, TNET_STUN_TRANSACID_SIZE); // } // // /* Add software attribute */ // if(binding->software) { // tnet_stun_attr_t* attribute = (tnet_stun_attr_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_pkt_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_pkt_resp_t* tnet_stun_send_unreliably(tnet_fd_t localFD, uint16_t RTO, uint16_t Rc, const tnet_stun_pkt_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_pkt_serialize(message); // tnet_stun_pkt_resp_t *response = tsk_null; // // if(!buffer) { // goto bail; // } // // { ////# 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) //// 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; i<Rc; i++) { // tv.tv_sec += rto/1000; // tv.tv_usec += (rto% 1000) * 1000; // // FD_ZERO(&set); // FD_SET(localFD, &set); // // ret = tnet_sockfd_sendto(localFD, server, buffer->data, 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 */ #if HAVE_CRT //Debug memory // data = calloc(len, sizeof(uint8_t)); #else // data = tsk_calloc(len, sizeof(uint8_t)); #endif //HAVE_CRT // 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->transac_id, response->transac_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_pkt_resp_t *response = 0; // tnet_stun_pkt_req_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_PKT_RESP_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_attr_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 kStunBindingInvalidId 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 = kStunBindingInvalidId; // // 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_transac_id_t id1, const tnet_stun_transac_id_t id2) //{ // tsk_size_t i; // for(i=0; i<sizeof(tnet_stun_transac_id_t); i++) { // if(id1[i] != id2[i]) { // return (id1[i] - id2[i]); // } // } // return 0; //} // // // // // // // // // // ////================================================================================================= //// STUN2 BINDING object definition //// //static tsk_object_t* tnet_stun_binding_ctor(tsk_object_t * self, va_list * app) //{ // tnet_stun_binding_t *binding = self; // if(binding) { // static tnet_stun_binding_id_t __binding_unique_id = 0; // // const char* server_address; // tnet_port_t server_port; // // binding->id = ++__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*)); // server_port = (tnet_port_t)va_arg(*app, unsigned); //#else // server_port = va_arg(*app, tnet_port_t); // // binding->username = tsk_strdup(va_arg(*app, const char*)); // binding->password = tsk_strdup(va_arg(*app, const char*)); // // if(server_address) { // 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; // //