/***************************************
  $Revision: 1.10 $

  IP handling (ip). ip.c  - conversions between ascii and binary forms 
                            of IP addresses, prefixes and ranges.

			    various operations on binary forms.

  Status: NOT REVUED, TESTED

  Design and implementation by: Marek Bukowy

  ******************/ /******************
  Copyright (c) 1999                              RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  ***************************************/

#define IP_IMPL
#include <iproutines.h>
#include <string.h>
#include <stdio.h>
#include <erroutines.h>

#include <ctype.h>
#include <memwrap.h>

#include <numconv.h>
#include <stubs.h>

#include <sys/socket.h>
#include <netinet/in.h> 


/***************************************************************************/
/*+ return the max. length of bits per space

   Yes, it *could* be a macro - but as a function it can detect 
   more programmer's errors. And will get inlined anyway.

+*/

int IP_sizebits(ip_space_t spc_id) {
  switch (spc_id) {
  case IP_V4:
    return 32;
  case IP_V6:
    return 128;
  default:
    /*    die; */ /* error: bad IP version specified */
    return -1;
  }
}

/***************************************************************************/
/*+
   ascii IP address to binary. 
   In IP_EXPN mode IP will be treated as not-expanded.
   (missing octets will be set to 0, MSB will be set).
   In IP_PLAIN mode the routine will complain if it sees less octets.
+*/

er_ret_t 
IP_addr_t2b(ip_addr_t *ipptr, char *addr, ip_exp_t expf)
{
  if( index(addr, ':') == NULL ) {
    /* IPv4 */
    char *dot;
    unsigned len, byte, result=0;
    char cpy[4];
    int last = 0, dotsfound=0;
    int bytes=0;

    if( expf != IP_PLAIN && expf != IP_EXPN ) {
      return IP_INVARG;
    }

    do {
      if ( (dot = index (addr, '.')) == NULL) {
	dot = index (addr, '\0');
	last = 1;
      }
      else {
	if( ++dotsfound > 3 ) {
	  /* handle syntax ERROR - too many dots found */
	  return IP_INVIP4;
	}
      }
	
      if ((len = dot - addr) > 4) {
	/* syntax ERROR - too many digits between dots*/
	return IP_INVIP4;
      }
      strncpy( cpy, addr, len );
      cpy[len]=0;

      /* sscanf is waay too slow */
       
      if( ut_dec_2_uns(cpy, &byte) < 0 ) {
	/* handle syntax ERROR - invalid characters found */
	return IP_INVIP4;
      }
      
	
      if( byte > 255 ) {
	/* handle syntax ERROR - number between dots too high */
	return IP_INVIP4;
      }
      
      result <<= 8;
      result += byte;
      bytes++;
      
      addr = dot + 1;
    } while (!last);

    if( expf == IP_PLAIN ) {
      if( bytes!=4 ) {
	return IP_INVIP4;
      }
    } 
    else {
      while( bytes<4 ) {
	result <<= 8;
	bytes++;
      }
    }

    memset(ipptr, 0, sizeof(ip_addr_t));
    ipptr->space = IP_V4;
    ipptr->words[0] = result; 
  }
  else {
    /* IPv6 */
    /* not yet implemented. Sorry. */
    /*    die; */
    return  IP_NO6YET;
  }
  return IP_OK;	
}

/***************************************************************************/

