/**************************************/
/* INCLUDES */

#include "tcp_by_dns_tunnel.h"


/**************************************/
/* CONSTANTS */

const char DNS_Q_BEGIN_DATA[DNS_Q_BEGIN_SIZE] = {
    0x00, 0x00,			/* Query number */
    0x01, 0x00,			/* Flags */
    0x00, 0x01,			/* Questions */
    0x00, 0x00,			/* Awnser RR */
    0x00, 0x00,			/* Authority RR */
    0x00, 0x00,			/* Additional RR */
};

const char DNS_Q_END_DATA[DNS_Q_END_SIZE] = {
    0x00, 0x01,			/* Host address type */
    0x00, 0x01,			/* Inet class */
};


const char DNS_A_BEGIN_DATA[DNS_A_BEGIN_SIZE] = {
    0x00, 0x00,			/* Query number */
    0x85, 0x80,			/* Flags */
    0x00, 0x01,			/* Questions */
    0x00, 0x01,			/* Answer RR */
    0x00, 0x00,			/* Authority RR */
    0x00, 0x00			/* Additional RR */
};

const char DNS_A_END_DATA[DNS_A_END_SIZE] = {
    0x00, 0x01,			/* Host address type */
    0x00, 0x01,			/* Inet class */
    0xc0, 0x0c,			/* Name pointer (fixed) */
    0x00, 0x01,			/* Host address type */
    0x00, 0x01,			/* Inet class */
    0x00, 0x00,			/* TTL:            */
    0x09, 0x60,			/*      60 minutes */
    0x00, 0x04,			/* Data length (4) */
    0xd8, 0xef,			/* Google         */
    0x33, 0x64			/*        address */
};


/**************************************/
/* FUNCTIONS */

int
 dns_l_encoder::encode(round_buf * in_rb_ptr, round_buf * out_rb_ptr)
{
    // Check if we have a message to send.
    if (msg_to_flush) {
	return flush_msg(out_rb_ptr);
    }

    else {
	return build_message(in_rb_ptr, out_rb_ptr);
    }
}

int dns_l_encoder::build_message(round_buf * in_rb_ptr,
				 round_buf * out_rb_ptr)
{
    // Build the message.
    int avail_read;
    char trans_buf[DNS_MAX_READ];

    // Read some data.
    avail_read = in_rb_ptr->used_size();

    if (avail_read == 0)
	return 0;

    if (avail_read > DNS_MAX_READ) {
	avail_read = DNS_MAX_READ;
    }

    in_rb_ptr->read_buf(trans_buf, avail_read);

    /* FIX ME */
    int encoded_size = avail_read;

    // Let's forge the message.
    msg_size = DNS_Q_BEGIN_SIZE + encoded_size + 2 + DNS_Q_END_SIZE;
    msg = new char[msg_size];

    // Begining.
    memcpy(msg, DNS_Q_BEGIN_DATA, DNS_Q_BEGIN_SIZE);

    // Size.
    msg[DNS_Q_BEGIN_SIZE] = encoded_size;

    // Data.
    memcpy(msg + DNS_Q_BEGIN_SIZE + 1, trans_buf, encoded_size);

    // Zero.
    msg[DNS_Q_BEGIN_SIZE + encoded_size + 1] = 0;

    // End.
    memcpy(msg + DNS_Q_BEGIN_SIZE + encoded_size + 2,
	   DNS_Q_END_DATA, DNS_Q_END_SIZE);

    msg_to_flush = 1;
    msg_index = 0;

    return flush_msg(out_rb_ptr);
}

int dns_l_encoder::flush_msg(round_buf * out_rb_ptr)
{
    dns_flush_msg(msg, &msg_index, msg_size, out_rb_ptr);
    if (msg_index == msg_size) {
	msg_to_flush = 0;
	delete[]msg;
    }

    return 0;
}

bool dns_l_encoder::has_unflushed_data()
{
    return msg_to_flush;
}


