/*
 * Copyright (c) 2004-2005 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: utilities.c 6856 2007-05-20 23:55:04Z andras $
 */

/* File header. */
#include "utilities.h"

/* Endace headers. */
#include "dagutil.h"


/* CVS Header. */
static const char* const kUtilitiesCvsHeader __attribute__ ((unused)) = "$Id: utilities.c 6856 2007-05-20 23:55:04Z andras $";


unsigned int
count_one_bits(uint32_t value)
{
	unsigned int count = 0;
	unsigned int index;

	for (index = 0; index < 32; index++)
	{
		if (value & (1 << index))
		{
			count++;
		}
	}

	return count;
}


uint32_t
cidr_suffix_to_mask(unsigned int suffix)
{
	uint32_t result = 0xffffffff;

	if (suffix)
	{
		int index;
		int zero_bits = 32 - suffix;

		result = 0;
		for (index = 0; index < 32; index++)
		{
			if (index >= zero_bits)
			{
				result |= (1 << index);
			}
		}
	}

	/* IP addresses are stored in network byte order, so in order to bitwise AND them we
	 * store masks in network byte order too.
	 */
	return htonl(result);
}


list_node_t*
new_list_node(ip_address_t* value, list_node_t* next)
{
	list_node_t* result = (list_node_t*) malloc(sizeof(list_node_t));

	result->address = value;
	result->next = next;

	return result;
}


void
dispose_list_node(list_node_t* node)
{
	free(node);
}


void 
dispose_filter_rule(filter_rule_t* rule) {
    list_node_t* current;
    list_node_t* next;
    if (rule->source) {
        current = rule->source; 
        while(current) {
            next = current->next;
            free(current->address);
            free(current);
            current = next;
        }
    }
    if (rule->dest) {
        current = rule->dest; 
        while(current) {
            next = current->next;
            free(current->address);
            free(current);
            current = next;
        }
    }
    free(rule);
}

list_node_t*
clone_address_list(list_node_t* list)
{
	list_node_t* result = NULL;
	list_node_t* current = list;

	while (current)
	{
		list_node_t* node = (list_node_t*) malloc(sizeof(list_node_t));
		
		node->address = current->address;
		node->next = result;
		result = node;

		current = current->next;
	}

	return result;
}


unsigned int
get_list_length(list_node_t* list)
{
	unsigned int result = 0;
	list_node_t* current = list;

	while (current)
	{
		result++;
		current = current->next;
	}

	return result;

}


ipf_port_t*
new_port(void)
{
	ipf_port_t* result = (ipf_port_t*) malloc(sizeof(ipf_port_t));

	if (result)
	{
		result->any = 0;
		result->inverse = 0;
		result->first = htons(0);
		result->last = htons(65535);
	}

	return result;
}


void
dispose_port(ipf_port_t* port)
{
	free(port);
}


const char*
get_action_string(uint8_t action)
{
	if (ACCEPT == action)
	{
		return "accept";
	}
	else if (REJECT == action)
	{
		return "reject";
	}

	assert(0);
	return NULL;
}


const char*
get_protocol_string(protocol_t protocol)
{
	if (0 == protocol.mask)
	{
		return "all";
	}

	assert(0xff == protocol.mask);
	switch (protocol.protocol)
	{
	case IPPROTO_IP: return "ip";
	case IPPROTO_ICMP: return "icmp";
	case IPPROTO_TCP: return "tcp";
	case IPPROTO_UDP: return "udp";
	case IPPROTO_IGRP: return "igrp";
	case IPPROTO_RAW: return "non-ip";
	}

	assert(0);
	return NULL;
}


void
display_rule(FILE* outfile, filter_rule_t* rule)
{
	uint16_t colour = rule->exact_colour;
	uint16_t rule_index = colour / 2;

	fprintf(outfile, "%4u: %s %s colour = %u (exact); %u (potential)\n", rule_index, get_action_string(rule->action), get_protocol_string(rule->protocol), colour, (colour - 1));

	if (rule->src_inverse)
	{
		fprintf(outfile, "%4u:   source = not {", rule_index);
	}
	else
	{
		fprintf(outfile, "%4u:   source = {", rule_index);
	}
	display_ip_address_list(outfile, rule->source);
	fprintf(outfile, "} ports {");
	display_port(outfile, &rule->src_port);
	fprintf(outfile, "}\n");

	if (rule->dest_inverse)
	{
		fprintf(outfile, "%4u:   dest = not {", rule_index);
	}
	else
	{
		fprintf(outfile, "%4u:   dest = {", rule_index);
	}
	display_ip_address_list(outfile, rule->dest);
	fprintf(outfile, "} ports {");
	display_port(outfile, &rule->dest_port);
	fprintf(outfile, "}\n");
}


void
display_ip_address_list(FILE* outfile, list_node_t* head)
{
	list_node_t* node = head;

	while (node)
	{
		display_ip_address(outfile, node->address);
		if (node->next)
		{
			fprintf(outfile, "  ");
		}

		node = node->next;
	}
}


void
display_ip_address(FILE* outfile, ip_address_t* address)
{
#if defined (__linux__) || defined(__FreeBSD__) || (defined(__APPLE__) && defined(__ppc__)) || (defined(__SVR4) && defined(__sun))
	struct in_addr dummy = {.s_addr = address->ip4_addr};
	struct in_addr dummy_mask = {.s_addr = address->mask};
#elif defined (_WIN32)
	struct in_addr dummy = {dummy.s_addr = address->ip4_addr};
	struct in_addr dummy_mask = {dummy_mask.s_addr = address->mask};
#endif

	unsigned int suffix = 0;
	unsigned int index;

	for (index = 0; index < 32; index++)
	{
		if (address->mask & (1 << index))
		{
			suffix++;
		}
	}
		
	if (suffix == 32)
	{
		fprintf(outfile, "%s", inet_ntoa(dummy));
	}
	else
	{
		char addr_buf[32];
		char mask_buf[32];

        /*
         * inet_ntoa reuses a dynamically allocated buffer for its
         * return value that's why Valgrind and other memory checkers
         * complain, but they can be ignored.
         *
         * (unless a libc bug shows up again, like in this case:
         * http://lists.debian.org/debian-alpha/1997/12/msg00002.html)
         */
		strncpy(addr_buf, inet_ntoa(dummy), 32);
		strncpy(mask_buf, inet_ntoa(dummy_mask), 32);
		fprintf(outfile, "%s/%u (%s)", addr_buf, suffix, mask_buf);
	}
}


void
display_port(FILE* outfile, ipf_port_t* port)
{
	fprintf(outfile, "any = %u, inverse = %u, first = %u, last = %u", port->any, port->inverse, ntohs(port->first), ntohs(port->last));
}
