#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define NALLOC 8

typedef struct {
	int *lo;          /*  List of lower range limits                              */
	int *hi;          /*  List of upper range limits                              */
	int *code;        /*  Int label for each range                                */
	int n;            /*  Number of ranges                                        */
	int nalloc;       /*  Maximum number of ranges for current memory allocation  */
	int nallocint;    /*  Number of ranges to allocate when increasing storage    */
} ip_range_t;


/*  Test routine to dump ip range  */
void dump_range (ip_range_t *ipr) {
	int i;
	if (ipr==NULL) return; 
	for (i=0;i<ipr->n;i++) {
		printf ("lo (0x%x)  hi (0x%x)  code (%d)\n", ipr->lo[i], ipr->hi[i], ipr->code[i]);
	}
}

/*  Convert strings like "138.99.201.5" or "137.99.26" to int ipaddress  */
void str2ip (char *ipstr, int *ipout, int *mask) {
	int ip[4];
	int n = sscanf (ipstr, "%d.%d.%d.%d", ip, ip+1, ip+2, ip+3);
	int i;
	*ipout = 0;
	for (i=0;i<4;i++) {
		*ipout = *ipout<<8;
		if (i<n) *ipout |= (ip[i] & 0xff);
	}
	*mask = 0xffffffff >> (8*n);

	/* for reasons unknown 0xffffffff >> 32 -> -1, so set to 0  */
	if (*mask==-1)  *mask=0;
}


/*  Allocate memory for iprange structure  */
ip_range_t *alloc_iprange (int nallocint) {
	ip_range_t *ip = (ip_range_t *) malloc (sizeof(ip_range_t));
	ip->lo   = (int *)  calloc(nallocint, sizeof(ip->lo  [0]));
	ip->hi   = (int *)  calloc(nallocint, sizeof(ip->hi  [0]));
	ip->code = (int *)  calloc(nallocint, sizeof(ip->code[0]));
	ip->n    = 0;
	ip->nalloc    = nallocint;
	ip->nallocint = nallocint;
	return ip;
}


/*  Add a single range to iprange structure  */
void add_iprange (ip_range_t **iprange, int code, int lo, int hi) {
	ip_range_t *ip = *iprange;
	if (ip==NULL) ip = alloc_iprange(NALLOC);
	if (ip->n==ip->nalloc) {
		ip->nalloc += ip->nallocint;
		ip->lo = (int *) realloc (ip->lo, ip->nalloc*sizeof(ip->lo[0]));
		ip->hi = (int *) realloc (ip->hi, ip->nalloc*sizeof(ip->hi[0]));
		ip->code = (int *) realloc (ip->code, ip->nalloc*sizeof(ip->code[0]));
	}
	ip->lo[ip->n] = lo;
	ip->hi[ip->n] = hi;
	ip->code[ip->n] = code;
	ip->n++;
	*iprange = ip;
}


/*  Store ip range in *arg_in into structure *ip associated with <code>
 *  Manages storage for ip (initial allocation and re-allocation)  */
void process_iprange (ip_range_t **iprange, int code, char *arg_in) {
	char *arg_cpy = strdup(arg_in);
	char *ipstr   = strdup(arg_in);
	char *netstr  = strdup(arg_in);
	char *range1, *range2;
	int  ip1, ip2, mask, net;

	range2 = arg_cpy;

	/*  break strings into separate ranges  */
	while (NULL!=range2) {
		/*  Break are into (1st range):(remaining ranges)  */
		range1 = range2;
		range2 = strpbrk(range1, ": ");
		if (NULL!=range2) *range2++ = '\0';

		/*  Look for range expressed as (lo ip)-(hi ip)  */
	 	if (2==sscanf (range1, "%[0-9.]-%[0-9.]", ipstr, netstr)) {
			str2ip(ipstr,  &ip1, &mask);
			str2ip(netstr, &ip2, &mask);

		/*  break range into (ip)/(net)  */
		} else if (2==sscanf (range1, "%[0-9.]/%[0-9]", ipstr, netstr)) {

			/*  read ip address */
			str2ip (ipstr, &ip1, &mask);

			net = atoi(netstr);
			if (net<0) net=0;
			else if (net>32) net=32;
			mask = 0xfffffff >> net;
			if (mask==-1)  mask = 0;
			ip2 = ip1 | mask;

		/*  Look for single ip address  */
		} else if (sscanf (range1, "%[0-9.].%[0-9].", ipstr, netstr)) {
			str2ip (ipstr, &ip1, &mask);
			ip2 = ip1 | mask;

		/*  Bad input format  */
		} else {
			fprintf (stderr, 
					"ERROR:  Ignoring invalid network range argument (%s)\n",
					arg_in);
			return;
		}

		/* Store results  */
		add_iprange (iprange, code, ip1, ip2);
	}

	free (netstr);
	free (ipstr);
	free (arg_cpy);
}


/*  Return code value for iprange containing ip  */
int get_range_code (int ip, ip_range_t *iprange) {
	int i;
	for (i=0; i<iprange->n; i++) {
		if (iprange->lo[i]<=ip && ip<=iprange->hi[i]) 
			return iprange->code[i];
	}
	return 0;
}

#define NBUFF 256

int main (int argc, char *argv[]) {
	ip_range_t *iprange = NULL;
#if 0
	char *ipstr = "137.99.20.5";
#endif
	char ipstr[NBUFF];
	int ip, mask, code, nread;
	char buffer[NBUFF];
	FILE *fin = NULL;

	if (argc<3) {
		printf ("Usage: iprange <range_file> <ip_address>\n");
		return 0;
	}

	fin = fopen (argv[1], "rt");
	if (fin==NULL) {
		printf ("Cannot open file <%s>\n", argv[1]);
		return 1;
	}

	while (fgets(buffer,NBUFF,fin)) {
		nread = sscanf (buffer, "%s %i", ipstr, &code);
		/*test*/
		printf ("ipstr <%s> code <%i>\n", ipstr, code);
		/* end test*/
		if (nread<2) continue;
		process_iprange (&iprange, code, ipstr);
	}

	dump_range (iprange);

	str2ip (argv[2], &ip, &mask);
	code = get_range_code (ip, iprange);
	printf ("code for ip (%s)(0x%x) (%d)\n", argv[2], ip, code);
	return 0;

#if 0
	code = get_range_code(ip, iprange);
	process_iprange (&iprange, 2, "137.99.20.0/24");
	str2ip (ipstr, &ip, &mask);
	code = get_range_code (ip, iprange);
	printf ("code for (%s) (%d)\n", ipstr, code);
	return 0;
#endif
}