/**************************************/
int dns_l_decoder::decode(round_buf * in_rb_ptr, round_buf * out_rb_ptr)
{
    // Check if we have a message to send.
    if (msg_to_flush) {
	return flush_msg(out_rb_ptr);
    }

    else {
	return build_message(in_rb_ptr, out_rb_ptr);
    }
}

int dns_l_decoder::build_message(round_buf * in_rb_ptr,
				 round_buf * out_rb_ptr)
{
    // What we want is to get a complete message. Since we don't know
    // the size of the complete message, we have to read (possibly too much)
    // data until we find the end of the message.
    int seek_size;
    int old_used_size;
    bool found_it = 0;
    int decoded_size;

    int avail_read = in_rb_ptr->used_size();

    if (avail_read == 0)
	return 0;

    // Resize buffer if necessary.
    if (buf_used_size + avail_read > buf_size) {
	buf_size = (buf_used_size + avail_read) * 2;
	buf = (char *) my_realloc(buf, buf_size);
    }
    // Read (lookahead) in buffer.
    old_used_size = buf_used_size;
    in_rb_ptr->peek_buf(buf + old_used_size, avail_read);
    buf_used_size += avail_read;

    // Check if we got all the message.
    seek_size = avail_read;

    // Get the data length.
    if (buf_used_size > DNS_A_BEGIN_SIZE) {
	decoded_size = buf[DNS_A_BEGIN_SIZE];

	if (buf_used_size >= DNS_A_BEGIN_SIZE + decoded_size + 2 +
	    DNS_A_END_SIZE) {
	    seek_size = DNS_A_BEGIN_SIZE + decoded_size + 2 +
		DNS_A_END_SIZE - old_used_size;
	    found_it = 1;
	}
    }
    // Seek in the round buffer.
    in_rb_ptr->seek(seek_size);

    if (!found_it)
	return 0;

    // Move the data to the beginning of the buffer.
    memmove(buf, buf + DNS_A_BEGIN_SIZE + 1, decoded_size);

    // We got the message.
    buf_used_size = decoded_size;
    msg_index = 0;
    msg_to_flush = 1;

    // Flush the message.
    return flush_msg(out_rb_ptr);
}

int dns_l_decoder::flush_msg(round_buf * out_rb_ptr)
{
    dns_flush_msg(buf, &msg_index, buf_used_size, out_rb_ptr);
    if (msg_index == buf_used_size) {
	buf_used_size = 0;

	// Reset msg_to_flush to 0: First case.
	msg_to_flush = 0;

	msg_index = 0;
    }

    return 0;
}

bool dns_l_decoder::has_unflushed_data()
{
    return msg_to_flush;
}


/**************************************/
int dns_r_encoder::encode(round_buf * in_rb_ptr, round_buf * out_rb_ptr)
{
    // Check if we have a message to send.
    if (msg_to_flush) {
	return flush_msg(out_rb_ptr);
    }

    else {
	return build_message(in_rb_ptr, out_rb_ptr);
    }
}

int dns_r_encoder::build_message(round_buf * in_rb_ptr,
				 round_buf * out_rb_ptr)
{
    // Build the message.
    int avail_read;
    char trans_buf[DNS_MAX_READ];

    // Read some data.
    avail_read = in_rb_ptr->used_size();

    if (avail_read == 0)
	return 0;

    if (avail_read > DNS_MAX_READ) {
	avail_read = DNS_MAX_READ;
    }

    in_rb_ptr->read_buf(trans_buf, avail_read);

    // Build the paquet.

    // Suppose we encode the data here... FIX ME.
    int encoded_size = avail_read;

    msg_size = DNS_A_BEGIN_SIZE + encoded_size + 2 + DNS_A_END_SIZE;
    msg = new char[msg_size];

    // Begining.
    memcpy(msg, DNS_A_BEGIN_DATA, DNS_A_BEGIN_SIZE);

    // Size.
    msg[DNS_A_BEGIN_SIZE] = encoded_size;

    // Data.
    memcpy(msg + DNS_A_BEGIN_SIZE + 1, trans_buf, encoded_size);

    // Zero.
    msg[DNS_A_BEGIN_SIZE + encoded_size + 1] = 0;

    // End.
    memcpy(msg + DNS_A_BEGIN_SIZE + encoded_size + 2,
	   DNS_A_END_DATA, DNS_A_END_SIZE);

    msg_to_flush = 1;
    msg_index = 0;

    return flush_msg(out_rb_ptr);
}

