/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jboss.netty.handler.ipfilter;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
/**
*/
public abstract class CIDR implements Comparable<CIDR> {
/** The base address of the CIDR notation */
protected InetAddress baseAddress;
/** The mask used in the CIDR notation */
protected int cidrMask;
/**
* Create CIDR using the CIDR Notation
*
* @return the generated CIDR
*/
public static CIDR newCIDR(InetAddress baseAddress, int cidrMask) throws UnknownHostException {
if (cidrMask < 0) {
throw new UnknownHostException("Invalid mask length used: " + cidrMask);
}
if (baseAddress instanceof Inet4Address) {
if (cidrMask > 32) {
throw new UnknownHostException("Invalid mask length used: " + cidrMask);
}
return new CIDR4((Inet4Address) baseAddress, cidrMask);
}
// IPv6.
if (cidrMask > 128) {
throw new UnknownHostException("Invalid mask length used: " + cidrMask);
}
return new CIDR6((Inet6Address) baseAddress, cidrMask);
}
/**
* Create CIDR using the normal Notation
*
* @return the generated CIDR
*/
public static CIDR newCIDR(InetAddress baseAddress, String scidrMask) throws UnknownHostException {
int cidrMask = getNetMask(scidrMask);
if (cidrMask < 0) {
throw new UnknownHostException("Invalid mask length used: " + cidrMask);
}
if (baseAddress instanceof Inet4Address) {
if (cidrMask > 32) {
throw new UnknownHostException("Invalid mask length used: " + cidrMask);
}
return new CIDR4((Inet4Address) baseAddress, cidrMask);
}
cidrMask += 96;
// IPv6.
if (cidrMask > 128) {
throw new UnknownHostException("Invalid mask length used: " + cidrMask);
}
return new CIDR6((Inet6Address) baseAddress, cidrMask);
}
/**
* Create CIDR using the CIDR or normal Notation<BR>
* i.e.:
* CIDR subnet = newCIDR ("10.10.10.0/24"); or
* CIDR subnet = newCIDR ("1fff:0:0a88:85a3:0:0:ac1f:8001/24"); or
* CIDR subnet = newCIDR ("10.10.10.0/255.255.255.0");
*
* @return the generated CIDR
*/
public static CIDR newCIDR(String cidr) throws UnknownHostException {
int p = cidr.indexOf('/');
if (p < 0) {
throw new UnknownHostException("Invalid CIDR notation used: " + cidr);
}
String addrString = cidr.substring(0, p);
String maskString = cidr.substring(p + 1);
InetAddress addr = addressStringToInet(addrString);
int mask;
if (maskString.indexOf('.') < 0) {
mask = parseInt(maskString, -1);
} else {
mask = getNetMask(maskString);
if (addr instanceof Inet6Address) {
mask += 96;
}
}
if (mask < 0) {
throw new UnknownHostException("Invalid mask length used: " + maskString);
}
return newCIDR(addr, mask);
}
/** @return the baseAddress of the CIDR block. */
public InetAddress getBaseAddress() {
return baseAddress;
}
/** @return the Mask length. */
public int getMask() {
return cidrMask;
}
/** @return the textual CIDR notation. */
@Override
public String toString() {
return baseAddress.getHostAddress() + '/' + cidrMask;
}
/** @return the end address of this block. */
public abstract InetAddress getEndAddress();
/**
* Compares the given InetAddress against the CIDR and returns true if
* the ip is in the subnet-ip-range and false if not.
*
* @return returns true if the given IP address is inside the currently
* set network.
*/
public abstract boolean contains(InetAddress inetAddress);
@Override
public boolean equals(Object o) {
if (!(o instanceof CIDR)) {
return false;
}
return compareTo((CIDR) o) == 0;
}
@Override
public int hashCode() {
return baseAddress.hashCode();
}
/**
* Convert an IPv4 or IPv6 textual representation into an
* InetAddress.
*
* @return the created InetAddress
*/
private static InetAddress addressStringToInet(String addr) throws UnknownHostException {
return InetAddress.getByName(addr);
}
/**
* Get the Subnet's Netmask in Decimal format.<BR>
* i.e.: getNetMask("255.255.255.0") returns the integer CIDR mask
*
* @param netMask a network mask
* @return the integer CIDR mask
*/
private static int getNetMask(String netMask) {
StringTokenizer nm = new StringTokenizer(netMask, ".");
int i = 0;
int[] netmask = new int[4];
while (nm.hasMoreTokens()) {
netmask[i] = Integer.parseInt(nm.nextToken());
i++;
}
int mask1 = 0;
for (i = 0; i < 4; i++) {
mask1 += Integer.bitCount(netmask[i]);
}
return mask1;
}
/**
* @param intstr a string containing an integer.
* @param def the default if the string does not contain a valid
* integer.
* @return the inetAddress from the integer
*/
private static int parseInt(String intstr, int def) {
Integer res;
if (intstr == null) {
return def;
}
try {
res = Integer.decode(intstr);
} catch (Exception e) {
res = def;
}
return res.intValue();
}
/**
* Compute a byte representation of IpV4 from a IpV6
*
* @return the byte representation
* @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4
*/
public static byte[] getIpV4FromIpV6(Inet6Address address) {
byte[] baddr = address.getAddress();
for (int i = 0; i < 9; i++) {
if (baddr[i] != 0) {
throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context");
}
}
if (baddr[10] != 0 && baddr[10] != 0xFF || baddr[11] != 0 && baddr[11] != 0xFF) {
throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context");
}
return new byte[]
{baddr[12], baddr[13], baddr[14], baddr[15]};
}
/**
* Compute a byte representation of IpV6 from a IpV4
*
* @return the byte representation
* @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4
*/
public static byte[] getIpV6FromIpV4(Inet4Address address) {
byte[] baddr = address.getAddress();
return new byte[]
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, baddr[0], baddr[1], baddr[2], baddr[3]};
}
}