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 +++++++++++++++ tinyNET/src/stun/tnet_stun.h | 128 +++++ tinyNET/src/stun/tnet_stun_attribute.c | 960 +++++++++++++++++++++++++++++++++ tinyNET/src/stun/tnet_stun_attribute.h | 329 +++++++++++ tinyNET/src/stun/tnet_stun_message.c | 440 +++++++++++++++ tinyNET/src/stun/tnet_stun_message.h | 240 +++++++++ 6 files changed, 2537 insertions(+) create mode 100644 tinyNET/src/stun/tnet_stun.c create mode 100644 tinyNET/src/stun/tnet_stun.h create mode 100644 tinyNET/src/stun/tnet_stun_attribute.c create mode 100644 tinyNET/src/stun/tnet_stun_attribute.h create mode 100644 tinyNET/src/stun/tnet_stun_message.c create mode 100644 tinyNET/src/stun/tnet_stun_message.h (limited to 'tinyNET/src/stun') 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; + + diff --git a/tinyNET/src/stun/tnet_stun.h b/tinyNET/src/stun/tnet_stun.h new file mode 100644 index 0000000..05933ca --- /dev/null +++ b/tinyNET/src/stun/tnet_stun.h @@ -0,0 +1,128 @@ +/* +* 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.h + * @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 + */ +#ifndef TNET_STUN_H +#define TNET_STUN_H + +#include "tinynet_config.h" +#include "stun/tnet_stun_message.h" +#include "tnet_types.h" +#include "tnet_socket.h" + +#include "tsk_object.h" + +TNET_BEGIN_DECLS + +/**@ingroup tnet_stun_group +*/ +typedef uint64_t tnet_stun_binding_id_t; +/**@ingroup tnet_stun_group + * @def TNET_STUN_INVALID_BINDING_ID + * STUN2 invalid binding id. +**/ +/**@ingroup tnet_stun_group + * @def TNET_STUN_IS_VALID_BINDING_ID + * Checks the validity of the STUN @a id. +**/ +#define TNET_STUN_INVALID_BINDING_ID 0 +#define TNET_STUN_IS_VALID_BINDING_ID(id) (id != TNET_STUN_INVALID_BINDING_ID) + +/**@ingroup tnet_stun_group + * Default port for both TCP and UDP protos as per RFC 5389 subclause 9. +**/ +#define TNET_STUN_TCP_UDP_DEFAULT_PORT 3478 + +/**@ingroup tnet_stun_group + * Default port for TLS protocol as per RFC 5389 subclause 9. +**/ +#define TNET_STUN_TLS_DEFAULT_PORT 5349 + + +/**@ingroup tnet_stun_group + * STUN2 magic cookie value in network byte order as per RFC 5389 subclause 6. +**/ +#define TNET_STUN_MAGIC_COOKIE 0x2112A442 + +/**@ingroup tnet_stun_group + * STUN2 header size as per RFC 5389 subclause 6. +**/ +#define TNET_STUN_HEADER_SIZE 20 + +/**@ingroup tnet_stun_group + * STUN2 binding context. +**/ +typedef struct tnet_stun_binding_s +{ + TSK_DECLARE_OBJECT; + + //! A unique id to identify this binding. + tnet_stun_binding_id_t id; + + //! The username to authenticate to the STUN server. + char* username; + //! The password to authenticate to the STUN server. + char* password; + //! The realm. + char* realm; + //! The nonce. + char* nonce; + //! The client name. + char* software; + //! Local file descriptor for which to get server reflexive address. + tnet_fd_t localFD; + //! The type of the bound socket. + tnet_socket_type_t socket_type; + //! The address of the STUN server. + struct sockaddr_storage server; + //! Server reflexive address of the local socket(STUN1 as per RFC 3489). + tnet_stun_attribute_mapped_addr_t *maddr; + //! XORed server reflexive address (STUN2 as per RFC 5389). + tnet_stun_attribute_xmapped_addr_t *xmaddr; +}tnet_stun_binding_t; + +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_binding_def_t; +/**@ingroup tnet_stun_group + * List of @ref tnet_stun_binding_t elements. +**/ +typedef tsk_list_t tnet_stun_bindings_L_t; + +//#if defined(__SYMBIAN32__) || ANDROID /* Forward declaration */ +struct tnet_nat_context_s; +//#endif + +int tnet_stun_send_reliably(const tnet_stun_message_t* message); +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); +TINYNET_API tnet_stun_binding_id_t tnet_stun_bind(const struct tnet_nat_context_s* nat_context, tnet_fd_t localFD); +int tnet_stun_transacid_cmp(const tnet_stun_transacid_t id1, const tnet_stun_transacid_t id2); + +TNET_END_DECLS + + +#endif /* TNET_STUN_H */ + diff --git a/tinyNET/src/stun/tnet_stun_attribute.c b/tinyNET/src/stun/tnet_stun_attribute.c new file mode 100644 index 0000000..feb6909 --- /dev/null +++ b/tinyNET/src/stun/tnet_stun_attribute.c @@ -0,0 +1,960 @@ +/* +* 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_attribute.c + * @brief STUN2(RFC 5389) attribute parser. + * + * @author Mamadou Diop + * + * @date Created: Sat Nov 8 16:54:58 2009 mdiop + */ +#include "tnet_stun_attribute.h" + +#include "tnet_stun.h" + +#include "../tnet_types.h" +#include "../tnet_endianness.h" + +#include "../turn/tnet_turn_attribute.h" + +#include "tsk_memory.h" +#include "tsk_string.h" +#include "tsk_debug.h" + +#include + + +/**@ingroup tnet_stun_group +* RFC 5389 - 15. STUN Attributes +* Creates new @ref tnet_stun_attribute_def_t object. +*/ +tnet_stun_attribute_t* tnet_stun_attribute_create() +{ + return tsk_object_new(tnet_stun_attribute_def_t); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.1. MAPPED-ADDRESS. +* Creates new @ref tnet_stun_attribute_mapped_addr_t object. +*/ +tnet_stun_attribute_mapped_addr_t* tnet_stun_attribute_mapped_address_create(const void* payload, tsk_size_t payload_size) +{ + return tsk_object_new(tnet_stun_attribute_mapped_addr_def_t, payload, payload_size); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.2. XOR-MAPPED-ADDRESS. +* Creates new @ref tnet_stun_attribute_xmapped_addr_t object. +*/ +tnet_stun_attribute_xmapped_addr_t* tnet_stun_attribute_xmapped_address_create(const void* payload, tsk_size_t payload_size) +{ + return tsk_object_new(tnet_stun_attribute_xmapped_addr_def_t, payload, payload_size); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.3. USERNAME. +* Creates new @ref tnet_stun_attribute_username_t object. +*/ +tnet_stun_attribute_username_t* tnet_stun_attribute_username_create(const void* payload, tsk_size_t payload_size) +{ + return tsk_object_new(tnet_stun_attribute_username_def_t, payload, payload_size); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.4. MESSAGE-INTEGRITY. +* Creates new @ref tnet_stun_attribute_integrity_t object. +*/ +tnet_stun_attribute_integrity_t* tnet_stun_attribute_integrity_create(const void* payload, tsk_size_t payload_size) +{ + return tsk_object_new(tnet_stun_attribute_integrity_def_t, payload, payload_size); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.5. FINGERPRINT. +* Creates new @ref tnet_stun_attribute_fingerprint_t object. +*/ +tnet_stun_attribute_fingerprint_t* tnet_stun_attribute_fingerprint_create(uint32_t fingerprint) +{ + return tsk_object_new(tnet_stun_attribute_fingerprint_def_t, fingerprint); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.6. ERROR-CODE +* Creates new @ref tnet_stun_attribute_errorcode_t object. +*/ +tnet_stun_attribute_errorcode_t* tnet_stun_attribute_errorcode_create(const void* payload, tsk_size_t payload_size) +{ + return tsk_object_new(tnet_stun_attribute_errorcode_def_t, payload, payload_size); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.7. REALM. +* Creates new @ref tnet_stun_attribute_realm_t object. +*/ +tnet_stun_attribute_realm_t* tnet_stun_attribute_realm_create(const void* payload, tsk_size_t payload_size) +{ + return tsk_object_new(tnet_stun_attribute_realm_def_t, payload, payload_size); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.8. NONCE. +* Creates new @ref tnet_stun_attribute_nonce_t object. +*/ +tnet_stun_attribute_nonce_t* tnet_stun_attribute_nonce_create(const void* payload, tsk_size_t payload_size) +{ + return tsk_object_new(tnet_stun_attribute_nonce_def_t, payload, payload_size); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.9. UNKNOWN-ATTRIBUTES. +* Creates new @ref tnet_stun_attribute_unknowns_t object. +*/ +tnet_stun_attribute_unknowns_t* tnet_stun_attribute_unknowns_create(const void* payload, tsk_size_t payload_size) +{ + return tsk_object_new(tnet_stun_attribute_unknowns_def_t, payload, payload_size); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.10. SOFTWARE. +* Creates new @ref tnet_stun_attribute_software_t object. +*/ +tnet_stun_attribute_software_t* tnet_stun_attribute_software_create(const void* payload, tsk_size_t payload_size) +{ + return tsk_object_new(tnet_stun_attribute_software_def_t, payload, payload_size); +} + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.11. ALTERNATE-SERVER. +* Creates new @ref tnet_stun_attribute_altserver_t object. +*/ +tnet_stun_attribute_altserver_t* tnet_stun_attribute_altserver_create(const void* payload, tsk_size_t payload_size) +{ + return tsk_object_new(tnet_stun_attribute_altserver_def_t, payload, payload_size); +} + +/**@ingroup tnet_stun_group +* Creates @ref tnet_stun_attribute_t from raw buffer. +* @param data Raw buffer from which to create the STUN attribute.* +* @param size The size of the eaw buffer. +* @retval @ref tnet_stun_attribute_t object if succeed and NULL other wise. +*/ +tnet_stun_attribute_t* tnet_stun_attribute_deserialize(const void* data, tsk_size_t size) +{ + tnet_stun_attribute_t *attribute = 0; + const uint8_t* dataPtr = data; + + tnet_stun_attribute_type_t type = (tnet_stun_attribute_type_t)tnet_ntohs_2(dataPtr); + uint16_t length = tnet_ntohs_2(&dataPtr[2]); + + /* Check validity */ + if(!data || size<=4/* Type(2-bytes) plus Length (2-bytes) */) + { + return 0; + } + + dataPtr += (2 /* Type */+ 2/* Length */); + + /* Attribute Value + */ + + switch(type) + { + /* RFC 5389 - 15.1. MAPPED-ADDRESS */ + case stun_mapped_address: + { + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_mapped_address_create(dataPtr, length); + break; + } + + /* RFC 5389 - 15.2. XOR-MAPPED-ADDRESS*/ + case stun_xor_mapped_address: + { + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_xmapped_address_create(dataPtr, length); + break; + } + + /* RFC 5389 - 15.3. USERNAME*/ + case stun_username: + { + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_username_create(dataPtr, length); + break; + } + + + /* RFC 5389 - MESSAGE-INTEGRITY*/ + case stun_message_integrity: + { + if(length == TSK_SHA1_DIGEST_SIZE){ + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_integrity_create(dataPtr, length); + } + break; + } + + /* RFC 5389 - 15.5. FINGERPRINT*/ + case stun_fingerprint: + { + uint32_t fingerprint = tnet_htonl_2(dataPtr); + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_fingerprint_create(fingerprint); + break; + } + + /* RFC 5389 - 15.6. ERROR-CODE*/ + case stun_error_code: + { + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_errorcode_create(dataPtr, length); + break; + } + + /* RFC 5389 - 15.7. REALM*/ + case stun_realm: + { + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_realm_create(dataPtr, length); + break; + } + + /* RFC 5389 - 15.8. NONCE*/ + case stun_nonce: + { + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_nonce_create(dataPtr, length); + break; + } + + /* RFC 5389 - 15.9. UNKNOWN-ATTRIBUTES*/ + case stun_unknown_attributes: + { + TSK_DEBUG_ERROR("DESERIALIZE:UNKNOWN-ATTRIBUTES ==> NOT IMPLEMENTED"); + attribute = tnet_stun_attribute_create(); + break; + } + + /* RFC 5389 - 15.10. SOFTWARE */ + case stun_software: + { + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_software_create(dataPtr, length); + break; + } + + /* RFC 5389 - 15.11. ALTERNATE-SERVER */ + case stun_alternate_server: + { + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_altserver_create(dataPtr, length); + break; + } + + /* draft-ietf-behave-turn-16 subclause 14 */ + case stun_channel_number: + case stun_lifetime: + case stun_reserved2: + case stun_xor_peer_address: + case stun_data: + case stun_xor_relayed_address: + case stun_even_port: + case stun_requested_transport: + case stun_dont_fragment: + case stun_reserved3: + case stun_reservation_token: + { + attribute = tnet_turn_attribute_deserialize(type, length, dataPtr, length); + break; + } + + default: + //TSK_DEBUG_WARN("==> NOT IMPLEMENTED"); + break; + } + + if(!attribute){ + /* Create default */ + attribute = tnet_stun_attribute_create(); + } + + /* Set common values (Do I need this ==> already set by the constructor). */ + attribute->type = type; + attribute->length = length; + + return attribute; +} + +/**@ingroup tnet_stun_group +* Serializes a @ref tnet_stun_attribute_t objet in binary format. +* @param attribute The STUN attribute to serialize. +* @param output The output binary buffer. +* @retval Zero if succeed and non-zero error code otherwise. +*/ +int tnet_stun_attribute_serialize(const tnet_stun_attribute_t* attribute, tsk_buffer_t *output) +{ + if(!attribute || !output){ + return -1; + } + + /* Attribute Type + */ + { + uint16_t type = tnet_htons(attribute->type); + tsk_buffer_append(output, &(type), 2); + } + + /* Attribute Length + */ + { + uint16_t length = tnet_htons(attribute->length); + tsk_buffer_append(output, &(length), 2); + } + + /* Attribute Value + */ + + switch(attribute->type){ + /* RFC 5389 - 15.1. MAPPED-ADDRESS */ + case stun_mapped_address: + { + TSK_DEBUG_ERROR("NOT IMPLEMENTED"); + return -3; + } + + /* RFC 5389 - 15.2. XOR-MAPPED-ADDRESS*/ + case stun_xor_mapped_address: + { + TSK_DEBUG_ERROR("NOT IMPLEMENTED"); + return -3; + } + + /* RFC 5389 - 15.3. USERNAME*/ + case stun_username: + { + tnet_stun_attribute_username_t *username = (tnet_stun_attribute_username_t*)attribute; + tsk_buffer_append(output, username->value, tsk_strlen(username->value)); + return 0; + } + + + /* RFC 5389 - MESSAGE-INTEGRITY*/ + case stun_message_integrity: + { + tnet_stun_attribute_integrity_t *integrity = (tnet_stun_attribute_integrity_t*)attribute; + tsk_buffer_append(output, integrity->sha1digest, TSK_SHA1_DIGEST_SIZE); + return 0; + } + + /* RFC 5389 - 15.5. FINGERPRINT*/ + case stun_fingerprint: + { + uint32_t fingerprint = /*tnet_htonl*/(((tnet_stun_attribute_fingerprint_t*)attribute)->value); + tsk_buffer_append(output, &fingerprint, 4); + return 0; + } + + /* RFC 5389 - 15.6. ERROR-CODE*/ + case stun_error_code: + { + TSK_DEBUG_ERROR("NOT IMPLEMENTED"); + return -3; + } + + /* RFC 5389 - 15.7. REALM*/ + case stun_realm: + { + tnet_stun_attribute_realm_t *realm = (tnet_stun_attribute_realm_t*)attribute; + tsk_buffer_append(output, realm->value, tsk_strlen(realm->value)); + return 0; + } + + /* RFC 5389 - 15.8. NONCE*/ + case stun_nonce: + { + tnet_stun_attribute_nonce_t *nonce = (tnet_stun_attribute_nonce_t*)attribute; + tsk_buffer_append(output, nonce->value, tsk_strlen(nonce->value)); + return 0; + } + + /* RFC 5389 - 15.9. UNKNOWN-ATTRIBUTES*/ + case stun_unknown_attributes: + { + TSK_DEBUG_ERROR("NOT IMPLEMENTED"); + return -3; + } + + /* RFC 5389 - 15.10. SOFTWARE */ + case stun_software: + { + tnet_stun_attribute_software_t *software = (tnet_stun_attribute_software_t*)attribute; + tsk_buffer_append(output, software->value, tsk_strlen(software->value)); + return 0; + } + + /* RFC 5389 - 15.11. ALTERNATE-SERVER */ + case stun_alternate_server: + { + TSK_DEBUG_ERROR("NOT IMPLEMENTED"); + return -3; + } + /* draft-ietf-behave-turn-16 - */ + case stun_channel_number: + case stun_lifetime: + case stun_reserved2: + case stun_xor_peer_address: + case stun_data: + case stun_xor_relayed_address: + case stun_even_port: + case stun_requested_transport: + case stun_dont_fragment: + case stun_reserved3: + case stun_reservation_token: + { + return tnet_turn_attribute_serialize(attribute, output); + } + + default: + return -2; + } +} + +/**@ingroup tnet_stun_group +* Pads a STUN attribute to align it on 4 octets. +* @param attribute The STUN attribute to pad. +* @param output The output buffer into which to put zeros. +*/ +void tnet_stun_attribute_pad(const tnet_stun_attribute_t* attribute, tsk_buffer_t *output) +{ + if(attribute->length%4){ + static uint32_t zeros = 0x00000000; + tsk_buffer_append(output, &zeros, 4-(attribute->length%4)); + } +} + + + + +//================================================================================================= +// [[RFC 5389 - 15. STUN Attributes]] object definition +// +static tsk_object_t* tnet_stun_attribute_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_t *attribute = self; + if(attribute){ + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_t *attribute = self; + if(attribute){ + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_def_s = +{ + sizeof(tnet_stun_attribute_t), + tnet_stun_attribute_ctor, + tnet_stun_attribute_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_def_t = &tnet_stun_attribute_def_s; + + +//================================================================================================= +// [[RFC 5389 - 15.1. MAPPED-ADDRESS]] object definition +// +static tsk_object_t* tnet_stun_attribute_mapped_addr_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_mapped_addr_t *attribute = self; + if(attribute){ + const void *payload = va_arg(*app, const void*); + tsk_size_t payload_size = va_arg(*app, tsk_size_t); + + if(payload && payload_size){ + const uint8_t *payloadPtr = (const uint8_t*)payload; + payloadPtr += 1; /* Ignore first 8bits */ + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_mapped_address; + TNET_STUN_ATTRIBUTE(attribute)->length = payload_size; + + attribute->family = (tnet_stun_addr_family_t) (*(payloadPtr++)); + attribute->port = tnet_ntohs_2(payloadPtr); + payloadPtr+=2; + + { /*=== Compute IP address */ + tsk_size_t addr_size = (attribute->family == stun_ipv6) ? 16 : (attribute->family == stun_ipv4 ? 4 : 0); + if(addr_size){ + tsk_size_t i; + + for(i=0; iaddress[i] = payloadPtr[0], + attribute->address[i+1] = payloadPtr[1], + attribute->address[i+2] = payloadPtr[2], + attribute->address[i+3] = payloadPtr[3]; + payloadPtr+=4; + } + } + else{ + TSK_DEBUG_ERROR("UNKNOWN FAMILY [%u].", attribute->family); + } + } + } + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_mapped_addr_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_mapped_addr_t *attribute = self; + if(attribute){ + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_mapped_addr_def_s = +{ + sizeof(tnet_stun_attribute_mapped_addr_t), + tnet_stun_attribute_mapped_addr_ctor, + tnet_stun_attribute_mapped_addr_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_mapped_addr_def_t = &tnet_stun_attribute_mapped_addr_def_s; + +//================================================================================================= +// [[RFC 5389 - 15.2. XOR-MAPPED-ADDRESS]] object definition +// +static tsk_object_t* tnet_stun_attribute_xmapped_addr_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_xmapped_addr_t *attribute = self; + if(attribute){ + const void *payload = va_arg(*app, const void*); + tsk_size_t payload_size = va_arg(*app, tsk_size_t); + + if(payload && payload_size){ + const uint8_t *payloadPtr = (const uint8_t*)payload; + payloadPtr += 1; /* Ignore first 8bits */ + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_xor_mapped_address; + TNET_STUN_ATTRIBUTE(attribute)->length = payload_size; + + attribute->family = (tnet_stun_addr_family_t)(*(payloadPtr++)); + + /* RFC 5389 - 15.2. XOR-MAPPED-ADDRESS + X-Port is computed by taking the mapped port in host byte order, + XOR'ing it with the most significant 16 bits of the magic cookie, and + then the converting the result to network byte order. + */ + attribute->xport = tnet_ntohs_2(payloadPtr); + attribute->xport ^= 0x2112; + payloadPtr+=2; + + + { /*=== Compute IP address */ + + /* RFC 5389 - 15.2. XOR-MAPPED-ADDRESS + If the IP address family is IPv4, X-Address is computed by taking the mapped IP + address in host byte order, XOR'ing it with the magic cookie, and + converting the result to network byte order. + */ + tsk_size_t addr_size = (attribute->family == stun_ipv6) ? 16 : (attribute->family == stun_ipv4 ? 4 : 0); + if(addr_size){ + tsk_size_t i; + uint32_t addr; + + for(i=0; ixaddress[i], &addr, 4); + payloadPtr+=4; + } + } + else{ + TSK_DEBUG_ERROR("UNKNOWN FAMILY [%u].", attribute->family); + } + } + } + + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_xmapped_addr_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_xmapped_addr_t *attribute = self; + if(attribute){ + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_xmapped_addr_def_s = +{ + sizeof(tnet_stun_attribute_xmapped_addr_t), + tnet_stun_attribute_xmapped_addr_ctor, + tnet_stun_attribute_xmapped_addr_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_xmapped_addr_def_t = &tnet_stun_attribute_xmapped_addr_def_s; + + +//================================================================================================= +// [[RFC 5389 - 15.3. USERNAME]] object definition +// +static tsk_object_t* tnet_stun_attribute_username_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_username_t *attribute = self; + if(attribute){ + const void *payload = va_arg(*app, const void*); + tsk_size_t payload_size = va_arg(*app, tsk_size_t); + + attribute->value = tsk_strndup(payload, payload_size); + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_username; + TNET_STUN_ATTRIBUTE(attribute)->length = payload_size; + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_username_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_username_t *attribute = self; + if(attribute){ + TSK_FREE(attribute->value); + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_username_def_s = +{ + sizeof(tnet_stun_attribute_username_t), + tnet_stun_attribute_username_ctor, + tnet_stun_attribute_username_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_username_def_t = &tnet_stun_attribute_username_def_s; + + +//================================================================================================= +// [[RFC 5389 - 15.4. MESSAGE-INTEGRITY]] object definition +// +static tsk_object_t* tnet_stun_attribute_integrity_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_integrity_t *attribute = self; + if(attribute){ + const void *payload = va_arg(*app, const void*); + tsk_size_t payload_size = va_arg(*app, tsk_size_t); + + if(payload_size == TSK_SHA1_DIGEST_SIZE){ + memcpy(attribute->sha1digest, payload, TSK_SHA1_DIGEST_SIZE); + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_message_integrity; + TNET_STUN_ATTRIBUTE(attribute)->length = TSK_SHA1_DIGEST_SIZE; + } + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_integrity_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_integrity_t *attribute = self; + if(attribute){ + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_integrity_def_s = +{ + sizeof(tnet_stun_attribute_integrity_t), + tnet_stun_attribute_integrity_ctor, + tnet_stun_attribute_integrity_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_integrity_def_t = &tnet_stun_attribute_integrity_def_s; + + +//================================================================================================= +// [[RFC 5389 - 15.5. FINGERPRINT]] object definition +// +static tsk_object_t* tnet_stun_attribute_fingerprint_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_fingerprint_t *attribute = self; + if(attribute){ + attribute->value = va_arg(*app, uint32_t); + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_fingerprint; + TNET_STUN_ATTRIBUTE(attribute)->length = 4; + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_fingerprint_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_fingerprint_t *attribute = self; + if(attribute){ + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_fingerprint_def_s = +{ + sizeof(tnet_stun_attribute_fingerprint_t), + tnet_stun_attribute_fingerprint_ctor, + tnet_stun_attribute_fingerprint_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_fingerprint_def_t = &tnet_stun_attribute_fingerprint_def_s; + + + +//================================================================================================= +// [[RFC 5389 - 15.6. ERROR-CODE]] object definition +// +static tsk_object_t* tnet_stun_attribute_errorcode_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_errorcode_t *attribute = self; + if(attribute){ + const uint8_t *payload = (const uint8_t*)va_arg(*app, const void*); + tsk_size_t payload_size = va_arg(*app, tsk_size_t); + + if(payload_size >4){ + uint32_t code = tnet_htonl_2(payload); + payload += 4; + + attribute->_class = code >>8; + attribute->number = (code & 0xFF); + attribute->reason_phrase = tsk_strndup((const char*)payload, (payload_size-4)); + } + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_error_code; + TNET_STUN_ATTRIBUTE(attribute)->length = payload_size; + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_errorcode_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_errorcode_t *attribute = self; + if(attribute){ + TSK_FREE(attribute->reason_phrase); + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_errorcode_def_s = +{ + sizeof(tnet_stun_attribute_errorcode_t), + tnet_stun_attribute_errorcode_ctor, + tnet_stun_attribute_errorcode_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_errorcode_def_t = &tnet_stun_attribute_errorcode_def_s; + + +//================================================================================================= +// [[RFC 5389 - 15.7. REALM]] object definition +// +static tsk_object_t* tnet_stun_attribute_realm_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_realm_t *attribute = self; + if(attribute){ + const void *payload = va_arg(*app, const void*); + tsk_size_t payload_size = va_arg(*app, tsk_size_t); + + attribute->value = tsk_strndup(payload, payload_size); + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_realm; + TNET_STUN_ATTRIBUTE(attribute)->length = payload_size; + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_realm_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_realm_t *attribute = self; + if(attribute){ + TSK_FREE(attribute->value); + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_realm_def_s = +{ + sizeof(tnet_stun_attribute_realm_t), + tnet_stun_attribute_realm_ctor, + tnet_stun_attribute_realm_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_realm_def_t = &tnet_stun_attribute_realm_def_s; + + +//================================================================================================= +// [[RFC 5389 - 15.8. NONCE]] object definition +// +static tsk_object_t* tnet_stun_attribute_nonce_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_nonce_t *attribute = self; + if(attribute){ + const void *payload = va_arg(*app, const void*); + tsk_size_t payload_size = va_arg(*app, tsk_size_t); + + attribute->value = tsk_strndup(payload, payload_size); + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_nonce; + TNET_STUN_ATTRIBUTE(attribute)->length = payload_size; + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_nonce_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_nonce_t *attribute = self; + if(attribute){ + TSK_FREE(attribute->value); + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_nonce_def_s = +{ + sizeof(tnet_stun_attribute_nonce_t), + tnet_stun_attribute_nonce_ctor, + tnet_stun_attribute_nonce_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_nonce_def_t = &tnet_stun_attribute_nonce_def_s; + + +//================================================================================================= +// [[RFC 5389 - 15.9. UNKNOWN-ATTRIBUTES]] object definition +// +static tsk_object_t* tnet_stun_attribute_unknowns_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_unknowns_t *attribute = self; + if(attribute){ + //--const void *payload = va_arg(*app, const void*); + //--tsk_size_t payload_size = va_arg(*app, tsk_size_t); + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_unknown_attributes; + attribute->value = tsk_buffer_create_null(); + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_unknowns_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_unknowns_t *attribute = self; + if(attribute){ + TSK_OBJECT_SAFE_FREE(attribute->value); + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_unknowns_def_s = +{ + sizeof(tnet_stun_attribute_unknowns_t), + tnet_stun_attribute_unknowns_ctor, + tnet_stun_attribute_unknowns_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_unknowns_def_t = &tnet_stun_attribute_unknowns_def_s; + +//================================================================================================= +// [[RFC 5389 - 15.10. SOFTWARE]] object definition +// +static tsk_object_t* tnet_stun_attribute_software_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_software_t *attribute = self; + if(attribute){ + const void *payload = va_arg(*app, const void*); + tsk_size_t payload_size = va_arg(*app, tsk_size_t); + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_software; + + attribute->value = tsk_strndup(payload, payload_size); + TNET_STUN_ATTRIBUTE(attribute)->length = tsk_strlen(attribute->value); + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_software_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_software_t *attribute = self; + if(attribute){ + TSK_FREE(attribute->value); + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_software_def_s = +{ + sizeof(tnet_stun_attribute_software_t), + tnet_stun_attribute_software_ctor, + tnet_stun_attribute_software_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_software_def_t = &tnet_stun_attribute_software_def_s; + +//================================================================================================= +// [[RFC 5389 - 15.11. ALTERNATE-SERVER]] object definition +// +static tsk_object_t* tnet_stun_attribute_altserver_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_attribute_altserver_t *attribute = self; + if(attribute){ + const void *payload = va_arg(*app, const void*); + tsk_size_t payload_size = va_arg(*app, tsk_size_t); + + const uint8_t *payloadPtr = (const uint8_t*)payload; + payloadPtr += 1; /* Ignore first 8bits */ + + TNET_STUN_ATTRIBUTE(attribute)->type = stun_alternate_server; + TNET_STUN_ATTRIBUTE(attribute)->length = payload_size; + + attribute->family = (tnet_stun_addr_family_t) (*(payloadPtr++)); + attribute->port = tnet_ntohs_2(payloadPtr); + payloadPtr+=2; + + if(attribute->family == stun_ipv4){ + uint32_t addr = tnet_htonl_2(payloadPtr); + memcpy(attribute->server, &addr, 4); + payloadPtr+=4; + } + else if(attribute->family == stun_ipv6){ + TSK_DEBUG_ERROR("IPv6 not supported yet."); + } + else{ + TSK_DEBUG_ERROR("UNKNOWN FAMILY."); + } + } + return self; +} + +static tsk_object_t* tnet_stun_attribute_altserver_dtor(tsk_object_t * self) +{ + tnet_stun_attribute_altserver_t *attribute = self; + if(attribute){ + } + return self; +} + +static const tsk_object_def_t tnet_stun_attribute_altserver_def_s = +{ + sizeof(tnet_stun_attribute_altserver_t), + tnet_stun_attribute_altserver_ctor, + tnet_stun_attribute_altserver_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_attribute_altserver_def_t = &tnet_stun_attribute_altserver_def_s; + diff --git a/tinyNET/src/stun/tnet_stun_attribute.h b/tinyNET/src/stun/tnet_stun_attribute.h new file mode 100644 index 0000000..66fd7ff --- /dev/null +++ b/tinyNET/src/stun/tnet_stun_attribute.h @@ -0,0 +1,329 @@ +/* +* 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_attribute.h + * @brief STUN2(RFC 5389) attribute parser. + * + * @author Mamadou Diop + * + * @date Created: Sat Nov 8 16:54:58 2009 mdiop + */ +#ifndef TNET_STUN_ATTRIBUTE_H +#define TNET_STUN_ATTRIBUTE_H + +#include "tinynet_config.h" + +#include "tnet_types.h" + +#include "tsk_object.h" +#include "tsk_buffer.h" +#include "tsk_sha1.h" + +/**@ingroup tnet_stun_group +* @def TNET_STUN_ATTRIBUTE +* Converts (cast) any STUN attribute to @ref tnet_stun_attribute_t pointer. +* @param self The attribute to convert (cast). +* @retval A pointer to @ref tnet_stun_attribute_t object. +*/ +TNET_BEGIN_DECLS + +#define TNET_STUN_ATTRIBUTE(self) ((tnet_stun_attribute_t*)(self)) + +/**@ingroup tnet_stun_group + * STUN IP family as per RFC 5389 subclause 15.1. +**/ +typedef enum tnet_stun_addr_family_e +{ + stun_ipv4 = 0x01, + stun_ipv6 = 0x02 +} +tnet_stun_addr_family_t; + +/**@ingroup tnet_stun_group + * STUN attribute types as per RFC 5389 subclause 18.2. +**/ +typedef enum tnet_stun_attribute_type_e +{ + /* === RFC 5389 - Comprehension-required range (0x0000-0x7FFF): + */ + stun_reserved = 0x0000, /**< (Reserved) */ + stun_mapped_address = 0x0001, /**< http://tools.ietf.org/html/rfc5389#page-32 */ + stun_response_address = 0x0002, /**< (Reserved; was RESPONSE-ADDRESS) */ + stun_change_address = 0x0003, /**< (Reserved; was CHANGE-ADDRESS) */ + stun_source_address = 0x0004, /**< (Reserved; was SOURCE-ADDRESS) */ + stun_changed_address = 0x0005, /**< (Reserved; was CHANGED-ADDRESS) */ + stun_username = 0x0006, /**< http://tools.ietf.org/html/rfc5389#page-34 */ + stun_password = 0x0007, /**< (Reserved; was PASSWORD) */ + stun_message_integrity = 0x0008, /**< http://tools.ietf.org/html/rfc5389#page-34 */ + stun_error_code = 0x0009, /**< http://tools.ietf.org/html/rfc5389#page-36 */ + stun_unknown_attributes = 0x000A, /**< http://tools.ietf.org/html/rfc5389#page-38 */ + stun_reflected_from = 0x000B, /**< (Reserved; was REFLECTED-FROM) */ + stun_realm = 0x0014, /**< http://tools.ietf.org/html/rfc5389#page-38 */ + stun_nonce = 0x0015, /**< http://tools.ietf.org/html/rfc5389#page-38 */ + stun_xor_mapped_address = 0x0020, /**< http://tools.ietf.org/html/rfc5389#page-33 */ + + /* === RFC 5389 - Comprehension-optional range (0x8000-0xFFFF) + */ + stun_software = 0x8022, /**< http://tools.ietf.org/html/rfc5389#page-39 */ + stun_alternate_server = 0x8023, /**< http://tools.ietf.org/html/rfc5389#page-39 */ + stun_fingerprint = 0x8028, /**< http://tools.ietf.org/html/rfc5389#page-36 */ + + /* === draft-ietf-behave-turn-16 + */ + stun_channel_number = 0x000C, /**< draft-ietf-behave-turn-16 - CHANNEL-NUMBER */ + stun_lifetime = 0x000D, /**< draft-ietf-behave-turn-16 - LIFETIME */ + stun_reserved2 = 0x0010, /**< draft-ietf-behave-turn-16 - Reserved (was BANDWIDTH) */ + stun_xor_peer_address = 0x0012, /**< draft-ietf-behave-turn-16 - XOR-PEER-ADDRESS */ + stun_data = 0x0013, /**< draft-ietf-behave-turn-16 - DATA */ + stun_xor_relayed_address = 0x0016, /**< draft-ietf-behave-turn-16 - XOR-RELAYED-ADDRESS */ + stun_even_port = 0x0018, /**< draft-ietf-behave-turn-16 - EVEN-PORT */ + stun_requested_transport = 0x0019, /**< draft-ietf-behave-turn-16 - REQUESTED-TRANSPORT */ + stun_dont_fragment = 0x001A, /**< draft-ietf-behave-turn-16 - DONT-FRAGMENT */ + stun_reserved3 = 0x0021, /**< draft-ietf-behave-turn-16 - Reserved (was TIMER-VAL) */ + stun_reservation_token = 0x0022, /**< draft-ietf-behave-turn-16 - RESERVATION-TOKEN */ +} +tnet_stun_attribute_type_t; + + +/**@ingroup tnet_stun_group + RFC 5389 - 15. STUN Attributes +*/ +typedef struct tnet_stun_attribute_s +{ + TSK_DECLARE_OBJECT; + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Value (variable) .... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + tnet_stun_attribute_type_t type; + uint16_t length; +} +tnet_stun_attribute_t; + +typedef tsk_list_t tnet_stun_attributes_L_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_def_t; + +#define TNET_STUN_DECLARE_ATTRIBUTE tnet_stun_attribute_t attribute + + +/**@ingroup tnet_stun_group + *RFC 5389 - 15.1. MAPPED-ADDRESS + */ +typedef struct tnet_stun_attribute_mapped_addr_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0| Family | Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | Address (32 bits or 128 bits) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + tnet_stun_addr_family_t family; + uint16_t port; + uint8_t address[16]; +} +tnet_stun_attribute_mapped_addr_t; + +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_mapped_addr_def_t; + + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.2. XOR-MAPPED-ADDRESS +*/ +typedef struct tnet_stun_attribute_xmapped_addr_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |x x x x x x x x| Family | X-Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | X-Address (Variable) + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + tnet_stun_addr_family_t family; + uint16_t xport; + uint8_t xaddress[16]; +} +tnet_stun_attribute_xmapped_addr_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_xmapped_addr_def_t; + + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.3. USERNAME. +*/ +typedef struct tnet_stun_attribute_username_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + + char* value; +} +tnet_stun_attribute_username_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_username_def_t; + + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.4. MESSAGE-INTEGRITY. +*/ +typedef struct tnet_stun_attribute_integrity_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + + tsk_sha1digest_t sha1digest; +} +tnet_stun_attribute_integrity_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_integrity_def_t; + + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.5. FINGERPRINT. +*/ +typedef struct tnet_stun_attribute_fingerprint_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + + uint32_t value; +} +tnet_stun_attribute_fingerprint_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_fingerprint_def_t; + +/**@ingroup tnet_stun_group + *RFC 5389 - 15.6. ERROR-CODE +*/ +typedef struct tnet_stun_attribute_errorcode_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved, should be 0 |Class| Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reason Phrase (variable) .. + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + uint8_t _class; + uint8_t number; + char* reason_phrase; +} +tnet_stun_attribute_errorcode_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_errorcode_def_t; + + + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.7. REALM. */ +typedef struct tnet_stun_attribute_realm_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + + char* value; +} +tnet_stun_attribute_realm_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_realm_def_t; + + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.8. NONCE. */ +typedef struct tnet_stun_attribute_nonce_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + + char* value; +} +tnet_stun_attribute_nonce_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_nonce_def_t; + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.9. UNKNOWN-ATTRIBUTES. */ +typedef struct tnet_stun_attribute_unknowns_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + + tsk_buffer_t *value; +} +tnet_stun_attribute_unknowns_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_unknowns_def_t; + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.10. SOFTWARE. */ +typedef struct tnet_stun_attribute_software_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + + char *value; +} +tnet_stun_attribute_software_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_software_def_t; + +/**@ingroup tnet_stun_group +* RFC 5389 - 15.11. ALTERNATE-SERVER. */ +typedef struct tnet_stun_attribute_altserver_s +{ + TNET_STUN_DECLARE_ATTRIBUTE; + + tnet_stun_addr_family_t family; + uint16_t port; + uint8_t server[128]; +} +tnet_stun_attribute_altserver_t; +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_attribute_altserver_def_t; + + +tnet_stun_attribute_t* tnet_stun_attribute_deserialize(const void* data, tsk_size_t size); +int tnet_stun_attribute_serialize(const tnet_stun_attribute_t* attribute, tsk_buffer_t *output); +void tnet_stun_attribute_pad(const tnet_stun_attribute_t* attribute, tsk_buffer_t *output); + + + + +tnet_stun_attribute_t* tnet_stun_attribute_create(); +tnet_stun_attribute_mapped_addr_t* tnet_stun_attribute_mapped_address_create(const void* payload, tsk_size_t payload_size); +tnet_stun_attribute_xmapped_addr_t* tnet_stun_attribute_xmapped_address_create(const void* payload, tsk_size_t payload_size); +tnet_stun_attribute_username_t* tnet_stun_attribute_username_create(const void* payload, tsk_size_t payload_size); +tnet_stun_attribute_integrity_t* tnet_stun_attribute_integrity_create(const void* payload, tsk_size_t payload_size); +tnet_stun_attribute_fingerprint_t* tnet_stun_attribute_fingerprint_create(uint32_t fingerprint); +tnet_stun_attribute_errorcode_t* tnet_stun_attribute_errorcode_create(const void* payload, tsk_size_t payload_size); +tnet_stun_attribute_realm_t* tnet_stun_attribute_realm_create(const void* payload, tsk_size_t payload_size); +tnet_stun_attribute_nonce_t* tnet_stun_attribute_nonce_create(const void* payload, tsk_size_t payload_size); +tnet_stun_attribute_unknowns_t* tnet_stun_attribute_unknowns_create(const void* payload, tsk_size_t payload_size); +tnet_stun_attribute_software_t* tnet_stun_attribute_software_create(const void* payload, tsk_size_t payload_size); +tnet_stun_attribute_altserver_t* tnet_stun_attribute_altserver_create(const void* payload, tsk_size_t payload_size); + + +TNET_END_DECLS + +#endif /* TNET_STUN_ATTRIBUTE_H */ + diff --git a/tinyNET/src/stun/tnet_stun_message.c b/tinyNET/src/stun/tnet_stun_message.c new file mode 100644 index 0000000..9769092 --- /dev/null +++ b/tinyNET/src/stun/tnet_stun_message.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_message.c + * @brief STUN2 (RFC 5389) message parser. + * + * @author Mamadou Diop + * + * @date Created: Sat Nov 8 16:54:58 2009 mdiop + */ +#include "tnet_stun_message.h" + +#include "tnet_stun.h" + +#include "../tnet_types.h" +#include "../tnet_endianness.h" +#include "../turn/tnet_turn_attribute.h" + +#include "tsk_memory.h" +#include "tsk_hmac.h" +#include "tsk_string.h" +#include "tsk_ppfcs32.h" + +#include + + +/**@ingroup tnet_stun_group +* Creates new STUN message. +* @retval @ref tnet_stun_message_t object. +* @sa tnet_stun_message_create_null. +*/ + +tnet_stun_message_t* tnet_stun_message_create(const char* username, const char* password) +{ + return tsk_object_new(tnet_stun_message_def_t, username, password); +} + +/**@ingroup tnet_stun_group +* Creates new STUN message. +* @retval @ref tnet_stun_message_t object. +* @sa tnet_stun_message_create. +*/ +tnet_stun_message_t* tnet_stun_message_create_null() +{ + return tnet_stun_message_create(tsk_null, tsk_null); +} + +#define SERIALIZE_N_ADD_ATTRIBUTE(att_name, payload, payload_size) \ + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_##att_name##_create(payload, payload_size); \ + tnet_stun_attribute_serialize(attribute, output); \ + tnet_stun_attribute_pad(attribute, output); \ + TSK_OBJECT_SAFE_FREE(attribute); + +/**@ingroup tnet_stun_group + * Serializes a STUN message as binary data. + * @param [in,out] self The STUN message to serialize. + * @retval A buffer holding the binary data (result) if serialization succeed and zero otherwise. +**/ +tsk_buffer_t* tnet_stun_message_serialize(const tnet_stun_message_t *self) +{ + tsk_buffer_t *output = 0; + tnet_stun_attribute_t *attribute; + unsigned compute_integrity = self->integrity; + + if(!self){ + goto bail; + } + + output = tsk_buffer_create_null(); + + /* RFC 5389 - 6. STUN Message Structure + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |0 0| STUN Message Type | Message Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Magic Cookie | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | Transaction ID (96 bits) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + /* STUN Message Type + */ + { + uint16_t type = tnet_htons(self->type); + tsk_buffer_append(output, &(type), 2); + } + + /* Message Length ==> Will be updated after attributes have been added. */ + { + uint16_t length = 0; + tsk_buffer_append(output, &(length), 2); + } + + /* Magic Cookie + */ + { + uint32_t cookie = tnet_htonl(self->cookie); + tsk_buffer_append(output, &(cookie), 4); + } + + + /* Transaction ID (96 bits==>16bytes) + */ + tsk_buffer_append(output, self->transaction_id, TNET_STUN_TRANSACID_SIZE); + + /* DONT-FRAGMENT + */ + if(self->dontfrag){ + attribute = (tnet_stun_attribute_t *)tnet_turn_attribute_dontfrag_create(); + tnet_stun_attribute_serialize(attribute, output); + TSK_OBJECT_SAFE_FREE(attribute); + } + + /*=== Attributes === + */ + { + tsk_list_item_t *item; + tsk_list_foreach(item, self->attributes) + { + attribute = item->data; + tnet_stun_attribute_serialize(attribute, output); + tnet_stun_attribute_pad(attribute, output); + } + } + + /* AUTHENTICATION */ + if(self->realm && self->nonce){ + SERIALIZE_N_ADD_ATTRIBUTE(username, self->username, tsk_strlen(self->username)); + SERIALIZE_N_ADD_ATTRIBUTE(realm, self->realm, tsk_strlen(self->realm)); + SERIALIZE_N_ADD_ATTRIBUTE(nonce, self->nonce, tsk_strlen(self->nonce)); + + compute_integrity = 1; + } + + /* Message Length: The message length MUST contain the size, in bytes, of the message + not including the 20-byte STUN header. + */ + { + uint16_t length = (output->size) - TNET_STUN_HEADER_SIZE; + if(self->fingerprint) + length += (2/* Type */ + 2 /* Length */+ 4 /* FINGERPRINT VALUE*/); + + if(compute_integrity) + length += (2/* Type */ + 2 /* Length */+ TSK_SHA1_DIGEST_SIZE /* INTEGRITY VALUE*/); + + *(((uint16_t*)output->data)+1) = tnet_htons(length); + } + + /* MESSAGE-INTEGRITY */ + if(compute_integrity){ + /* RFC 5389 - 15.4. MESSAGE-INTEGRITY + The MESSAGE-INTEGRITY attribute contains an HMAC-SHA1 [RFC2104] of the STUN message. + + For long-term credentials ==> key = MD5(username ":" realm ":" SASLprep(password)) + For short-term credentials ==> key = SASLprep(password) + FIXME: what about short term credentials? + FIXME: what about SASLprep + */ + char* keystr = 0; + tsk_sha1digest_t hmac; + tsk_md5digest_t md5; + + tsk_sprintf(&keystr, "%s:%s:%s", self->username, self->realm, self->password); + TSK_MD5_DIGEST_CALC(keystr, tsk_strlen(keystr), md5); + hmac_sha1digest_compute(output->data, output->size, (const char*)md5, TSK_MD5_DIGEST_SIZE, hmac); + + SERIALIZE_N_ADD_ATTRIBUTE(integrity, hmac, TSK_SHA1_DIGEST_SIZE); + + TSK_FREE(keystr); + } + + /* FINGERPRINT */ + if(self->fingerprint){ + /* RFC 5389 - 15.5. FINGERPRINT + The FINGERPRINT attribute MAY be present in all STUN messages. The + value of the attribute is computed as the CRC-32 of the STUN message + up to (but excluding) the FINGERPRINT attribute itself, XOR'ed with + the 32-bit value 0x5354554e + */ + uint32_t fingerprint = tsk_pppfcs32(TSK_PPPINITFCS32, output->data, output->size); + fingerprint ^= 0x5354554e; + fingerprint = tnet_htonl(fingerprint); + + attribute = (tnet_stun_attribute_t *)tnet_stun_attribute_fingerprint_create(fingerprint); + tnet_stun_attribute_serialize(attribute, output); + TSK_OBJECT_SAFE_FREE(attribute); + } + +bail: + return output; +} + + +/**@ingroup tnet_stun_group + * + * Deserializes a STUN message from binary data. + * + * @param [in,out] data A pointer to the binary data. + * @param size The size of the binary data. + * + * @retval A STUN message if deserialization succeed or NULL otherwise. +**/ +tnet_stun_message_t* tnet_stun_message_deserialize(const uint8_t *data, tsk_size_t size) +{ + tnet_stun_message_t *message = 0; + uint8_t* dataPtr, *dataEnd; + + + if(!data || (size < TNET_STUN_HEADER_SIZE) || !TNET_IS_STUN2(data)) + { + goto bail; + } + + dataPtr = (uint8_t*)data; + dataEnd = (dataPtr + size); + + message = tnet_stun_message_create_null(); + + /* Message Type + */ + message->type = (tnet_stun_message_type_t)tnet_ntohs_2(dataPtr); + dataPtr += 2; + + /* Message Length + */ + message->length = tnet_ntohs_2(dataPtr); + dataPtr += 2; + + /* Check message validity + */ + if((message->length + TNET_STUN_HEADER_SIZE) != size) + { + TSK_OBJECT_SAFE_FREE(message); + goto bail; + } + + /* Magic Cookie + ==> already set by the constructor and checked by @ref TNET_IS_STUN2 + */ + dataPtr += 4; + + /* Transaction ID + */ + memcpy(message->transaction_id, dataPtr, TNET_STUN_TRANSACID_SIZE); + dataPtr += TNET_STUN_TRANSACID_SIZE; + + /* == Parse attributes + */ + while(dataPtr < dataEnd){ + tnet_stun_attribute_t *attribute = tnet_stun_attribute_deserialize(dataPtr, (dataEnd - dataPtr)); + if(attribute){ + tsk_size_t att_size = (attribute->length + 2 /* Type*/ + 2/* Length */); + att_size += (att_size%4) ? 4-(att_size%4) : 0; // Skip zero bytes used to pad the attribute. + + dataPtr += att_size; + tsk_list_push_back_data(message->attributes, (void**)&attribute); + + continue; + } + else{ + continue; + } + + + + + } + + +bail: + return message; +} + +/**@ingroup tnet_stun_group +* Adds an attribute to a STUN message. +* @param self The STUN message into which to add the attribute. +* @param attribute The attribute to add. +* @retval Zero if succeed and non-zero error code otherwise. +*/ +int tnet_stun_message_add_attribute(tnet_stun_message_t *self, tnet_stun_attribute_t** attribute) +{ + //if(self && attribute) + { + tsk_list_push_back_data(self->attributes, (void**)attribute); + return 0; + } + return -1; +} + +/**@ingroup tnet_stun_group +* Gets a STUN attribute from a message. +* @param self The message from which to get the attribute. +* @param type The type of the attribute to retrieve. +* @retval @ref tnet_stun_attribute_t object if found and NULL otherwise. +*/ +const tnet_stun_attribute_t* tnet_stun_message_get_attribute(const tnet_stun_message_t *self, tnet_stun_attribute_type_t type) +{ + tnet_stun_attribute_t* attribute; + + if(self && !TSK_LIST_IS_EMPTY(self->attributes)){ + tsk_list_item_t *item; + tsk_list_foreach(item, self->attributes){ + if((attribute = item->data) && attribute->type == type){ + return attribute; + } + } + } + return 0; +} + +/**@ingroup tnet_stun_group +* Gets the STUN error-code attribute value from the message. +* @param self The STUN message from which to get the error code. +* @retval The error code if the message contain such attribute or -1 otherwise. +*/ +short tnet_stun_message_get_errorcode(const tnet_stun_message_t *self) +{ + const tnet_stun_attribute_errorcode_t* error = (const tnet_stun_attribute_errorcode_t*)tnet_stun_message_get_attribute(self, stun_error_code); + if(error){ + return ((error->_class*100) + error->number); + } + return -1; +} + +/**@ingroup tnet_stun_group +* Gets the STUN @b realm attribute value from the message. +* @param self The STUN message from which to get the @b realm. +* @retval The @b realm as a string pointer code if the message contain such attribute or NULL otherwise. +*/ +const char* tnet_stun_message_get_realm(const tnet_stun_message_t *self) +{ + const tnet_stun_attribute_realm_t* realm = (const tnet_stun_attribute_realm_t*)tnet_stun_message_get_attribute(self, stun_realm); + if(realm){ + return realm->value; + } + return 0; +} + +/**@ingroup tnet_stun_group +* Gets the STUN @b nonce attribute value from the message. +* @param self The STUN message from which to get the @b nonce. +* @retval The @b nonce as a string pointer code if the message contain such attribute or NULL otherwise. +*/ +const char* tnet_stun_message_get_nonce(const tnet_stun_message_t *self) +{ + const tnet_stun_attribute_nonce_t* nonce = (const tnet_stun_attribute_nonce_t*)tnet_stun_message_get_attribute(self, stun_nonce); + if(nonce){ + return nonce->value; + } + return 0; +} + +/**@ingroup tnet_stun_group +* Gets the STUN @b lifetime attribute value from the message. +* @param self The STUN message from which to get the @b lifetime. +* @retval The @b lifetime (any positive value) if the message contain such attribute or -1 otherwise. +*/ +int32_t tnet_stun_message_get_lifetime(const tnet_stun_message_t *self) +{ + const tnet_turn_attribute_lifetime_t* lifetime = (const tnet_turn_attribute_lifetime_t*)tnet_stun_message_get_attribute(self, stun_lifetime); + if(lifetime){ + return lifetime->value; + } + return -1; +} + + + + + + + + + +//================================================================================================= +// STUN2 MESSAGE object definition +// +static tsk_object_t* tnet_stun_message_ctor(tsk_object_t * self, va_list * app) +{ + tnet_stun_message_t *message = self; + if(message){ + message->username = tsk_strdup(va_arg(*app, const char*)); + message->password = tsk_strdup(va_arg(*app, const char*)); + + message->cookie = TNET_STUN_MAGIC_COOKIE; + message->attributes = tsk_list_create(); + + message->fingerprint = 1; + message->integrity = 0; + } + return self; +} + +static tsk_object_t* tnet_stun_message_dtor(tsk_object_t * self) +{ + tnet_stun_message_t *message = self; + if(message){ + TSK_FREE(message->username); + TSK_FREE(message->password); + TSK_FREE(message->realm); + TSK_FREE(message->nonce); + + TSK_OBJECT_SAFE_FREE(message->attributes); + } + + return self; +} + +static const tsk_object_def_t tnet_stun_message_def_s = +{ + sizeof(tnet_stun_message_t), + tnet_stun_message_ctor, + tnet_stun_message_dtor, + tsk_null, +}; +const tsk_object_def_t *tnet_stun_message_def_t = &tnet_stun_message_def_s; + diff --git a/tinyNET/src/stun/tnet_stun_message.h b/tinyNET/src/stun/tnet_stun_message.h new file mode 100644 index 0000000..862c7b6 --- /dev/null +++ b/tinyNET/src/stun/tnet_stun_message.h @@ -0,0 +1,240 @@ +/* +* 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_message.h + * @brief STUN2 (RFC 5389) message parser. + * + * @author Mamadou Diop + * + * @date Created: Sat Nov 8 16:54:58 2009 mdiop + */ +#ifndef TNET_STUN_MESSAGE_H +#define TNET_STUN_MESSAGE_H + +#include "tinynet_config.h" +#include "stun/tnet_stun_attribute.h" + +#include "tsk_buffer.h" + +TNET_BEGIN_DECLS + +#define TNET_STUN_CLASS_REQUEST_MASK (0x0000) +#define TNET_STUN_CLASS_INDICATION_MASK (0x0010) +#define TNET_STUN_CLASS_SUCCESS_MASK (0x0100) +#define TNET_STUN_CLASS_ERROR_MASK (0x0110) + +/**@ingroup tnet_stun_group +* @def TNET_STUN_RESPONSE_IS_REQUEST +* Checks whether the STUN message is a request or not. +*/ +/**@ingroup tnet_stun_group +* @def TNET_STUN_RESPONSE_IS_INDICATION +* Checks whether the STUN message is an indicaton message or not. +*/ +/**@ingroup tnet_stun_group +* @def TNET_STUN_RESPONSE_IS_SUCCESS +* Checks whether the STUN message is a success response or not. +*/ +/**@ingroup tnet_stun_group +* @def TNET_STUN_RESPONSE_IS_ERROR +* Checks whether the STUN message is an error response or not. +*/ +#define TNET_STUN_RESPONSE_IS_REQUEST(self) ((self->type & TNET_STUN_CLASS_REQUEST_MASK) == TNET_STUN_CLASS_REQUEST_MASK) +#define TNET_STUN_RESPONSE_IS_INDICATION(self) ((self->type & TNET_STUN_CLASS_INDICATION_MASK) == TNET_STUN_CLASS_INDICATION_MASK) +#define TNET_STUN_RESPONSE_IS_SUCCESS(self) ((self->type & TNET_STUN_CLASS_SUCCESS_MASK) == TNET_STUN_CLASS_SUCCESS_MASK) +#define TNET_STUN_RESPONSE_IS_ERROR(self) ((self->type & TNET_STUN_CLASS_ERROR_MASK) == TNET_STUN_CLASS_ERROR_MASK) + +/**@ingroup tnet_stun_group + * Checks if the pointer to the buffer hold a STUN header by checking that it starts with 0b00 and contain the magic cookie. + * As per RFC 5389 subclause 19: Explicitly point out that the most significant 2 bits of STUN are + * 0b00, allowing easy differentiation with RTP packets when used with ICE. + * As per RFC 5389 subclause 6: The magic cookie field MUST contain the fixed value 0x2112A442 in + * network byte order. + * + * @param PU8 The pointer to the buffer holding the STUN raw data. +**/ +#define TNET_IS_STUN2(PU8) \ + (((PU8)[0] & 0xc0) == 0x00) && \ + ( (*(((uint32_t *)(PU8))+1)) == tnet_htonl(TNET_STUN_MAGIC_COOKIE) ) + +/**@ingroup tnet_stun_group + * STUN trasactionn ID size (96bits = 12bytes). +*/ +#define TNET_STUN_TRANSACID_SIZE 12 + +/**@ingroup tnet_stun_group + * Defines an alias representing the STUN transaction id type. +**/ +typedef uint8_t tnet_stun_transacid_t[TNET_STUN_TRANSACID_SIZE]; + +/**@ingroup tnet_stun_group + * List of all supported STUN classes as per RFC 5389 subcaluse 6. +**/ +typedef enum tnet_stun_class_type_e +{ + stun_class_request = 0x00, /**< Request class: 0b00 */ + stun_class_indication = 0x01, /**< Indication class: 0b01 */ + stun_class_success_response = 0x02, /**< Success response class: 0b10 */ + stun_class_error_response = 0x03, /**< Error/failure response class: 0b11 */ +} +tnet_stun_class_type_t; + +/**@ingroup tnet_stun_group + * List of all supported STUN methods. + * RFC 5389 only define one method(Bining). All other methods have been defined + * by TURN (draft-ietf-behave-turn-16 and draft-ietf-behave-turn-tcp-05). +**/ +typedef enum tnet_stun_method_type_e +{ + stun_method_binding = 0x0001, /**< RFC 5389 - Binding method: 0b000000000001 */ + + stun_method_allocate = 0x0003, /**< draft-ietf-behave-turn-16 - Allocate (only request/response semantics defined) */ + stun_method_refresh = 0x0004, /**< draft-ietf-behave-turn-16 - Refresh (only request/response semantics defined) */ + stun_method_send = 0x0006, /**< draft-ietf-behave-turn-16 - Send (only indication semantics defined) */ + stun_method_data = 0x0007, /**< draft-ietf-behave-turn-16 - Data (only indication semantics defined) */ + stun_method_createpermission = 0x0008, /**< draft-ietf-behave-turn-16 - CreatePermission (only request/response semantics defined */ + stun_method_channelbind = 0x0009, /**< draft-ietf-behave-turn-16 - ChannelBind (only request/response semantics defined) */ +} +tnet_stun_method_type_t; + +/**@ingroup tnet_stun_group +* List of all supported STUN types. +*/ +typedef enum tnet_stun_message_type_e +{ + /* RFC 5389 - 6. STUN Message Structure + + The message type defines the message class (request, success + response, failure response, or indication) and the message method + (the primary function) of the STUN message. Although there are four + message classes, there are only two types of transactions in STUN: + request/response transactions (which consist of a request message and + a response message) and indication transactions (which consist of a + single indication message). Response classes are split into error + and success responses to aid in quickly processing the STUN message. + + The message type field is decomposed further into the following + structure: + + 0 1 + 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +--+--+-+-+-+-+-+-+-+-+-+-+-+-+ + |M |M |M|M|M|C|M|M|M|C|M|M|M|M| + |11|10|9|8|7|1|6|5|4|0|3|2|1|0| + +--+--+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + stun_binding_request = (stun_method_binding | TNET_STUN_CLASS_REQUEST_MASK), + stun_binding_indication = (stun_method_binding | TNET_STUN_CLASS_INDICATION_MASK), + stun_binding_success_response = (stun_method_binding | TNET_STUN_CLASS_SUCCESS_MASK), + stun_binding_error_response = (stun_method_binding | TNET_STUN_CLASS_ERROR_MASK), + + stun_allocate_request = (stun_method_allocate | TNET_STUN_CLASS_REQUEST_MASK), + stun_allocate_indication = (stun_method_allocate | TNET_STUN_CLASS_INDICATION_MASK), + stun_allocate_success_response = (stun_method_allocate | TNET_STUN_CLASS_SUCCESS_MASK), + stun_allocate_error_response = (stun_method_allocate | TNET_STUN_CLASS_ERROR_MASK), + + stun_refresh_request = (stun_method_refresh | TNET_STUN_CLASS_REQUEST_MASK), + stun_refresh_indication = (stun_method_refresh | TNET_STUN_CLASS_INDICATION_MASK), + stun_refresh_success_response = (stun_method_refresh | TNET_STUN_CLASS_SUCCESS_MASK), + stun_refresh_error_response = (stun_method_refresh | TNET_STUN_CLASS_ERROR_MASK), + + stun_send_indication = (stun_method_send | TNET_STUN_CLASS_INDICATION_MASK), + + stun_data_indication = (stun_method_data | TNET_STUN_CLASS_INDICATION_MASK), + + stun_createpermission_request = (stun_method_createpermission | TNET_STUN_CLASS_REQUEST_MASK), + stun_createpermission_indication = (stun_method_createpermission | TNET_STUN_CLASS_INDICATION_MASK), + stun_createpermission_success_response = (stun_method_createpermission | TNET_STUN_CLASS_SUCCESS_MASK), + stun_createpermission_error_response = (stun_method_createpermission | TNET_STUN_CLASS_ERROR_MASK), + + stun_channelbind_request = (stun_method_channelbind | TNET_STUN_CLASS_REQUEST_MASK), + stun_channelbind_indication = (stun_method_channelbind | TNET_STUN_CLASS_INDICATION_MASK), + stun_channelbind_success_response = (stun_method_channelbind | TNET_STUN_CLASS_SUCCESS_MASK), + stun_channelbind_error_response = (stun_method_channelbind | TNET_STUN_CLASS_ERROR_MASK), +} +tnet_stun_message_type_t; + +/**@ingroup tnet_stun_group + * + * STUN Message structure as per RFC 5389 subclause 6. + * http://tools.ietf.org/html/rfc5389#section-6 +*/ +typedef struct tnet_stun_message_s +{ + TSK_DECLARE_OBJECT; + + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |0 0| STUN Message Type | Message Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Magic Cookie | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | Transaction ID (96 bits) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + tnet_stun_message_type_t type; + uint16_t length; + uint32_t cookie; + tnet_stun_transacid_t transaction_id; + + unsigned fingerprint:1; + unsigned integrity:1; + unsigned dontfrag:1; + + char* username; + char* password; + char* realm; + char* nonce; + + tnet_stun_attributes_L_t *attributes; /**< List of all attributes associated to this message */ +} +tnet_stun_message_t; + +typedef tnet_stun_message_t tnet_stun_response_t; +typedef tnet_stun_message_t tnet_stun_request_t; + +tsk_buffer_t* tnet_stun_message_serialize(const tnet_stun_message_t *message); +tnet_stun_message_t* tnet_stun_message_deserialize(const uint8_t *data, tsk_size_t size); +int tnet_stun_message_add_attribute(tnet_stun_message_t *self, tnet_stun_attribute_t** attribute); +const tnet_stun_attribute_t* tnet_stun_message_get_attribute(const tnet_stun_message_t *self, tnet_stun_attribute_type_t type); +short tnet_stun_message_get_errorcode(const tnet_stun_message_t *self); +const char* tnet_stun_message_get_realm(const tnet_stun_message_t *self); +const char* tnet_stun_message_get_nonce(const tnet_stun_message_t *self); +int32_t tnet_stun_message_get_lifetime(const tnet_stun_message_t *self); + + +tnet_stun_message_t* tnet_stun_message_create(const char* username, const char* password); +tnet_stun_message_t* tnet_stun_message_create_null(); + +TINYNET_GEXTERN const tsk_object_def_t *tnet_stun_message_def_t; + + +TNET_END_DECLS + + +#endif /* TNET_STUN_MESSAGE_H */ + -- cgit v1.1