/*+ converts a "IP/length" string into a binary prefix 
+*/
er_ret_t
IP_pref_t2b(ip_prefix_t *prefptr, char *prefstr, ip_exp_t expf)
{
  char ip[256];
  char *slash, *trash;
  int len;
  er_ret_t err;

  if( expf != IP_PLAIN && expf != IP_EXPN ) {
    return IP_INVARG;
  }
  
  if( (slash=index(prefstr, '/')) == NULL ) {
    /* die;  */ /* error: missing slash in prefix */
    return    IP_NOSLAS;
  }
  else {
    /* copy the IP part to another string, ERROR if 256 chars is not nough */
    
    len = slash - prefstr;
    if( len > 255 ) { 
      /* die;  */ /* ERROR - ip address part of the string too long. */
      return  IP_ADTOLO;
    }
    strncpy(ip, prefstr, len);
    ip[len]=0;

    if( (err=IP_addr_t2b( &(prefptr->ip), ip, expf)) != IP_OK) {
      /* die;   */ /* set error flag: incorrect address format */
      return err;
    }

    /* stop at first non-digit */
    for(trash = slash+1; isdigit(*trash) ; trash++);
    len = trash - (slash+1) ;
    if( len > 4 ) { 
      /* die; */ /* ERROR - prefix length part of the string too long. */
      return IP_PRTOLO;
    }
    strncpy(ip, slash+1, len);
    ip[len]=0;

    if( ut_dec_2_uns(ip, &prefptr->bits) < 0 ) {

      /*    if( sscanf (slash+1, "%d", &(prefptr->bits)) < 1 ) {
            die; */ /* handle syntax ERROR invalid characters found */
      return IP_INVPRF;
    }
  }
  /* sanitify the prefix - maybe some irrelevant bits are set */
  /* never create broken binary prefixes. */

  IP_pref_bit_fix(prefptr);

  return IP_OK;
}

/***************************************************************************/

/*+ convert a range string into a binary range struct. 
+*/
er_ret_t
IP_rang_t2b(ip_range_t *rangptr, char *rangstr, ip_exp_t expf)
{
  char *ips, *dash;
  er_ret_t err;

  if( expf != IP_PLAIN && expf != IP_EXPN ) {
    return IP_INVARG;
  }

  if( (dash=index(rangstr, '-')) == NULL ) {
    /*    die;  */ /* error: missing dash in range */
    return IP_INVRAN;
  }
  else {
    /* copy the first IP */
    if( (err = wr_calloc( (void*) &ips,1,dash - rangstr + 1)) != UT_OK ) {
      return err;
    }

    strncpy(ips, rangstr, dash - rangstr);
    
    /* convert the first IP into a binary struct */
    err=IP_addr_t2b( &(rangptr->begin), ips, expf);

    /* check later */ /* set error flag: incorrect address format */

    wr_free(ips);

    if( err != IP_OK ) {
      return err;
    }

    /* now find the other ip, skip the space */
    ips=dash+1;
    while( *ips == ' ' ) {
      ips++;
    }
    
    /* convert the second IP into a binary struct */
    if( (err=IP_addr_t2b( &(rangptr->end), ips, expf)) != IP_OK ) {
      /* die;  */ /* incorrect address format */
      return err;
    }
    
    if( rangptr->begin.space != rangptr->end.space ) {
      /* die;  */ /* incompatible IP spaces */
      return IP_INVRAN;
    }
    
    return IP_OK;
  }
}

/***************************************************************************/
/*+ convert the socket's idea of address into a binary range struct. 

  space    select the address type (and consequently struct type)
*/

er_ret_t
IP_addr_s2b(ip_addr_t *addrptr, 
	    void      *addr_in, 
	    int       addr_len)
{
  if( addr_len == sizeof(struct sockaddr_in) 
      && ((struct sockaddr_in *)addr_in)->sin_family == AF_INET ) {
    addrptr->space = IP_V4;
    addrptr->words[0] = 
      ntohl( ((struct sockaddr_in*)addr_in)->sin_addr.s_addr);
    
    addrptr->words[1] = addrptr->words[2] = addrptr->words[3] = 0;
  }
  else { /* unsupported family or invalid struct */
    die;
  }
  return IP_OK;
}


