doubango/tinyNET/src/dns/tnet_dns_message.c
c732d49e
 #if HAVE_CRT
 #define _CRTDBG_MAP_ALLOC 
 #include <stdlib.h> 
 #include <crtdbg.h>
 #endif //HAVE_CRT
 /*
 * Copyright (C) 2017, University of the Basque Country (UPV/EHU)
 * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com>
 *
 * The original file was part of Open Source Doubango Framework
 * Copyright (C) 2010-2011 Mamadou Diop.
 * Copyright (C) 2012 Doubango Telecom <http://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_dns_message.h
  * @brief DNS Message holding RRs (RFC 1035).
  *
  */
 #include "tnet_dns_message.h"
 
 #include "../tnet_utils.h"
 #include "../tnet_endianness.h"
 
 #include "tsk_memory.h"
 #include "tsk_string.h"
 #include "tsk_debug.h"
 
 #include <string.h>
 
 /**@ingroup tnet_dns_group
 * Creates new DNS message.
 * @sa tnet_dns_message_create_null.
 */
 tnet_dns_message_t* tnet_dns_message_create(const char* qname, tnet_dns_qclass_t qclass, tnet_dns_qtype_t qtype, tsk_bool_t isquery)
 {
 	return tsk_object_new(tnet_dns_message_def_t, qname, qclass, qtype, isquery);
 }
 
 /**@ingroup tnet_dns_group
 * Creates new DNS message.
 * @sa tnet_dns_message_create.
 */
 tnet_dns_message_t* tnet_dns_message_create_null()
 {
 	return tnet_dns_message_create(tsk_null, qclass_any, qtype_any, tsk_false);
 }
 
 /**@ingroup tnet_dns_group
 * Creates new DNS response message.
 * @sa tnet_dns_query_create.
 */
 tnet_dns_response_t* tnet_dns_response_create(const char* qname, tnet_dns_qclass_t qclass, tnet_dns_qtype_t qtype)
 {
 	return tnet_dns_message_create(qname, qclass, qtype, tsk_false);
 }
 
 /**@ingroup tnet_dns_group
 * Creates new DNS query message.
 * @sa tnet_dns_response_create.
 */
 tnet_dns_query_t* tnet_dns_query_create(const char* qname, tnet_dns_qclass_t qclass, tnet_dns_qtype_t qtype)
 {
 	return tnet_dns_message_create(qname, qclass, qtype, tsk_true);
 }
 
 /**@ingroup tnet_dns_group
 * Serializes a DNS message in binary data.
 * @param message The DNS message to seriablize.
 * @retval The binary buffer containong the message if succeed. Otherwise, @a tsk_null is returned.
 * @sa tnet_dns_message_deserialize.
 */
 tsk_buffer_t* tnet_dns_message_serialize(const tnet_dns_message_t *message)
 {
 	tsk_buffer_t* output = tsk_null;
 	uint16_t _2bytes;
 	tsk_list_item_t *item;
 
 	/* Check message validity */
 	if (!message){
 		goto bail;
 	}
 
 	/* Creates empty buffer */
 	output = tsk_buffer_create_null();
 
 	/* ==============================
 	*	HEADER
 	*/
 	//tsk_buffer_append(output, &(message->Header), sizeof(message->Header));
 
 	/* ID */
 	_2bytes = tnet_ntohs(message->Header.ID);
 	tsk_buffer_append(output, &(_2bytes), 2);
 	/* |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   | */
 	{
 		uint16_t temp, _2bytes = 0;
 
 		temp = message->Header.QR, temp <<= 15;
 		_2bytes |= temp;
 
 		temp = message->Header.OPCODE, temp <<= 11;
 		_2bytes |= temp;
 
 		temp = message->Header.AA, temp <<= 10;
 		_2bytes |= temp;
 
 		temp = message->Header.TC, temp <<= 9;
 		_2bytes |= temp;
 
 		temp = message->Header.RD, temp <<= 8;
 		_2bytes |= temp;
 
 		temp = message->Header.RA, temp <<= 7;
 		_2bytes |= temp;
 
 		temp = message->Header.Z, temp <<= 4;
 		_2bytes |= temp;
 
 		temp = message->Header.RCODE, temp <<= 4;
 		_2bytes |= temp;
 
 		_2bytes = tnet_ntohs(_2bytes);
 		tsk_buffer_append(output, &(_2bytes), 2);
 	}
 	/* QDCOUNT */
 	_2bytes = tnet_ntohs(message->Header.QDCOUNT);
 	tsk_buffer_append(output, &(_2bytes), 2);
 	/* ANCOUNT */
 	_2bytes = tnet_ntohs(message->Header.ANCOUNT);
 	tsk_buffer_append(output, &(_2bytes), 2);
 	/* NSCOUNT */
 	_2bytes = tnet_ntohs(message->Header.NSCOUNT);
 	tsk_buffer_append(output, &(_2bytes), 2);
 	/* ARCOUNT */
 	_2bytes = tnet_ntohs(message->Header.ARCOUNT);
 	tsk_buffer_append(output, &(_2bytes), 2);
 
 
 	/* ==============================
 	*	QUESTION
 	*/
 	if (TNET_DNS_MESSAGE_IS_QUERY(message))
 	{
 		/* QNAME */
 		tnet_dns_rr_qname_serialize(message->Question.QNAME, output);
 		/* QTYPE */
 		_2bytes = tnet_ntohs(message->Question.QTYPE);
 		tsk_buffer_append(output, &(_2bytes), 2);
 		/* QCLASS */
 		_2bytes = tnet_ntohs(message->Question.QCLASS);
 		tsk_buffer_append(output, &(_2bytes), 2);
 	}
 
 	/* ==============================
 	*	ANSWERS
 	*/
 	tsk_list_foreach(item, message->Answers)
 	{
 		tnet_dns_rr_serialize((tnet_dns_rr_t *)item->data, output);
 	}
 
 	/* ==============================
 	*	AUTHORITIES
 	*/
 	tsk_list_foreach(item, message->Authorities)
 	{
 		tnet_dns_rr_serialize((tnet_dns_rr_t *)item->data, output);
 	}
 
 	/* ==============================
 	*	ADDITIONALS
 	*/
 	tsk_list_foreach(item, message->Additionals)
 	{
 		tnet_dns_rr_serialize((tnet_dns_rr_t *)item->data, output);
 	}
 
 
 bail:
 	return output;
 }
 
 /**@ingroup tnet_dns_group
 * Deserialize a STUN message from binary data.
 * @param data A pointer to the binary data.
 * @param size The size of the bnary data buffer.
 * @retval @ref tnet_dns_message_t object if succeed or NULL otherwise.
 * @sa @ref tnet_dns_message_serialize.
 */
 tnet_dns_message_t* tnet_dns_message_deserialize(const uint8_t *data, tsk_size_t size)
 {
 	tnet_dns_message_t *message = 0;
 	uint8_t *dataPtr, *dataEnd, *dataStart;
 	uint16_t i;
 	tsk_size_t offset = 0;
 
 	if (!data || !size){
 		goto bail;
 	}
 
 	dataPtr = (uint8_t*)data;
 	dataStart = dataPtr;
 	dataEnd = (dataStart + size);
 
 	message = tnet_dns_message_create_null();
 
 	/* === HEADER ===*/
 	/* ID */
 	message->Header.ID = tnet_ntohs_2(dataPtr);
 	dataPtr += 2;
 	/* |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   | */
 	{
 		uint16_t flags = tnet_ntohs_2(dataPtr);
 
 		message->Header.QR = (flags >> 15);
 		message->Header.OPCODE = ((flags >> 11) & 0x000F);
 		message->Header.AA = ((flags >> 10) & 0x0001);
 		message->Header.TC = ((flags >> 9) & 0x0001);
 		message->Header.RD = ((flags >> 8) & 0x0001);
 		message->Header.RA = ((flags >> 7) & 0x0001);
 		message->Header.Z = ((flags >> 4) & 0x0007);
 		message->Header.RCODE = (flags & 0x000F);
 
 		dataPtr += 2;
 	}
 	/* QDCOUNT */
 	message->Header.QDCOUNT = tnet_ntohs_2(dataPtr);
 	dataPtr += 2;
 	/* ANCOUNT */
 	message->Header.ANCOUNT = tnet_ntohs_2(dataPtr);
 	dataPtr += 2;
 	/* NSCOUNT */
 	message->Header.NSCOUNT = tnet_ntohs_2(dataPtr);
 	dataPtr += 2;
 	/* ARCOUNT */
 	message->Header.ARCOUNT = tnet_ntohs_2(dataPtr);
 	dataPtr += 2;
 
 	/* === Queries ===*/
 	offset = (tsk_size_t)(dataPtr - dataStart);
 	for (i = 0; i < message->Header.QDCOUNT; i++)
 	{
 		/* Do not need to parse queries in the response ==> silently ignore */
 		char* name = 0;
 		tnet_dns_rr_qname_deserialize(dataStart, &name, &offset); /* QNAME */
 		dataPtr += offset;
 		dataPtr += 4, offset += 4; /* QTYPE + QCLASS */
 		TSK_FREE(name);
 	}
 	dataPtr = (dataStart + offset); /* TODO: remove ==> obly for debug tests */
 
 	/* === Answers ===*/
 	offset = (tsk_size_t)(dataPtr - dataStart);
 	for (i = 0; i < message->Header.ANCOUNT; i++)
 	{
 		tnet_dns_rr_t* rr = tnet_dns_rr_deserialize(dataStart, (tsk_size_t)(dataEnd - dataPtr), &offset);
 		if (rr){
 			if (!message->Answers){
 				message->Answers = tsk_list_create();
 			}
 			/* Push in descending order (useful for NAPTR and SRV records). */
 			tsk_list_push_descending_data(message->Answers, (void**)&rr);
 		}
 	}
 	dataPtr = (dataStart + offset);
 
 	/* === Authorities ===*/
 	offset = (tsk_size_t)(dataPtr - dataStart);
 	for (i = 0; i < message->Header.NSCOUNT; i++)
 	{
 		tnet_dns_rr_t* rr = tnet_dns_rr_deserialize(dataStart, (tsk_size_t)(dataEnd - dataPtr), &offset);
 		if (rr){
 			if (!message->Authorities){
 				message->Authorities = tsk_list_create();
 			}
 			tsk_list_push_back_data(message->Authorities, (void**)&rr);
 		}
 	}
 	dataPtr = (dataStart + offset);
 
 	/* === Additionals ===*/
 	offset = (tsk_size_t)(dataPtr - dataStart);
 	for (i = 0; i < message->Header.ARCOUNT; i++)
 	{
 		tnet_dns_rr_t* rr = tnet_dns_rr_deserialize(dataStart, (tsk_size_t)(dataEnd - dataPtr), &offset);
 		if (rr){
 			if (!message->Additionals){
 				message->Additionals = tsk_list_create();
 			}
 			tsk_list_push_back_data(message->Additionals, (void**)&rr);
 		}
 	}
 	dataPtr = (dataStart + offset);
 
 
 bail:
 	return message;
 }
 
 //=================================================================================================
 //	[[DNS MESSAGE]] object definition
 //
 static tsk_object_t* tnet_dns_message_ctor(tsk_object_t * self, va_list * app)
 {
 	tnet_dns_message_t *message = self;
 	if (message){
 		static uint16_t __dnsmessage_unique_id = 0;
 
 		const char* qname = va_arg(*app, const char*);
 		tnet_dns_qclass_t qclass = va_arg(*app, tnet_dns_qclass_t);
 		tnet_dns_qtype_t qtype = va_arg(*app, tnet_dns_qtype_t);
 		tsk_bool_t isquery = va_arg(*app, tsk_bool_t);
 
 		/* Create random ID. */
 		message->Header.ID = ++__dnsmessage_unique_id;
 
 		/* QR field ==> query (0) - response (1) */
 		message->Header.QR = isquery ? 0 : 1;
 
 		if (isquery){
 			/* QDCOUNT field ==> at least one question */
 			message->Header.QDCOUNT = 1;
 		}
 
 		if (qname){
 			message->Question.QNAME = tsk_strdup(qname);
 			message->Question.QTYPE = qtype;
 			message->Question.QCLASS = qclass;
 		}
 	}
 	return self;
 }
 
 static tsk_object_t* tnet_dns_message_dtor(tsk_object_t * self)
 {
 	tnet_dns_message_t *message = self;
 	if (message){
 		TSK_FREE(message->Question.QNAME);
 
 		TSK_OBJECT_SAFE_FREE(message->Answers);
 		TSK_OBJECT_SAFE_FREE(message->Authorities);
 		TSK_OBJECT_SAFE_FREE(message->Additionals);
 	}
 	return self;
 }
 
 static const tsk_object_def_t tnet_dns_message_def_s =
 {
 	sizeof(tnet_dns_message_t),
 	tnet_dns_message_ctor,
 	tnet_dns_message_dtor,
 	tsk_null,
 };
 const tsk_object_def_t *tnet_dns_message_def_t = &tnet_dns_message_def_s;