/*
* 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 org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
*/
public class CIDR6 extends CIDR {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CIDR6.class);
/** The big integer for the base address */
private BigInteger addressBigInt;
/** The big integer for the end address */
private final BigInteger addressEndBigInt;
protected CIDR6(Inet6Address newaddress, int newmask) {
cidrMask = newmask;
addressBigInt = ipv6AddressToBigInteger(newaddress);
BigInteger mask = ipv6CidrMaskToMask(newmask);
try {
addressBigInt = addressBigInt.and(mask);
baseAddress = bigIntToIPv6Address(addressBigInt);
} catch (UnknownHostException e) {
// this should never happen.
}
addressEndBigInt = addressBigInt.add(ipv6CidrMaskToBaseAddress(cidrMask)).subtract(BigInteger.ONE);
}
@Override
public InetAddress getEndAddress() {
try {
return bigIntToIPv6Address(addressEndBigInt);
} catch (UnknownHostException e) {
if (logger.isErrorEnabled()) {
logger.error("invalid ip address calculated as an end address");
}
return null;
}
}
public int compareTo(CIDR arg) {
if (arg instanceof CIDR4) {
BigInteger net = ipv6AddressToBigInteger(arg.baseAddress);
int res = net.compareTo(addressBigInt);
if (res == 0) {
if (arg.cidrMask == cidrMask) {
return 0;
}
if (arg.cidrMask < cidrMask) {
return -1;
}
return 1;
}
return res;
}
CIDR6 o = (CIDR6) arg;
if (o.addressBigInt.equals(addressBigInt) && o.cidrMask == cidrMask) {
return 0;
}
int res = o.addressBigInt.compareTo(addressBigInt);
if (res == 0) {
if (o.cidrMask < cidrMask) {
// greater Mask means less IpAddresses so -1
return -1;
}
return 1;
}
return res;
}
@Override
public boolean contains(InetAddress inetAddress) {
BigInteger search = ipv6AddressToBigInteger(inetAddress);
return search.compareTo(addressBigInt) >= 0 && search.compareTo(addressEndBigInt) <= 0;
}
/**
* Given an IPv6 baseAddress length, return the block length. I.e., a
* baseAddress length of 96 will return 2**32.
*/
private static BigInteger ipv6CidrMaskToBaseAddress(int cidrMask) {
return BigInteger.ONE.shiftLeft(128 - cidrMask);
}
private static BigInteger ipv6CidrMaskToMask(int cidrMask) {
return BigInteger.ONE.shiftLeft(128 - cidrMask).subtract(BigInteger.ONE).not();
}
/**
* Given an IPv6 address, convert it into a BigInteger.
*
* @return the integer representation of the InetAddress
* @throws IllegalArgumentException if the address is not an IPv6
* address.
*/
private static BigInteger ipv6AddressToBigInteger(InetAddress addr) {
byte[] ipv6;
if (addr instanceof Inet4Address) {
ipv6 = getIpV6FromIpV4((Inet4Address) addr);
} else {
ipv6 = addr.getAddress();
}
if (ipv6[0] == -1) {
return new BigInteger(1, ipv6);
}
return new BigInteger(ipv6);
}
/**
* Convert a big integer into an IPv6 address.
*
* @return the inetAddress from the integer
* @throws UnknownHostException if the big integer is too large,
* and thus an invalid IPv6 address.
*/
private static InetAddress bigIntToIPv6Address(BigInteger addr) throws UnknownHostException {
byte[] a = new byte[16];
byte[] b = addr.toByteArray();
if (b.length > 16 && !(b.length == 17 && b[0] == 0)) {
throw new UnknownHostException("invalid IPv6 address (too big)");
}
if (b.length == 16) {
return InetAddress.getByAddress(b);
}
// handle the case where the IPv6 address starts with "FF".
if (b.length == 17) {
System.arraycopy(b, 1, a, 0, 16);
} else {
// copy the address into a 16 byte array, zero-filled.
int p = 16 - b.length;
System.arraycopy(b, 0, a, p, b.length);
}
return InetAddress.getByAddress(a);
}
}