/*+converts the IP binary address (binaddr) to a string (ascaddr) 
   of at most strmax characters. Independent of the result
   (success or failure) it messes up the string.
+*/
er_ret_t
IP_addr_b2a( ip_addr_t *binaddr, char *ascaddr, int strmax ) 
{
  
  if(binaddr->space == IP_V4) {
    if (snprintf(ascaddr, strmax, "%d.%d.%d.%d",
		 ((binaddr->words[0]) & ((unsigned)0xff<<24))>>24,
		 ((binaddr->words[0]) & (0xff<<16))>>16,
		 ((binaddr->words[0]) & (0xff<<8))>>8,
		 ((binaddr->words[0]) & (0xff<<0))>>0
		 ) >= strmax) {
      /*die;  */ /* string too short */
      return IP_TOSHRT;
    }
    
#if 0
    char buf[5];
    int mask;
    
    *ascaddr = '\0';  

    /* this is very inefficient - but maybe this is the way to go for IPv6 */

    for(mask=24; mask >= 0; mask -= 8) {

      sprintf(buf, "%d%s", ((binaddr->words[0]) & 0xff<<mask)>>mask, 
	      mask==0 ? "" : ".");
      if( (strlen(buf)+strlen(ascaddr)) >= strmax ) {
	/* die;  */ /* error: insufficient space */
	return IP_TOSHRT;
      }
      else {
	strcat(ascaddr, buf);
      }
    }

#endif

  }
  else {
    /* IPv6 */
    /* not yet implemented. Sorry. */
    /* die; */
    return    IP_NO6YET;
  }
  return IP_OK;
}

/***************************************************************************/

/*+ convert a binary prefix back into ascii string at most strmax chars long 
+*/
er_ret_t
IP_pref_b2a(ip_prefix_t *prefptr, char *ascaddr, int strmax) 
{
  int strl;
  er_ret_t err;

  if( (err=IP_addr_b2a (&(prefptr->ip), ascaddr, strmax)) != IP_OK) {
    /*die;  */ /* what the hell */
    return err;
  }
  strl = strlen(ascaddr);
  strmax -= strl;

  /* now strmax holds the space that is left */

  if( snprintf(ascaddr+strl, strmax, "/%d", prefptr->bits) >= strmax) {
    /* die;  */ /* error: string too short */
    return IP_TOSHRT;
  }
  return IP_OK;
}



/***************************************************************************/
/*+ convert a binary range back into ascii string at most strmax chars long 
+*/
er_ret_t
IP_rang_b2a(ip_range_t *rangptr, char *ascaddr, int strmax) 
{
  int strl=0, strleft;
  er_ret_t err;

  strleft = strmax - strl;
  if( (err=IP_addr_b2a (&(rangptr->begin), ascaddr, strleft)) != IP_OK) {
    return err;
  }
  strl = strlen(ascaddr);
  
  strleft = strmax - strl;
  if( strleft < 5 ) {
    return IP_TOSHRT; 
  }
  strcat( ascaddr, " - " );
  strl += 3;

  strleft = strmax - strl;
  if( (err=IP_addr_b2a (&(rangptr->end), ascaddr+strl, strleft)) != IP_OK) {
    return err;
  }

  return IP_OK;
}

/***************************************************************************/
/*+ return the bitnum bit of the address, 
   COUNTING FROM THE TOP !!!!! , 
   starting with 0 for the *most significant bit*.
+*/
int
IP_addr_bit_get(ip_addr_t *binaddr, int bitnum) {
  register int bitval;
  
  /* IPv4 is easy... */
  bitval = (binaddr->words[0] & (0x80000000 >> (bitnum)));

  return (bitval != 0);

}

/***************************************************************************/
/*+ set the bitnum bit of the address to bitval, 
   COUNTING FROM THE TOP !!!!! , 
   starting with 0 for the *most significant bit*.
+*/
void
IP_addr_bit_set(ip_addr_t *binaddr, int bitnum, int bitval) {
  
  /* IPv4 is easy... */
  if ( bitval == 1 )
    binaddr->words[0] |= (0x80000000 >> (bitnum));
  else
    binaddr->words[0] &=  ~(0x80000000 >> (bitnum));
}
/***************************************************************************/

/*+ this fixes a prefix by setting insignificant bits to 0 +*/
void
IP_pref_bit_fix( ip_prefix_t *prefix ) 
{

unsigned mask=0xffffffff;

/* shorthand for ipv4 */

/* Shifting out by 32 bits does NOT turn all bits into 0... */
 if( prefix->bits < 32 ) {
   prefix->ip.words[0] &= ~(mask >> prefix->bits);
 }

#if 0
 int i;
 for(i=prefix->bits; i < IP_sizebits(prefix->ip.space) ; i++) {
   IP_addr_bit_set( & prefix->ip, i, 0);
 }
#endif
 

}