int dns_r_encoder::flush_msg(round_buf * out_rb_ptr)
{
    dns_flush_msg(msg, &msg_index, msg_size, out_rb_ptr);
    if (msg_index == msg_size) {
	msg_to_flush = 0;
	delete[]msg;
    }

    return 0;
}

bool dns_r_encoder::has_unflushed_data()
{
    return msg_to_flush;
}


/**************************************/
int dns_r_decoder::decode(round_buf * in_rb_ptr, round_buf * out_rb_ptr)
{
    // Check if we have a message to send.
    if (msg_to_flush) {
	return flush_msg(out_rb_ptr);
    }

    else {
	return build_message(in_rb_ptr, out_rb_ptr);
    }
}

int dns_r_decoder::build_message(round_buf * in_rb_ptr,
				 round_buf * out_rb_ptr)
{
    // What we want is to get a complete message. Since we don't know
    // the size of the complete message, we have to read (possibly too much)
    // data until we find the end of the message.
    int seek_size;
    int old_used_size;
    bool found_it = 0;
    int decoded_size;

    int avail_read = in_rb_ptr->used_size();

    if (avail_read == 0)
	return 0;

    // Resize buffer if necessary.
    if (buf_used_size + avail_read > buf_size) {
	buf_size = (buf_used_size + avail_read) * 2;
	buf = (char *) my_realloc(buf, buf_size);
    }
    // Read (lookahead) in buffer.
    old_used_size = buf_used_size;
    in_rb_ptr->peek_buf(buf + old_used_size, avail_read);
    buf_used_size += avail_read;

    // Check if we got all the message.
    seek_size = avail_read;

    // Get the data length.
    if (buf_used_size > DNS_Q_BEGIN_SIZE) {
	decoded_size = buf[DNS_Q_BEGIN_SIZE];

	if (buf_used_size >= DNS_Q_BEGIN_SIZE + decoded_size + 2 +
	    DNS_Q_END_SIZE) {
	    seek_size = DNS_Q_BEGIN_SIZE + decoded_size + 2 +
		DNS_Q_END_SIZE - old_used_size;
	    found_it = 1;
	}
    }
    // Seek in the round buffer.
    in_rb_ptr->seek(seek_size);

    if (!found_it)
	return 0;

    // Move the data to the beginning of the buffer.
    memmove(buf, buf + DNS_Q_BEGIN_SIZE + 1, decoded_size);

    // We got the message.
    buf_used_size = decoded_size;
    msg_index = 0;
    msg_to_flush = 1;

    // Flush the message.
    return flush_msg(out_rb_ptr);
}

int dns_r_decoder::flush_msg(round_buf * out_rb_ptr)
{
    dns_flush_msg(buf, &msg_index, buf_used_size, out_rb_ptr);
    if (msg_index == buf_used_size) {
	buf_used_size = 0;
	msg_to_flush = 0;
    }

    return 0;
}

bool dns_r_decoder::has_unflushed_data()
{
    return msg_to_flush;
}


/**************************************/
void
dns_flush_msg(char *buf, int *buf_index, int buf_size,
	      round_buf * out_rb_ptr)
{
    int byte_to_copy = buf_size - *buf_index;
    int byte_avail = out_rb_ptr->avail_size();

    if (byte_to_copy > byte_avail)
	byte_to_copy = byte_avail;

    out_rb_ptr->write_buf(buf, byte_to_copy);
    *buf_index += byte_to_copy;
}
