summaryrefslogtreecommitdiffstats
path: root/tinyNET/src/stun/tnet_stun_message.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyNET/src/stun/tnet_stun_message.c')
-rw-r--r--tinyNET/src/stun/tnet_stun_message.c440
1 files changed, 440 insertions, 0 deletions
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 <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tnet_stun_message.c
+ * @brief STUN2 (RFC 5389) message parser.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @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 <string.h>
+
+
+/**@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;
+
OpenPOWER on IntegriCloud