/***************************************************************************/


/*+
  This is a hook function for use with g_list_foreach, to print a list
  of prefixes 
+*/

void ip_print_prefix(void *dataptr, void *junk) {
  char ascpref[IP_PREFSTR_MAX];
  ip_prefix_t *binpref=dataptr;
  
  IP_pref_b2a( binpref, ascpref, IP_PREFSTR_MAX );
  printf ("prefix: %s\n", ascpref);
}


/***************************************************************************/

/*+ compares two IP addresses up to the bit # len, 
   returns 0 if equal, 1 if ptra greater, -1 if ptrb greater.
   
   It is the responsility of the caller to ensure that both addresses
   are from the same IP space.
+*/

int 
IP_addr_cmp(ip_addr_t *ptra, ip_addr_t *ptrb, int len)
{
  int a,b,i;

  for(i=0; i<len; i++) {
    a=IP_addr_bit_get(ptra, i);
    b=IP_addr_bit_get(ptrb, i);
    if( a != b ) {
      if( a > b ) return 1;
      else return -1;
    }
  }
  return 0;
}





/***************************************************************************/

/*+ 
this is a shorthand notation to pull out the first word of the address.
it is defined for the scope od the following functions
+*/
#define ad(which) (rangptr->which)

/***************************************************************************/
/*+ calculate the span of a range == size - 1 +*/

ip_rangesize_t 
/* ottrey 30/12/1999
IP_rang_span( ip_range_t *rangptr )
*/
IP_rang_span( ip_range_t rangptr )
{
  /* IPv4: */
  /* ottrey 30/12/1999
  return ad(end).words[0] - ad(begin).words[0];
  */
  return rangptr.end.words[0] - rangptr.begin.words[0];
}

/***************************************************************************/

/*+ Decomposes a binary range into prefixes and appends them to the list.
   Allocates prefix structures and list elements, they must be freed after use.

   returns a bitmask of prefix lengths used.
+*/

unsigned
IP_rang_decomp(ip_range_t *rangptr, GList **preflist)
{
unsigned            prefmask=0;
register int        slash=0;
register unsigned   c_dif, blk, ff;
ip_range_t  workrange;
ip_addr_t   workbegin;
ip_addr_t   workend;
ip_prefix_t *prefptr;

  if( ad(begin).words[0] > ad(end).words[0] ) {   /* has gone too far */
    return 0; 
  }

  if( ad(begin).words[0] == ad(end).words[0] ) {  /* one IP, i.e. /32 for IPv4 */
    prefmask |= 1;
    if(  wr_calloc( (void **)& prefptr, sizeof(ip_prefix_t), 1) != UT_OK) {
      die;
    }
    prefptr->ip = ad(begin);
    prefptr->bits = 32;

    *preflist = g_list_append( *preflist, prefptr );

    return prefmask;
  }
  
  c_dif = ad(end).words[0] - ad(begin).words[0];
  
  /* initialize work vars */

  workbegin = ad(begin);
  workend = ad(end);
 
  /* now find the biggest block fitting in this range */
  /* i.e. the first 2^n number smaller than c_dif */

  /* the loop would not work for /0 (some stupid queries may have that) */
  /* so this must be checked for separately */

  if( c_dif == 0xffffffff ) {
    /* they are already set to 0.0.0.0 - 255.255.255.255 */
    /* leave them alone.  */
    blk = 0;
    slash = 0;
  }
  else {

    c_dif += 1;     /* was not done earlier to protect from overflow */

    for(slash=1; 
	slash<32 && ((blk=((unsigned)0x80000000>>(slash-1))) & c_dif) == 0; 
	slash++) {}

    /* clear all digits in a and b under the blk one. */
    ff=blk-1;

    workbegin.words[0] = (workbegin.words[0] + ff) & ~ff;
    
    workend.words[0] = (workend.words[0] + 1) & ~ff;
  }
  
  if( workbegin.words[0] != workend.words[0] ) {
    prefmask |= blk;
    if(  wr_malloc( (void **)& prefptr, sizeof(ip_prefix_t)) != UT_OK) {
      die;
    }
    prefptr->ip = workbegin;
    prefptr->bits = slash;
    
    *preflist = g_list_append( *preflist, prefptr );
  }

  if( ad(begin).words[0] != workbegin.words[0] ) {
    workrange.begin = ad(begin);

    workbegin.words[0] -= 1;
    workrange.end   = workbegin;

    prefmask |= IP_rang_decomp( &workrange, preflist );
  }
  
  /* here we must protect from decomposition of  */
  /* 255.255.255.255 - 255.255.255.255 in case the range */
  /* 0.0.0.0 - 255.255.255.255 is considered. Hence the slash>0 condition. */

  if( workend.words[0] <= ad(end).words[0] && slash > 0) {
    workrange.begin = workend;
    workrange.end   = ad(end);

    prefmask |= IP_rang_decomp( &workrange, preflist );
  }
  
  return prefmask;
}


