/*
 * Copyright (C) 2001-2003  Daniel Kelly
 * Copyright (C) 2009, 2013, 2016, 2018  Peter Pentchev
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include "prips.h"

#if !defined(INET_ADDRSTRLEN)
#define INET_ADDRSTRLEN 16
#endif

/**********************************************/
/* Turn an IP address in dotted decimal into  */ 
/* a number.  This function also works  for   */
/* partial addresses.                         */
/**********************************************/
uint32_t numberize(const char * const addr)
{
	uint32_t sin_addr;
	const int retval = inet_pton(AF_INET, addr, &sin_addr);

	/* invalid address or error in inet_pton() */
	if(retval == 0 || retval == -1)
		return (uint32_t)-1; 

	return ntohl(sin_addr);	
}

/**********************************************/
/* Converts an IP address into dotted decimal */
/* format.  Note that this function cannot be */
/* used twice in one instruction (e.g. printf */
/* ("%s%s",denumberize(x),denumberize(y)));   */
/* because the return value is static.        */
/**********************************************/
const char *denumberize(const uint32_t addr)
{
	static char buffer[INET_ADDRSTRLEN];
	const uint32_t addr_nl = htonl(addr);
	
	if(!inet_ntop(AF_INET, &addr_nl, buffer, sizeof(buffer)))
		return NULL;
	return buffer;
}

/**********************************************/
/* Given start and end IP addresses, we try   */
/* to find the offset used to print the range */
/* in CIDR notation.                          */
/**********************************************/
const char *cidrize(const uint32_t start, const uint32_t end)
{
	int offset = 32;

	/* find the mask (offset) by finding the 
	 * highest bit set differently in the start
	 * and end addresses. 
	 */
	uint32_t diff = start ^ end;
	
	/* find the highest bit set in diff */
	for(unsigned i=1; i <= 32; i++) 
	{
		if (diff == 0) 
		{
			offset = i - 1;
			break;
		}
		diff = diff >> 1;
	}

	if (offset == 32)
		return "0.0.0.0/0";

	/* clear out the bits below the mask */
	const uint32_t base = (start >> offset) << offset;

	static char buffer[BUF_SIZE];
	snprintf(buffer, BUF_SIZE, "%s/%d",
		denumberize(base), 32 - offset);

	return(buffer);
}

/***********************************************/
/* Takes offset (number of bits from the left  */ 
/* of addr) and subtracts it from the number   */
/* of possible bits.  The number of possible   */
/* bits is the number of bits left for hosts.  */
/* We then return last host address.           */
/***********************************************/
uint32_t add_offset(const char *addr, int offset)
{
	uint32_t naddr, hostmask;

	if(offset > 32 || offset < 0)
	{
		errx(1, "CIDR offsets are between 0 and 32");
	}
	if(offset == 0)
		hostmask = (uint32_t)-1L;
	else
		hostmask = (1UL << (32 - offset)) - 1;

	naddr = numberize(addr);
	if((naddr & hostmask) != 0) 
	{
		errx(1, "CIDR base address didn't start at subnet boundary");
	}

	return naddr + hostmask;
}

uint32_t set_bits_from_right(int bits)
{
        register int i;
        uint32_t number = 0;

        for(i = 0; i < bits; i++)
                number += (int)(1 << i);

        return number;
}

int count_on_bits(uint32_t number)
{
	uint32_t mask = 1;
	int i, on_bits = 0;

	mask <<= 31;
	for(i = 0; i < 32; i++)
	{
		if(mask & number)
			on_bits++;
		number <<= 1;
	}

	return on_bits; 
}
 
char get_class(uint32_t address)
{
	uint32_t addr;
        char class;

        addr = (address & 0xff000000) >> 24;

        if (addr < 127)
                class = 'a';
        else if (addr >= 127 && addr < 192)
                class = 'b';
        else if (addr >= 192 && addr < 224)
                class = 'c';
        else
                class = 'd';

	return class;
}
