import java.net.InetAddress;

public class InetNetwork
{
	/*
	 * Implements network masking, and is compatible with RFC 1518 and
	 * RFC 1519, which describe CIDR: Classless Inter-Domain Routing.
	 */

	private InetAddress network;
	private InetAddress netmask;

	public InetNetwork(InetAddress ip, InetAddress netmask)
	{
		network = maskIP(ip, netmask);
		this.netmask = netmask;
	}

	public boolean contains(final String name) throws java.net.UnknownHostException
	{
		return network.equals(maskIP(InetAddress.getByName(name), netmask));
	}

	public boolean contains(final InetAddress ip)
	{
		return network.equals(maskIP(ip, netmask));
	}

	public String toString()
	{
		return network.getHostAddress() + "/" + netmask.getHostAddress();
	}

	public static InetNetwork getFromString(String netspec) throws java.net.UnknownHostException
	{
//		System.out.println("arg: " + netspec);
		if (netspec.endsWith("*")) netspec = normalizeFromAsterisk(netspec);
		else
		{
			int iSlash = netspec.indexOf('/');
			if (iSlash == -1) netspec += "/255.255.255.255";
			else if (netspec.indexOf('.', iSlash) == -1) netspec = normalizeFromCIDR(netspec);
		}
//		System.out.println("net: " + netspec);

		return new InetNetwork(InetAddress.getByName(netspec.substring(0, netspec.indexOf('/'))),
							   InetAddress.getByName(netspec.substring(netspec.indexOf('/') + 1)));
	}

	public static InetAddress maskIP(final byte[] ip, final byte[] mask)
	{
		try
		{
/* JDK_1.3_ANT_KEY */
			return InetAddress.getByName
			(
				 Integer.toString((mask[0] & ip[0]) >>   0 & 0xFF, 10) + "." +
				 Integer.toString((mask[1] & ip[1]) >>   8 & 0xFF, 10) + "." +
				 Integer.toString((mask[2] & ip[2]) >>  16 & 0xFF, 10) + "." +
				 Integer.toString((mask[3] & ip[3]) >>  24 & 0xFF, 10)
			);
/* JDK_1.3_ANT_KEY */

/* JDK_1.4_ANT_KEY *
			return InetAddress.getByAddress(new byte[]
			{
				(byte) (mask[0] & ip[0]),
				(byte) (mask[1] & ip[1]),
				(byte) (mask[2] & ip[2]),
				(byte) (mask[3] & ip[3])
			});
* JDK_1.4_ANT_KEY */
		}
		catch(Exception _) {}
		{
			return null;
		}
	}

	public static InetAddress maskIP(final InetAddress ip, final InetAddress mask)
	{
		return maskIP(ip.getAddress(), mask.getAddress());
	}

	/*
	 * This converts from an uncommon "wildcard" CIDR format
	 * to "address + mask" format:
	 * 
	 *	 *               =>  000.000.000.0/000.000.000.0
	 *   xxx.*           =>  xxx.000.000.0/255.000.000.0
	 *   xxx.xxx.*       =>  xxx.xxx.000.0/255.255.000.0
	 *   xxx.xxx.xxx.*   =>  xxx.xxx.xxx.0/255.255.255.0
	 */
	static private String normalizeFromAsterisk(final String netspec)
	{
		String[] masks = {  "0.0.0.0/0.0.0.0", "0.0.0/255.0.0.0", "0.0/255.255.0.0", "0/255.255.255.0" };
		char[] srcb = netspec.toCharArray();				
		int octets = 0;
		for (int i = 1; i < netspec.length(); i++) {
			if (srcb[i] == '.') octets++;
		}
		return (octets == 0) ? masks[0] : netspec.substring(0, netspec.length() -1 ).concat(masks[octets]);
	}

	/*
	 * RFC 1518, 1519 - Classless Inter-Domain Routing (CIDR)
	 * This converts from "prefix + prefix-length" format to
	 * "address + mask" format, e.g. from xxx.xxx.xxx.xxx/yy
	 * to xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy.
	 */
	static private String normalizeFromCIDR(final String netspec)
	{
		final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1));
		final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1);

		return netspec.substring(0, netspec.indexOf('/') + 1) +
				Integer.toString(mask >> 24 & 0xFF, 10) + "." +
				Integer.toString(mask >> 16 & 0xFF, 10) + "." +
				Integer.toString(mask >>  8 & 0xFF, 10) + "." +
				Integer.toString(mask >>  0 & 0xFF, 10);
	}
}