/***************************************************************************/

/*+ Similar name, slightly different code, totally different functionality.

   finds the smallest canonical block encompassing the whole given range, 
   then MODIFIES the range pointed to by the argument 
   so that it's equal to this block.

   returns a bitmask of prefix length used.
+*/

unsigned
IP_rang_encomp(ip_range_t *rangptr)
{
  unsigned    prefmask=0;
  int         slash=0;
  unsigned    c_dif, blk, ff, t_dif;
  ip_range_t  workrange;
  ip_addr_t   workbegin;
  ip_addr_t   workend;
  
  c_dif = ad(end).words[0] - ad(begin).words[0];

  /* now find the biggest block fitting in this range */
  /* i.e. the first 2^n number smaller than c_dif */

  /* the loop would not work for /0 (some stupid queries may have that) */
  /* so this must be checked for separately */

  if( c_dif > 0x80000000 ) {
    slash = 0;
    ff = 0xffffffff;
    blk = 0;

    workbegin = workend = ad(begin);
    workbegin.words[0] = 0;
    workend.words[0] = ff;
  }
  else {
    
    do {
      c_dif += 1;
      
      /* find the smallest block ENCOMPASSING c_dif. */
      /* this implies a loop from the bottom up */
      
      for(slash=32; 
	  slash>1 && (blk=((unsigned)0x80000000>>(slash-1))) < c_dif; 
	  slash--) {}
      
      ff=blk-1;
      
      /* clear all digits in workbegin under the blk one. */
      
      workbegin = ad(begin);
      workbegin.words[0] = workbegin.words[0] & ~ff;
    
      /* see if it has not made the difference larger than blk,  */
      /* retry if so */
      
      t_dif = c_dif;
      c_dif = ad(end).words[0] - workbegin.words[0];
      
    } while( c_dif >= t_dif );
    
    /* set the endpoint to workbegin + blocksize - 1 */
    /* which amounts to + ff */
    
    workend = ad(begin);
    workend.words[0] = workbegin.words[0] + ff;
  }


    /* set the range to new values */
  
    rangptr->begin = workbegin;
    rangptr->end   = workend;
}

/***************************************************************************/
/*+ sets a range equal to a prefix +*/

er_ret_t
IP_pref_2_rang( ip_range_t *rangptr, ip_prefix_t *prefptr )
{
  ip_rangesize_t span;

  ad(begin) = ad(end) = prefptr->ip;
  
  if( prefptr->bits > 0 ) {
    span = (1 << (32 - prefptr->bits)) - 1 ;
  }
  else {
    span = 0xffffffff;
  }
  
  ad(end).words[0] += span;
  
  return IP_OK;
}

#undef ad

/***************************************************************************/

/*+ 
   This is to parse a classfull address into a range.

   Takes the address by pointer from addrptr and puts the result
   at rangptr. 

   Throws error if the address does not fall into any of the 
   classfull categories 

+*/

