/* -*- mode: java; c-basic-offset: 4; indent-tabs-mode: nil -*- * * Copyright (C) 2001 ArsDigita Corporation. All Rights Reserved. * * The contents of this file are subject to the ArsDigita Public * License (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of * the License at http://www.arsdigita.com/ADPL.txt * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * */ package com.arsdigita.auth.http; import java.net.InetAddress; import java.net.UnknownHostException; import com.arsdigita.util.Assert; import com.arsdigita.util.UncheckedWrapperException; /** *
* This class represents an IPV4 address range, such as 192.168.1.0/24 * or 192.168.1.0/255.255.255.0 (both these examples are equivalent). * An address range consists of an address and a netmask. *
* ** At the time that I wrote this class, I wasn't aware of any standard * class which supported this feature. However, it is clear that at some * point in the future such a class will be added to Java, at which * point this code will be redundant. *
* *
* NB. This will NOT work with JDK 1.4. To make it work, you need to
* manually replace all references to InetAddress to
* Inet4Address. Jeez, how much do I hate Java.
*
* Given a string in one of the valid forms below, construct an * IPV4 address range. *
* ** The valid forms for the string are: *
* *"192.168.1.0/24" "192.168.1.0/255.255.255.0" "192.168.1.15" (the implicit netmask is
* 255.255.255.255).
* If the string has an invalid format, this returns null.
*
Inet4AddressRange object or null.
*/
public static Inet4AddressRange getByName (String s)
{
InetAddress address, netmask;
try {
int i = s.indexOf ('/');
// If no "/" in the string, set netmask to 255.255.255.255 and try
// to set the address.
if (i == -1)
{
netmask = makeNetmask (32);
address = InetAddress.getByName (s);
}
else
{
// Split the string on the first "/" character.
String addrString = s.substring (0, i);
String netmaskString = s.substring (i+1, s.length ());
address = InetAddress.getByName (addrString);
// If the netmask is NOT just a simple number, resolve it.
if (!isNumber (netmaskString))
netmask = InetAddress.getByName (netmaskString);
// Otherwise the netmask is a simple number so infer it.
else
netmask = makeNetmask (Integer.parseInt (netmaskString));
}
}
catch (UnknownHostException ex) {
return null;
}
catch (SecurityException ex) {
throw new UncheckedWrapperException (ex);
}
return new Inet4AddressRange (address, netmask);
}
/**
* Returns true iff the string parameter looks like a number.
*/
private static boolean isNumber (String s)
{
for (int i = 0; i < s.length(); ++i)
if (s.charAt (i) < '0' || s.charAt (i) > '9')
return false;
return true;
}
/**
* Given an address and netmask, construct an object of this class.
*/
public Inet4AddressRange (InetAddress address, InetAddress netmask)
{
m_address = address;
m_netmask = netmask;
}
/**
* Return the address field of this object.
*/
public InetAddress getAddress ()
{
return m_address;
}
/**
* Return the netmask field of this object.
*/
public InetAddress getNetmask ()
{
return m_netmask;
}
/**
* Convert to a printable string.
*/
public String toString ()
{
return
inetAddressToString (m_address) + "/" + inetAddressToString (m_netmask);
}
/**
* Return true if and only if two Inet4AddressRange objects are
* semantically equal.
*/
public boolean equals (Inet4AddressRange other)
{
return
m_address.equals (other.m_address) &&
m_netmask.equals (other.m_netmask);
}
/**
* Return true if the address parameter given is inside this range
* of addresses.
*/
public boolean inRange (InetAddress address)
{
long maddressLong = inetAddressToLong (m_address);
long mnetmaskLong = inetAddressToLong (m_netmask);
long addressLong = inetAddressToLong (address);
return (addressLong & mnetmaskLong) == maddressLong;
}
/**
* This is the function we use to convert an InetAddress
* to a long (to get around the stupid lack of unsigned types in Java).
* This is public because it might be generally useful.
*/
public static long inetAddressToLong (InetAddress address)
{
byte[] bytes = address.getAddress ();
long a = bytes[0], b = bytes[1], c = bytes[2], d = bytes[3];
a &= 0xff; b &= 0xff; c &= 0xff; d &= 0xff;
return (a << 24) | (b << 16) | (c << 8) | d;
}
/**
* This is the function we use to convert a long to an
* InetAddress. This is public because it might be
* generally useful.
*/
public static InetAddress longToInetAddress (long address)
{
try {
return InetAddress.getByName (String.valueOf (address));
}
catch (UnknownHostException ex) {
throw new UncheckedWrapperException (ex);
}
catch (SecurityException ex) {
throw new UncheckedWrapperException (ex);
}
}
/**
* Convert an InetAddress to a string. The normal
* toString method is pretty useless.
*/
public static String inetAddressToString (InetAddress address)
{
byte[] bytes = address.getAddress ();
long a = bytes[0], b = bytes[1], c = bytes[2], d = bytes[3];
a &= 0xff; b &= 0xff; c &= 0xff; d &= 0xff;
return a + "." + b + "." + c + "." + d;
}
/**
* Private function to construct a netmask from a number.
* eg. 24 will return the address 255.255.255.0.
*/
private static InetAddress makeNetmask (int bits)
throws UnknownHostException, SecurityException
{
long n = (0xffffffff00000000L >> bits) & 0xffffffffL;
return longToInetAddress (n);
}
/**
* Test suite for this class.
*/
public static void main (String[] args)
throws Exception
{
// Test inetAddressToLong and longToInetAddress.
InetAddress addr = InetAddress.getByName ("192.168.0.99");
InetAddress addr2 = longToInetAddress (inetAddressToLong (addr));
String addrStr = inetAddressToString (addr);
String addr2Str = inetAddressToString (addr2);
System.out.println ("addr = " + addrStr + "; addr2 = " + addr2Str);
Assert.isTrue (addrStr.equals (addr2Str));
// Test makeNetmask.
addr = makeNetmask (0);
System.out.println ("makeNetmask(0) = " + addr);
Assert.isTrue (inetAddressToString (addr).equals ("0.0.0.0"));
addr = makeNetmask (8);
System.out.println ("makeNetmask(8) = " + addr);
Assert.isTrue (inetAddressToString (addr).equals ("255.0.0.0"));
addr = makeNetmask (16);
System.out.println ("makeNetmask(16) = " + addr);
Assert.isTrue (inetAddressToString (addr).equals ("255.255.0.0"));
addr = makeNetmask (24);
System.out.println ("makeNetmask(24) = " + addr);
Assert.isTrue (inetAddressToString (addr).equals ("255.255.255.0"));
addr = makeNetmask (28);
System.out.println ("makeNetmask(28) = " + addr);
Assert.isTrue (inetAddressToString (addr).equals ("255.255.255.240"));
addr = makeNetmask (32);
System.out.println ("makeNetmask(32) = " + addr);
Assert.isTrue (inetAddressToString (addr).equals ("255.255.255.255"));
// Test getByName.
Inet4AddressRange range
= Inet4AddressRange.getByName ("192.168.0.0/16");
System.out.println ("range = " + range.toString ());
Inet4AddressRange range2
= Inet4AddressRange.getByName ("192.168.0.0/255.255.0.0");
System.out.println ("range = " + range.toString ());
// Test equals.
Assert.isTrue (range.equals (range2));
range = Inet4AddressRange.getByName ("192.168.0.99");
System.out.println ("range = " + range.toString ());
// Test inRange.
range = Inet4AddressRange.getByName ("192.168.0.0/16");
addr = InetAddress.getByName ("192.168.0.99");
Assert.isTrue (range.inRange (addr));
range = Inet4AddressRange.getByName ("192.168.0.0/24");
addr = InetAddress.getByName ("192.168.2.99");
Assert.isTrue (! range.inRange (addr));
range = Inet4AddressRange.getByName ("192.168.0.99");
addr = InetAddress.getByName ("192.168.0.99");
Assert.isTrue (range.inRange (addr));
range = Inet4AddressRange.getByName ("192.168.0.99");
addr = InetAddress.getByName ("192.168.3.99");
Assert.isTrue (! range.inRange (addr));
System.out.println ("All test completed OK.");
}
};