#include <stdio.h>
#ifdef sun
#include <strings.h>
#else
#include <string.h>
#endif
#include <stdlib.h>
#include <arpa/inet.h>
#include <glib.h>

#include "as.h"
#include "read_line.h"
#include "common.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>


/*
-------------------------------------------------------------------------------
Purpose           : Read and parse next line from traceroute output file
Input	          : buffer holding the input line, 
		    Hash table of previous IP-AS num matches
Output		  : src         pointer to Starting point (binary IP num)
              	  : dest        pointer to End point (binary format IP)
             	  : time        pointer to Time when the vector was determined
              	  : npoint      pointer to Number of points along the way.
              	  : ipvec       routing vector (char string with integer IPs)
		  : aspath	The AS numbers corresponding to IPvec
		  : asnums    	(upated) hash table

Comments          : the get_origin() routine can return more than
                    one AS matching a single prefix, either due to old
                    records hanging in around in routing registry or
                    some form of multihomedness. Thus the asnums
                    hash indexed by ipnums is filled with and returns
                    character string of one or more ASnum sperated by /

		  : Both for the aspath and the ip vector the calling porgram
		    ia assumed to have taken care of memory allocation.

NOTE		  : The input buffer will be modified in the process of
		    decoding; calling routines MUST make a copy if they
		    need the contents further on.

Returns		  :   0  if the line was successfully read,
	              1  if a line could be read but didn't contain valid data.
       		      2  if nothing could be read.
		     -1  if the line was read succesfully but last IP != dst

static char const rcsid[] = "$Id: read_line.c,v 1.8 2004/03/10 18:04:33 wilhelm Exp $";

*/

int read_line (char *buf, unsigned int *src, unsigned int *dest, int* time,
		int *npoints, char *ipvec, char *aspath, GHashTable* asnums) {

   
	char **ap, *arg[MAXWORDS];
	int argc;
   
	unsigned int ipnum;

	char *point,*current;
	int i;

	point = buf;

	if (point == NULL) return 2;

	if (buf[strlen(buf)-1] == '\n') {
		/* chop off trailing newline char */
		buf[strlen(buf)-1] = '\0';
	}

	argc = 0;

#if defined(USE_STRTOK)		   /* POSIX compliant */
   	/* Split line into elements */
	ap = arg;
	*ap = strtok (point, " \t");
	while (*ap != NULL) {
		if (**ap != '\0') {
			++ap;
			++argc;
		}
		*ap = strtok (NULL, " \t");
	}
   
#else				   /*  BSD specific */
	/* Split line into elements */
	for (ap = arg; (*ap = strsep (&point," \t")) != NULL ;) {
		if (**ap != '\0') {
			++ap;
			++argc;
		}
	}
#endif

	/* Check if it starts with "RVEC" */
	if ((arg[0] == NULL) || (strcmp(arg[0], "RVEC") != 0)) {
#ifdef DEBUG
		printf ("Line did not start with RVEC\n");
#endif
		return 1;
	}

	if (argc < 7) {
		/* Not at least 5 data elements (src, dst, time, #points endpoint) */
#ifdef DEBUG
		printf ("Not at least 5 data items\n");
#endif
		return 1;
	}

	if (atoi(arg[1]) != atoi(arg[5]) + 4) {
		/* Not at least 4 data elements (src, dest, time, #points) */
#ifdef DEBUG
		printf ("Number of elements mismatch\n");
#endif
		return 1;
	}

	if (argc != atoi(arg[1]) + 2) {
		/* incomplete line */
#ifdef DEBUG
		printf ("Number of elements mismatch, incomplete line\n");
#endif
		return 1;
	}


	/* Convert the elementes on the line to something interesting */
#ifdef DEBUG
	printf ("Converting strings...\n");
#endif
	*src      = rv_iptoint(arg[2]);
	*dest     = rv_iptoint(arg[3]);
	if (*src == 0 || *dest == 0) {
		/* error decoding source or destination, give up on this line */
#ifdef DEBUG
		printf ("invalid source/destination IP address encountered; %s",
			"giving up on this line\n");
#endif
		return 1;
	}

	*time     = atoi (arg[4]);
	*npoints  = atoi (arg[5]);


	/* If there are more than 30 hops, traceroute mysteriously failed,
	   ignore the entry */

	if (*npoints > 30) {
#ifdef DEBUG
		printf ("More than 30 hops, entry ignored!\n");
#endif
		return 1;
	}


	/* line seems ok, add IPnums to route vector, create aspath */

	ipnum=0;
	ipvec[0] = '\0';	  /* start with empty vector */
	aspath[0] = '\0';   	  /* ...........  and path */

	for (i=0; i < (*npoints); i++) {

		char ips[MAX_IP_SIZE+1];

		/* lookup AS num */
		current = match_ip_as (asnums, arg[6+i], 0);

		/*
		   add integer ip num to vector
	           note that rv_iptoint will *change* the input string
		   so this comes _after_ asnum lookup
		*/
		
		ipnum = rv_iptoint(arg[6+i]);
		strcat(aspath, current);

		if (i != (*npoints)-1) {
			strcat(aspath, " ");
			sprintf(ips, "%u ", ipnum);
		}
		else {
			sprintf(ips, "%u", ipnum);
		}
		strcat(ipvec, ips);

	}

	/* Check if the traceroute reached the target destination */


	if (ipnum == *dest) {
		return(0);
	}
	else {
		return(-1);
	}
}



