/* *************************************************************
   RWhois Software

   Copyright (c) 1994 Scott Williamson and Mark Kosters
   Copyright (c) 1996-2000 Network Solutions, Inc.

   See the file LICENSE for conditions of use and distribution.
**************************************************************** */

#include "ip_network.h"

#include "common_regexps.h"
#include "misc.h"
#include "defines.h"

/* address convert from ascii ip network string to netinfo structure */
int
addrstring_to_ni( addstr, ni )
  char *addstr;
  struct netinfo *ni;
{
#ifdef HAVE_IPV6

  if ( inet_pton( AF_INET, addstr, &(ni->prefix) ) == 1 ) {
    ni->af = AF_INET;
    ni->masklen = 32;
    return( 0 );
  }

  else if ( inet_pton( AF_INET6, addstr, &(ni->prefix) ) == 1 ) {
    ni->af = AF_INET6;
    ni->masklen = 128;
    return( 0 );
  }

  else return( -1 );

#else

  long addr;

  addr = inet_addr(addstr);
  if (addr == -1) return( -1 );
  ni->af = AF_INET;
  ni->masklen = 32;
  memcpy(ni->prefix, &addr, sizeof(struct in_addr));

  return( 0 );

#endif
}

/* Mask addr to prefix */
void
mask_addr_to_len( ni, len )
  struct netinfo *ni;
  int           len;
{
  int numbytes, i;

  if ( ni->af == AF_INET ) numbytes = 4;
  else numbytes = 16;

  for( i = 0; i < numbytes; i++, len -= 8 ) {
    if ( len <= 0 ) ni->prefix[i] = 0;
    else if ( ( len > 0 ) && ( len < 8 ) )
           ni->prefix[i] &= 0xff << ( 8 - len );
  }

  return;
}

/* compares two numerical IP addresses in network byte order (big endian)
   (-2 if address family mismatch, -1 if a < b, 0 if a = b, 1 if a > b) */
int
compare_addr(a, b)
  struct netinfo *a;
  struct netinfo *b;
{
  int i, numbytes;

  if ( a->af != b->af ) return( -2 );

  if ( a->af == AF_INET ) numbytes = 4;
  else numbytes = 16;

  for (i = 0; i < numbytes; i++)
  {
    if ( a->prefix[i] < b->prefix[i] )
    {
      return(-1);
    }
    if ( a->prefix[i] > b->prefix[i] )
    {
      return(1);
    }
  }

  return(0);
}

int
is_network_valid_for_searching(value)
  char *value;
{
  static regexp *net_prog = NULL;

  if (!net_prog)
  {
    net_prog = regcomp(NETWORK_REGEXP);
  }

  if (regexec(net_prog, value))
  {
    return(TRUE);
  }
  else
  {
    return(FALSE);
  }
}

int
is_network_valid_for_index(line)
  char *line;
{
  static regexp *strict_net_prog = NULL;
  struct netinfo prefix;

  if (!strict_net_prog)
  {
    strict_net_prog = regcomp(NETWORK_REGEXP);
  }

  if (regexec(strict_net_prog, line))
  {
    if ( get_network_prefix_and_len( line, &prefix ) )
    {
      return TRUE;
    }
  }

  return FALSE;
}

int
is_cidr_network(value)
  char *value;
{
  static regexp *net_prog = NULL;

  if (!net_prog)
  {
    net_prog = regcomp(STRICT_CIDR_NET_REGEXP);
  }

  if (regexec(net_prog, value))
  {
    return(TRUE);
  }
  else
  {
    return(FALSE);
  }
}

/* determine_network_len_from_policy: given a network IP number
   without an explicit prefix length, determine the prefix length by
   the first octet and presence of 0 octets. */
int
determine_network_len_from_policy(addr)
  struct netinfo *addr;
{
  uint32_t *prefix;
  int      len = 32;

  if ( addr->af != AF_INET ) return( -1 );

  if ( addr->prefix[0] < 128 )
  {
    len = 8;
  }
  else if ( addr->prefix[0] < 192 )
  {
    len = 16;
  }
  else if ( addr->prefix[0] < 224 )
  {
    len = 24;
  }

  /* if the calculated host part has anything in it, something is wrong,
     so we will assume that it is a host address */
  prefix = (uint32_t *) &(addr->prefix);
  if ( ( *prefix & ( 0xffffffff << ( 32 - len ) ) ) != *prefix )
  {
    len = 32;
  }

  return(len);
}

/* calculates the network length by looking at the number of contiguous
   low-order non-zero octets.  128.0.0.0 would yield 8, 128.1.0.0 would
   yield 16, 128.0.1.0 would yield 24, and so on */

int
determine_network_len_from_octets(addr)
  struct netinfo *addr;
{
  int len = 32;
  int i;

  if ( addr->af != AF_INET ) return( -1 );

  for (i = 3; i >= 0; i--)
  {
    if ( addr->prefix[i] > 0 ) break;
    len -= 8;
  }

  return(len);
}

/* get_network_prefix_prefix_len_and_mask: This function parses
   network prefix and prefix length, given a string in quad-octet
   prefix/prefix length format */
int
get_network_prefix_and_len( str, ni )
  char          *str;
  struct netinfo *ni;
{
  char *buf;
  char *p;
  int  len = -1;
  int  maxlength;

  if ( NOT_STR_EXISTS( str ) || !ni )
  {
    return FALSE;
  }

  buf = xstrdup(str);

  /* Parse prefix and prefix length */
  if ((p = strrchr(buf, '/')) != NULL)
  {
    *p = '\0';
    p++;
    len = atoi(p);
  }

  if ( addrstring_to_ni( buf, ni ) )
  {
    free(buf);
    return( FALSE );
  }

  free(buf);

  if ( ni->af == AF_INET ) maxlength = 32;
  else maxlength = 128;

  if ( len >= 0 ) {
    /* check the length */
    if ( len > maxlength )
    {
      return(FALSE);
    }

    ni->masklen = len;
  }
  else { /* unspecified length so assume the maximum */
    ni->masklen = maxlength;
  }

  /* fix any bit masking problems */
  mask_addr_to_len( ni, ni->masklen );

  return(TRUE);
}

int
write_network( str, ni )
  char *str;
  struct netinfo *ni;
{
  if (!str)
  {
    return FALSE;
  }

  sprintf( str, "%s/%d", natop( ni ), ni->masklen );

  return TRUE;
}


char *
ni_to_addrstring(ni, str, str_len)
  struct netinfo        *ni;
  char                  *str;
  int                    str_len;
{
  if (ni->af == AF_INET)
  {
    if (str_len < 15) return NULL;
    sprintf(str, "%03hu:%03hu:%03hu:%03hu",
            ni->prefix[0], ni->prefix[1],
            ni->prefix[2], ni->prefix[3]);
    return str;
  }

#ifdef HAVE_IPV6
  if (ni->af == AF_INET6)
  {
    char *r;
    int i;
    
    if (str_len < 39) return NULL;

    r = str;
    for (i = 0; i < 16; i++)
    {
      sprintf(r, "%02x", ni->prefix[i]);
      r += 2;
      if (((i+1) % 2) == 0 && (i+1) < 16)
      {
        *r = ':';
        r++;
      }
    }
    *r = '\0';

    return str;
  }
#endif
  
  return NULL;
}

char *
natop( ni )
  struct netinfo *ni;
{
  static char str[MAX_LINE];

  return ni_to_addrstring(ni, str, sizeof(str));
}
