//-----------------------------------------------------------------------------
// Copyright © 2003 - Philip Howard - All rights reserved
//
// 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//-----------------------------------------------------------------------------
// package	libh/net
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This file is best viewed using a fixed spaced font such as Courier
// and in a display at least 120 columns wide.
//-----------------------------------------------------------------------------

#include "net_lib.h"

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	ipv4_range_to_class_int
//
// purpose	Convert a range of IPv4 addresses, in host byte order,
//		into a group of traditional classful network block addresses,
//		returned one at a time.  This variety of the function uses
//		addresses in int format.  See ipv4_range_to_class_in_addr()
//		for in_addr format.
//
// arguments	1 (ipv4_t *) pointer to range first address
//		2 (ipv4_t *) pointer to range last address
//		3 (ipv4_t *) where to store CIDR block address
//		4 (ipv4_t *) where to store CIDR block netmask
//		5 (ipv4_t *) where to store CIDR block size
//
// returns	(int) 8,16,24,32 : CIDR-style block prefix length
//		(int)          0 : the whole IPv4 address space
//		(int)         -1 : no (more) net blocks
//
// note		The locations pointed to are modified in place to reduce the
//		range by removing each network block returned.
//-----------------------------------------------------------------------------
int
ipv4_range_to_class_int (
    ipv4_t *		arg_range_addr
    ,
    ipv4_t *		arg_range_last
    ,
    ipv4_t *		arg_class_addr
    ,
    ipv4_t *		arg_class_mask
    ,
    ipv4_t *		arg_class_size
    )
__PROTO_END__
{
    ipv4_t		range_addr	;
    ipv4_t		range_last	;
    ipv4_t		range_size	;

    range_addr = * arg_range_addr;
    range_last = * arg_range_last;

    //-- Handle an invalid range.
    if ( range_addr > range_last ) return -1;

    //-- Handle special case of whole space.
    if ( range_addr == 0 && range_last == 0xffffffffU ) {
	* arg_range_addr = 0xffffffffU;
	* arg_range_last = 0;
	if ( arg_class_addr ) * arg_class_addr = 0U;
	if ( arg_class_mask ) * arg_class_mask = 0U;
	if ( arg_class_size ) * arg_class_size = 0U;
	return 0;
    }

    range_size = range_last - range_addr + 1U;

    //-- Check for class A.
    if ( ( range_addr & 0x00ffffffU ) == 0 && range_size >= 0x01000000U ) {
	if ( arg_class_addr ) * arg_class_addr = range_addr;
	if ( arg_class_mask ) * arg_class_mask = 0xff000000U;
	if ( arg_class_size ) * arg_class_size = 0x01000000U;
	if ( range_size == 0x01000000U ) {
	    * arg_range_addr = 0xffffffffU;
	    * arg_range_last = 0;
	} else {
	    * arg_range_addr = range_addr + 0x01000000U;
	}
	return 8;
    }

    //-- Check for class B.
    if ( ( range_addr & 0x0000ffffU ) == 0 && range_size >= 0x00010000U ) {
	if ( arg_class_addr ) * arg_class_addr = range_addr;
	if ( arg_class_mask ) * arg_class_mask = 0xffff0000U;
	if ( arg_class_size ) * arg_class_size = 0x00010000U;
	if ( range_size == 0x00010000U ) {
	    * arg_range_addr = 0xffffffffU;
	    * arg_range_last = 0;
	} else {
	    * arg_range_addr = range_addr + 0x00010000U;
	}
	return 16;
    }

    //-- Check for class C.
    if ( ( range_addr & 0x000000ffU ) == 0 && range_size >= 0x00000100U ) {
	if ( arg_class_addr ) * arg_class_addr = range_addr;
	if ( arg_class_mask ) * arg_class_mask = 0xffffff00U;
	if ( arg_class_size ) * arg_class_size = 0x00000100U;
	if ( range_size == 0x00000100U ) {
	    * arg_range_addr = 0xffffffffU;
	    * arg_range_last = 0;
	} else {
	    * arg_range_addr = range_addr + 0x00000100U;
	}
	return 24;
    }

    //-- Return a single host.
    if ( arg_class_addr ) * arg_class_addr = range_addr;
    if ( arg_class_mask ) * arg_class_mask = 0xffffffffU;
    if ( arg_class_size ) * arg_class_size = 0x00000001U;
    if ( range_size == 0x00000001U ) {
	* arg_range_addr = 0xffffffffU;
	* arg_range_last = 0;
    } else {
	* arg_range_addr = range_addr + 0x00000001U;
    }
    return 32;
}