/*
-------------------------------------------------------------------------------
Purpose           : IPv6 counterpart to read_line()
Input             : buffer holding the input line,
Output            : src         pointer to IPv6 address string (source Testbox) 
                  : dest        pointer to IPv6 address string (destination TB) 
                  : time        pointer to Time when the vector was determined
                  : npoint      pointer to Number of points along the way.
                  : ipvec       routing vector (char string with ASCII IPs)

Comments          : For the ip vector the calling porgram
                    is assumed to have taken care of memory allocation.

NOTE              : The input buffer will be modified in the process of
                    decoding; calling routines MUST make a copy if they
                    need the contents further on.

Returns           :   0  if the line was successfully read,
                      1  if a line could be read but didn't contain valid data.
                      2  if nothing could be read.
                     -1  if the line was read succesfully but last IP != dst

*/
int read_line_ip6 (char *buf, char **src, char **dest, int* time,
		int *npoints, char *ipvec, char *aspath, GHashTable* asnums) {

   
        char **ap, *arg[MAXWORDS];
        int argc;
   
	char *point,*current;
        
        int i;

	char ip6addr[INET6_ADDRSTRLEN];

        point = buf;

        if (point == NULL) return 2;

        if (buf[strlen(buf)-1] == '\n') {
                /* chop off trailing newline char */
                buf[strlen(buf)-1] = '\0';
        }

        argc = 0;

#if defined(USE_STRTOK)            /* POSIX compliant */
        /* Split line into elements */
        ap = arg;
        *ap = strtok (point, " \t");
        while (*ap != NULL) {
                if (**ap != '\0') {
                        ++ap;
                        ++argc;
                }
                *ap = strtok (NULL, " \t");
        }
   
#else                              /*  BSD specific */
        /* Split line into elements */
        for (ap = arg; (*ap = strsep (&point," \t")) != NULL ;) {
                if (**ap != '\0') {
                        ++ap;
                        ++argc;
                }
        }
#endif

        /* Check if it starts with "RVEC6" */
        if ((arg[0] == NULL) || (strcmp(arg[0], "RVEC6") != 0)) {
#ifdef DEBUG
                printf ("Line did not start with RVEC6\n");
#endif
                return 1;
        }

        if (argc < 7) {
                /* Not at least 5 data elements (src, dst, time, #points endpoint) */
#ifdef DEBUG
                printf ("Not at least 5 data items\n");
#endif
                return 1;
        }

        if (atoi(arg[1]) != atoi(arg[5]) + 4) {
                /* Not at least 4 data elements (src, dest, time, #points) */
#ifdef DEBUG
                printf ("Number of elements mismatch\n");
#endif
                return 1;
        }

        if (argc != atoi(arg[1]) + 2) {
                /* incomplete line */
#ifdef DEBUG
                printf ("Number of elements mismatch, incomplete line\n");
#endif
                return 1;
        }

        /* Convert the elementes on the line to something interesting */
#ifdef DEBUG
        printf ("Storing strings...\n");
#endif
        *src      = arg[2];
        *dest     = arg[3];
        if ( ( (strcmp(arg[2],"0::0") != 0) && (inet_pton(AF_INET6,arg[2],ip6addr) < 1) ) ||
	     ( (strcmp(arg[3],"0::0") != 0) && (inet_pton(AF_INET6,arg[3],ip6addr) < 1) ) ) {
                /* error decoding source or destination, give up on this line */
#ifdef DEBUG
                printf ("invalid source/destination IPv6 address encountered; %s",
                        "giving up on this line\n");
#endif
                return 1;
        }

        *time     = atoi (arg[4]);
        *npoints  = atoi (arg[5]);


        /* If there are more than 30 hops, traceroute mysteriously failed,
           ignore the entry */

        if (*npoints > 30) {
#ifdef DEBUG
                printf ("More than 30 hops, entry ignored!\n");
#endif
                return 1;
        }


        /* line seems ok, add IPnums to route vector, create aspath */

        ipvec[0] = '\0';          /* start with empty vector */
	aspath[0] = '\0';   	  /* ...........  and path */

        for (i=0; i < (*npoints); i++) {

		if(inet_pton(AF_INET6,arg[6+i],ip6addr)) {

	                char ips[MAX_IP6_SIZE+2];

			current = match_ip_as (asnums, arg[6+i], 1);
			strcat(aspath, current);

	                if (i != (*npoints)-1) {
				strcat(aspath, " ");
	                        sprintf(ips, "%s ", arg[6+i]);
	                }
	                else {
	                        sprintf(ips, "%s", arg[6+i]);
	                }
	                strcat(ipvec, ips);
		} else {
#ifdef DEBUG
                	printf ("invalid IPv6 hop address encountered; %s",
                        	"giving up on this line\n");
#endif
                	return 1;
		}
        }

        /* Check if the traceroute reached the target destination */

	i--;

        if (strcmp(arg[6+i], *dest) == 0) {
                return(0);
        }
        else {
                return(-1);
        }
}