er_ret_t
IP_rang_classful( ip_range_t *rangptr, ip_addr_t *addrptr)
{
int i;
unsigned b[4];

  if( addrptr->space != IP_V4 ) {
    /* it's IPv6. There are no classful ranges or anything like that. */
    /* we accept only explicit ranges */

    die;
  }
  
  rangptr->begin = *addrptr;
  rangptr->end.space = IP_V4;
  for(i=0; i<4; i++) {
    rangptr->end.words[i] = 0;
  }
  
  /* assume it's at least a valid IP. let's try different classes now */
	      
  /* we could have used a union here, but it would not work on */
  /* low endians. So byte by byte copying to and from an array. */
  
  for(i=0; i<4; i++) {
    b[i] = ( rangptr->begin.words[0] & (0xFF << i*8) ) >> i*8;
  }
  
  if( b[3] >= 1 && b[3] < 128 
      && b[2] == 0 && b[1] == 0 && b[0] == 0 ) {
    b[2]=b[1]=b[0]=255;
  }
  else if( b[3] >= 128 && b[3] < 192 
	   && b[1] == 0 && b[0] == 0 ) {
    b[1]=b[0]=255;
  }
  else if( b[3] >= 192 && b[3] < 224 
	   &&  b[0] == 0 ) {
    b[0]=255;
  }
  else if( b[3] >= 224 && b[3] < 255 ) {
    /* just leave it, make it a /32, i.e. begin == end */
  }
  else {
    /* Leave it and make it a /32 */
    /* This is AGAINST the rule! but we have some junk  */
    /* so we have to compensate for it. */
  }
  
  /* copy the (now - modified) bytes into the end of range */
  for(i=0; i<4; i++) {
    rangptr->end.words[0] |= (b[i] << i*8);
  }
  
  return IP_OK;
}


/***************************************************************************/
/*+ 
  Trying to be smart :-) and convert a query search term into prefix(es),
  regardless of whether specified as IP address, prefix or range.
 
  justcheck - if just checking the syntax (justcheck == 1), 
     then the prefixes are freed before the function returns,
     otherwise it is the responsibility of the caller to free the list.

+*/

er_ret_t
IP_smart_conv(char *key, 
	      int justcheck, 
	      int encomp, 
	      GList **preflist, 
	      ip_exp_t expf)
{
  int free_it;
  er_ret_t call_err, err=IP_OK;      /* let's be optimistic :-) */
  ip_prefix_t *querypref;

  /* if just checking the syntax (justcheck == 1), 
     then free_it = 1, 
     else 0, but may be modified later (in range conversion)
  */

  free_it = justcheck;
  
  if( (call_err = wr_malloc( (void **) &querypref, sizeof(ip_prefix_t))) 
      != UT_OK) {
    return call_err;
  }
  
  if( IP_pref_t2b(querypref, key, expf) == IP_OK ) {
    if( justcheck == 0) {
      *preflist = g_list_append(*preflist, querypref);
    }
  } 
  else {
    /* not a prefix.  */
    /* Maybe an IP ? */
    if( IP_addr_t2b( &(querypref->ip), key, expf) == IP_OK ) {

      /*convert to a /32 */
      querypref->bits = 32;

      if( justcheck == 0) {
	*preflist = g_list_append(*preflist, querypref);
      }
    }
    else {    
      /* hm, maybe a range then ? */
      ip_range_t myrang;
      
      /* won't use the querypref anymore, mark it for freeing later */
      free_it = 1;
      
      if( IP_rang_t2b(&myrang, key, expf) == IP_OK ) {
	/* Wow. Great.  */
	
	/* sometimes (exless match) we look for the first bigger(shorter)  */
	/* prefix containing this range. */
	
	if( encomp ) {
	  IP_rang_encomp(&myrang);
	}
	/* OK, now we can let the engine happily find that it's just one */
	/* range */
	
	if( justcheck == 0) {
	  IP_rang_decomp(&myrang, preflist);
	}
      }
      else {
	err = IP_INVARG; /* "conversion error" */
      }
    }
  }
  
  if( free_it ) {
    wr_free(querypref);
  }
  
  return err;
}


#ifdef MODULE_TEST
#include "ip_test.c"
#endif
