/* Code style: -*- linux-c -*- */
/* File : xfw/find/range.c */

/* This segment of code is capable, when supplied with the ip address
  of the current interface (normally eth0)
       a) finding the netmask and the braodcast of the interface
       b) finding the locically available range of machines in the current network
       c) building a structure of the current net segment layout
*/

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

/* Add in structures and prototype definitions */
#include "range.h"


char * curr_time(void)
{
       /* returns textual timestamp */

       struct tm now;
       time_t ctime;

       static char temp_buff[255]="";

       time(&ctime);
       now = *localtime(&ctime);
       strcpy (temp_buff, asctime(&now));

#ifdef DEBUG
       sleep(1);
       printf("Time is now %s", temp_buff);
#endif

       return (temp_buff);
}

int encode_ip(const char * number)
{
       unsigned int class_a;
       unsigned int class_b;
       unsigned int class_c;
       unsigned int class_d;
       unsigned int result=0;

       static char internal_buffer[20]=""; /* nessesary evil
                                              (avoids segv's across text segments) */
       strcpy(internal_buffer, number);


       class_a = atoi(strtok(internal_buffer, "."));
       class_b = atoi(strtok(NULL, "."));
       class_c = atoi(strtok(NULL, "."));
       class_d = atoi(strtok(NULL, "."));


#ifdef DEBUG
       printf("encoded broke up these numbers %d %d %d %d\n", class_a, class_b, class_c, class_d);
#endif

       /* encode the numbers AA BB CC DD = 1 byte per number */
       /* the << is used here as a very fast multiply */

       result  = (class_d        +
                 (class_c << 8)  +
                 (class_b << 16) +
                 (class_a << 24) );

       return (result);
}

unsigned char decode(int number, int place)
{
/* this routime is *heavily* used so I've tried to optimise it */
       unsigned int a, b, c, d;

       /* masks :
          FF FF FF 00
          FF FF 00 FF
          FF 00 FF FF
          00 FF FF FF
          */

/*        a = b = c = d = 0; */ /* not really needed */

       d = (number >> 0)  | 0xFFFFFF00;
       c = (number >> 8)  | 0xFFFFFF00;
       b = (number >> 16) | 0xFFFFFF00;
       a = (number >> 24) | 0xFFFFFF00;

       /* now mask out the high 0xFFFFFF-- bits */

       d -= 0xFFFFFF00;
       c -= 0xFFFFFF00;
       b -= 0xFFFFFF00;
       a -= 0xFFFFFF00;

       switch (place) {
       case 1  : return (a); break;
       case 2  : return (b); break;
       case 3  : return (c); break;
       case 4  : return (d); break;
       default : perror("In decode, invalid arg[2]"); exit(1);
       };
       return(0);              /* this is to shut up gcc -Wall
                                  as we never actually get here */
}

struct range * calc(unsigned int ip, unsigned int netmask, unsigned int broadcast)
{
       /* this is all bit pattern related */

       unsigned int loop, copy, max_range, start, finish;

       struct range *block;

       block = (struct range *) calloc(1,sizeof(block));

       /* work out how many 0's are in the netmask, some of my better logic 8) */

       copy = netmask;
       loop = 0 ;
       while (copy % 2 != 1) {
               copy = copy >> 1 ;
               loop++;
       }
       loop--;                 /* went around the loop once to often */

       max_range = 2 << loop;

       start = (ip / max_range) * max_range;
       finish = start + (max_range -1); /* blame the arabics! they invented the zero! */

#ifdef DEBUG
       printf("start of network range is  = (0x%x)\n", start);
       printf("finish of network range is = (0x%x)\n", finish);
#endif

       block -> start  = start;
       block -> finish = finish;

       return (block);
}


char * ip_to_str(int encoded_ip)
{
       /* damn,.. why isn't there a reverse for atoi? like itoa? */

       char buffer[12];        /* just make sure it's big enuogh */
       static char string[20];

       sprintf(buffer,"%d", decode(encoded_ip, CLASS_A));
       strcpy(string, buffer);
       strcat(string, ".");

       sprintf(buffer,"%d", decode(encoded_ip, CLASS_B));
       strcat(string, buffer);
       strcat(string, ".");

       sprintf(buffer,"%d", decode(encoded_ip, CLASS_C));
       strcat(string, buffer);
       strcat(string, ".");

       sprintf(buffer,"%d", decode(encoded_ip, CLASS_D));
       strcat(string, buffer);

 return (string);
}

void translate(char *string, int encoded_ip)
{
       /* damn,.. why isn't there a reverse for atoi? like itoa? */

       char buffer[12];        /* just make sure it's big enuogh */

       sprintf(buffer,"%d", decode(encoded_ip, CLASS_A));
       strcpy(string, buffer);
       strcat(string, ".");

       sprintf(buffer,"%d", decode(encoded_ip, CLASS_B));
       strcat(string, buffer);
       strcat(string, ".");

       sprintf(buffer,"%d", decode(encoded_ip, CLASS_C));
       strcat(string, buffer);
       strcat(string, ".");

       sprintf(buffer,"%d", decode(encoded_ip, CLASS_D));
       strcat(string, buffer);

}


struct range * describe_net(struct net_attrib *node)
{
       struct range *range;
#ifdef DEBUG
       char start[20]="", finish[20]=""; /* debugging at the end needs these */

       printf("ip, netmask, broadcast encoded as\n(0x%X) (0x%X) (0x%X)\n\n",
              node -> ip,
              node -> netmask,
              node -> broadcast);
#endif

       /* allocate some memory for range even though it should
          already be allocated when we attempt to access it */
       range = (struct range *) calloc(1, sizeof(range));

       range = calc(node -> ip,
                    node -> netmask,
                    node -> broadcast); /* find me the range of addresses for that network */

#ifdef DEBUG
       printf("The ip range spans (%d) ip addresses\n", (range->finish - range->start));

       printf("Running from (%s) - the network addreess (unusable)) to\n        to   (%s) - the network broadcast ip (unusable))\n",
              ip_to_str(range->start), ip_to_str(range->finish));
#endif

       return (range);
}

/* this #ifdef is is so I can link in these other code segments */

#ifndef NOMAIN
int main()
{
       struct range *range;
       struct net_attrib *net_desc;

       unsigned int loop = 0;
       char start[20], finish[20], test[20];

       /* these would be supplied by a routine, which has used encode_ip */
       /* to pass three int arguments */
       /* here, we'll hard code it into the net_attrib struct */

       net_desc = (struct net_attrib *) calloc(1, sizeof(net_desc));

       net_desc-> ip =                 encode_ip("164.11.100.67");
       net_desc-> netmask =            encode_ip("255.255.255.0");
       net_desc-> broadcast =          encode_ip("164.11.100.255");


       /* now we call desc_net(net_desc) which will return the range structure */
       /* which will also need decoding though -DDEBUG will force it to tell tales */

       range = describe_net(net_desc);


       printf("Start was (%s) and the finish is (%s)\n",
                ip_to_str(range->start), ip_to_str(range->finish));
       printf("-----------------------------------------------------------\n");

       /* note the starting +1, you can not ping a network boundry, bad things happen */
       /* this loop also shows very strongly the case for encoding the ip as an int */

       for(loop = (range->start) +1 ; loop < (range->finish); loop++) {
               translate(test, loop);
               printf("test ip (%s) with ping packet\n", test);
       }

       free(net_desc);

       return(0);
}

#endif