/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * 
 * Misc ip specific utilities, currently only IPV4 is supported.
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <glib.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*
 * Function     : cidr_to_mask
 * Description  : Takes a CIDR number and returns the proper, host byte ordered
 *                netmask.
 * Arguments    : bits    -- The number of bits for this netmask.
 * Returns      : The host byte ordered netmask value. This is suitable for
 *                direct conversion to dotted quad via the inet_ntoa call.
 */
gulong
cidr_to_mask (gint bits)
{
  gint  l_bits;

  if (bits < 1)
    return 0;
  if (bits > 32)
    l_bits = 32;
  else
    l_bits = bits;
  return htonl(l_bits == 32 ? (gulong)-1 : ~((gulong)-1 >> bits));
}

/*
 * Function    : mask_to_cidr
 * Description : This function will take a netmask and turn it back into the
 *               cidr number for that netmask.
 * Arguments   : 
 */
gint 
mask_to_cidr (gulong mask)
{
   register int bits; 
   register int count;

   if (mask == 0)
     return 0;
   bits = 0;
   for (count = 0; count < 32 ; count++)   /* Process all the bits */
     {
       bits += (mask & 1);               /* Pick off the LSb */
       mask = mask >> 1;                   /* rotate the rest down */
     }
   return(bits);
}

/*
 * Function    : ip_range
 * Description : Given a network and the number of bits in the net mask
 *               return the begining and ending ip address of that range.
 * Arguments   : network     -- the network,
 *               bits        -- The number of bits in the net mask.
 *               range_start -- A pointer to storage for the start.
 *               range_end   -- A pointer to storgae for the end.
 */
void
ip_range (gulong network, int bits, gulong *range_start, gulong *range_end)
{
  gulong  net_mask, bcast_mask;
  struct in_addr   inp;

  net_mask   = cidr_to_mask (bits);      /* Network */
  bcast_mask = pow (2, 32 - bits) - 1;   /* broadcast */
  bcast_mask = htonl (bcast_mask);
  inp.s_addr = network;

  if (range_start)
     *range_start = (network & net_mask);
  if (range_end)
     *range_end = network | bcast_mask;

}

/*
 * Function    : quad_to_parts
 * Description : This will take a dotted quad ip address and break it into
 *               the octet parts.
 * Arguments   :
 */
void
quad_to_parts (const gchar *quad, gint *a, gint *b, gint *c, gint *d)
{
  gchar   *work;
  gint   c1, c2, c3, c4;
  gint   rc;

  work = g_strdup (quad);      /* work on a private copy */
  rc = sscanf (work, "%d.%d.%d.%d", &c1, &c2, &c3, &c4);

  if (rc != 4)
    {
      g_free (work);
      return;
    }
  if (a)
    *a = c1;
  if (b)
    *b = c2;
  if (c)
    *c = c3;
  if (d)
    *d = c4;

  g_free (work);
}

/*
 * Glibc's inet_aton function is foo'd so we have this hack.
 */
gulong 
quad_to_ulong (const char *quad)
{
  gint  a,b,c,d;
  gulong rc;

  quad_to_parts (quad, &a, &b, &c, &d);
  rc = d;

  rc |= (a << 24) | (b << 16) | (c << 8);
  return htonl (rc);
}

#define UC(b) (((int)b)&0xff)

/*
 * Function    : ulong_to_sol_ip
 * Description : This function is equivlant to the libc version except that
 *               it will pad the ip address out to 'xxx.xxx.xxx.xxx' where
 *               each octet is three chars. This function assumes your 
 *               specifying the ip addres in decimal.
 */
gchar *
ulong_to_sol_quad (gulong addr, gchar *buf, gint bufsize)
{
  register gchar *p;

  p = (gchar *)&addr;
  (void)g_snprintf(buf, bufsize, "%0.3d.%0.3d.%0.3d.%0.3d", 
		 UC(p[0]),UC(p[1]),UC(p[2]),UC(p[3]));
  return (buf);
}

/*
 * Function    : next_ip_block
 * Description : Given the 32 bit ip address and the number of bits in the
 *               netmask, return the next block.
 */
gulong 
next_ip_block (gulong addr, gint bits)
{
  gulong    mask;

  mask = pow (2, 32 - bits);
  mask = htonl (mask);

  return (addr | mask);
}


/*
**  Given a host address and a network address, 
**  determine if the host address falls within the network address.
**
**  Returns TRUE if the host address falls within the network range
**          FALSE otherwise.
*/
#if 0
gboolean
is_host_on_net (hosts * host, net_entry * network)
{
  gulong              net;
  gint                bits;
  gchar               buf[26];
  gchar               nbuf[26];

  bits = mask_to_cidr (host->hl_netmask.s_addr);
  ip_range (host->hl_addr.s_addr, bits, &net, NULL);
 
  /* This is icky but the only way I could get it to work */
  ulong_to_sol_quad (net, buf, sizeof (buf));
  ulong_to_sol_quad (network->nl_net.s_addr, nbuf, sizeof (nbuf));

  return !strcmp (buf, nbuf);
}
#endif

/*
**  Given a host address and a netmask, return the address of the network.
*/

gchar *
host_and_netmask_to_network (gchar * address, gchar * netmask)
{
  struct in_addr   in_address;
  struct in_addr   in_netmask;
  struct in_addr   in_network;

  if (!inet_aton (address, &in_address))
    return NULL;

  if (!inet_aton (netmask, &in_netmask))
    return NULL;

  in_network.s_addr = in_address.s_addr & in_netmask.s_addr;

  return g_strdup (inet_ntoa (in_network));
}

/* EOF */