/*
-------------------------------------------------------------------------------
Subroutine Header
Purpose           : find matching ASnum(s) for specific IPs
Input             : hash table with results from previous lookups
                    IP address (ascii format)
Returns           : matching ASnums (character string) or NULL if no match
Comments          : When more than one AS is found to originate the IP,
                    all matching ASnums are returned , seperated by a /
Note              : GHashTable only stores pointers, the application must
                    take care of memory allocation, so we malloc() here
-------------------------------------------------------------------------------
*/


char *match_ip_as (GHashTable* asnums, char *ipstring, int ip6flag) {

	char *AS;

	char *value;
	int  *key;
	int length;
	char *ipcopy;

	ipcopy = malloc(strlen(ipstring)+1); 

	/* must copy, rv_iptoint will truncate and the v6 hash
	   needs a stable key */

	strcpy(ipcopy, ipstring);

	if (!ip6flag) {
		/* IPv4 hash key is the 32 bit IP address */


		key  = (int *) malloc(sizeof(int));
		*key = rv_iptoint (ipcopy);
        	AS = g_hash_table_lookup(asnums, key);
		free(ipcopy); /* v4 no longer needs it */
	}
	else {
		/* IPv4 hash key is the ASCII string representing IPv6 addr */
        	AS = g_hash_table_lookup(asnums, ipcopy);
	}

        if (AS == NULL) {

		/* search for it in the DB and insert in the hash table */

		AS = get_origin(ipstring, ip6flag);

		if (AS == NULL) {
			AS = "0";	/* not a real AS -> no data */
		}

		length = strlen(AS) + 1;
		value = (char *) malloc(length);
		strncpy(value, AS, length);

		/* insert in the hash table */

		if (!ip6flag) {
			g_hash_table_insert (asnums, key, value);
		}
		else {
			g_hash_table_insert (asnums, ipcopy, value);
		}
	}

	return(AS);

}

/*
-------------------------------------------------------------------------------
*/

char* rv_inttoip (unsigned int ipno, char* point) {
/*    ============

	Converts an unsigned int ("0x01020304") into a standard
	IP number (1.2.3.4); Space for the string must have been
	allocated in the calling routine.
*/

	int byte[4], i;

	for (i=0 ; i<4 ; i++) {
		byte[i] = ipno & 0xFF;
		ipno = ipno / 0x100;
	}

	sprintf (point, "%d.%d.%d.%d", byte[3], byte[2], byte[1], byte[0]);

	return point;
}

unsigned int rv_iptoint (char * ipstring) {
/*       ==============

	Convert IP number (e.g. 1.2.3.4) into an 32 unsigned int ("0x01020304")

	Input : char * ipstring    String containing the ip number 
	Output: unsigned int       IP number as a 32 bit unsigned int.

	Non-numbers in the input string will be converted to FF.
  
*/

	unsigned int temp;
	char buffer[10];
	char *point= &buffer[0];
	int i;

	point = strtok ( ipstring, "." );
	if (point == NULL) { return 0; }

	if (strcmp(point, "??") == 0) {
		temp = 0xFF;
	}
	else {
		temp = (int) strtol (point, (char**)NULL, 10);
		if (temp == 0) { temp = 0xFF; }
	}

	for (i=0 ; i<3 ; i++) {
		point = strtok ( NULL, "." );
		if ( (point == NULL) ) {
			return 0;
		}
		else if ((strncmp(point, "??", 2) == 0) ) {
			/* use strncmp, since last token of a line
			   includes \n char */
			temp = temp*0x100 + 0xFF;
		}
		else {
			temp = temp*0x100 + (int) strtol(point, NULL, 10);
		}
	}
   
	return temp;
}
