net.ripe.ipresource.Ipv6Address.java Source code

Java tutorial

Introduction

Here is the source code for net.ripe.ipresource.Ipv6Address.java

Source

/**
 * The BSD License
 *
 * Copyright (c) 2010-2012 RIPE NCC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *   - Neither the name of the RIPE NCC nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package net.ripe.ipresource;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import java.math.BigInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Ipv6 address. This implementation has no support for interfaces.
 */
public class Ipv6Address extends IpAddress {

    private static final long serialVersionUID = 2L;

    /* Pattern to match IPv6 addresses in forms defined in http://www.ietf.org/rfc/rfc4291.txt */
    private static final Pattern IPV6_PATTERN = Pattern.compile(
            "(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))");
    private static final int COLON_COUNT_FOR_EMBEDDED_IPV4 = 6;
    private static final int COLON_COUNT_IPV6 = 7;
    private static final String COLON = ":";

    /**
     * Mask for 16 bits, which is the length of one part of an IPv6 address.
     */
    private BigInteger PART_MASK = BigInteger.valueOf(0xffff);

    private final BigInteger value;

    public Ipv6Address(BigInteger value) {
        this.value = value;
    }

    @Override
    public IpResourceType getType() {
        return IpResourceType.IPv6;
    }

    @Override
    protected int doHashCode() {
        return value.hashCode();
    }

    @Override
    protected int doCompareTo(IpResource obj) {
        if (obj instanceof Ipv6Address) {
            Ipv6Address that = (Ipv6Address) obj;
            return this.getValue().compareTo(that.getValue());
        } else {
            return super.doCompareTo(obj);
        }
    }

    @Override
    public int getCommonPrefixLength(UniqueIpResource other) {
        Validate.isTrue(getType() == other.getType(), "incompatible resource types");
        BigInteger temp = this.getValue().xor(other.getValue());
        return getType().getBitSize() - temp.bitLength();
    }

    @Override
    public Ipv6Address lowerBoundForPrefix(int prefixLength) {
        BigInteger mask = bitMask(0, getType()).xor(bitMask(prefixLength, getType()));
        return new Ipv6Address(this.getValue().and(mask));
    }

    @Override
    public IpAddress upperBoundForPrefix(int prefixLength) {
        return new Ipv6Address(this.getValue().or(bitMask(prefixLength, getType())));
    }

    public static Ipv6Address parse(String ipAddressString) {
        Validate.notNull(ipAddressString);
        ipAddressString = ipAddressString.trim();
        Validate.isTrue(IPV6_PATTERN.matcher(ipAddressString).matches(),
                "Invalid IPv6 address: " + ipAddressString);

        ipAddressString = expandMissingColons(ipAddressString);
        if (isInIpv4EmbeddedIpv6Format(ipAddressString)) {
            ipAddressString = getIpv6AddressWithIpv4SectionInIpv6Notation(ipAddressString);
        }
        return new Ipv6Address(ipv6StringtoBigInteger(ipAddressString));
    }

    private static String expandMissingColons(String ipAddressString) {
        int colonCount = isInIpv4EmbeddedIpv6Format(ipAddressString) ? COLON_COUNT_FOR_EMBEDDED_IPV4
                : COLON_COUNT_IPV6;
        return ipAddressString.replace("::",
                StringUtils.repeat(":", colonCount - StringUtils.countMatches(ipAddressString, ":") + 2));
    }

    private static boolean isInIpv4EmbeddedIpv6Format(String ipAddressString) {
        return ipAddressString.contains(".");
    }

    private static String getIpv6AddressWithIpv4SectionInIpv6Notation(String ipAddressString) {
        String ipv6Section = StringUtils.substringBeforeLast(ipAddressString, ":");
        String ipv4Section = StringUtils.substringAfterLast(ipAddressString, ":");
        try {
            String ipv4SectionInIpv6Notation = StringUtils.join(
                    new Ipv6Address(Ipv4Address.parse(ipv4Section).getValue()).toString().split(":"), ":", 2, 4);
            return ipv6Section + ":" + ipv4SectionInIpv6Notation;
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Embedded Ipv4 in IPv6 address is invalid: " + ipAddressString, e);
        }
    }

    /**
     * Converts a fully expanded IPv6 string to a BigInteger
     *
     * @param ipAddressString Fully expanded address (i.e. no '::' shortcut)
     * @return Address as BigInteger
     */
    private static BigInteger ipv6StringtoBigInteger(String ipAddressString) {
        Pattern p = Pattern.compile(
                "([0-9a-fA-F]{0,4}):([0-9a-fA-F]{0,4}):([0-9a-fA-F]{0,4}):([0-9a-fA-F]{0,4}):([0-9a-fA-F]{0,4}):([0-9a-fA-F]{0,4}):([0-9a-fA-F]{0,4}):([0-9a-fA-F]{0,4})");
        Matcher m = p.matcher(ipAddressString);
        m.find();

        String ipv6Number = "";
        for (int i = 1; i <= m.groupCount(); i++) {
            String part = m.group(i);
            String padding = "0000".substring(0, 4 - part.length());
            ipv6Number = ipv6Number + padding + part;
        }

        return new BigInteger(ipv6Number, 16);
    }

    @Override
    public String toString(boolean defaultMissingOctets) {
        long[] parts = new long[8];
        String[] formatted = new String[parts.length];
        for (int i = 0; i < parts.length; ++i) {
            parts[i] = getValue().shiftRight((7 - i) * 16).and(PART_MASK).longValue();
            formatted[i] = Long.toHexString(parts[i]);
        }

        // Find longest sequence of zeroes. Use the first one if there are
        // multiple sequences of zeroes with the same length.
        int currentZeroPartsLength = 0;
        int currentZeroPartsStart = 0;
        int maxZeroPartsLength = 0;
        int maxZeroPartsStart = 0;
        for (int i = 0; i < parts.length; ++i) {
            if (parts[i] == 0) {
                if (currentZeroPartsLength == 0) {
                    currentZeroPartsStart = i;
                }
                ++currentZeroPartsLength;
                if (currentZeroPartsLength > maxZeroPartsLength) {
                    maxZeroPartsLength = currentZeroPartsLength;
                    maxZeroPartsStart = currentZeroPartsStart;
                }
            } else {
                currentZeroPartsLength = 0;
            }
        }

        if (maxZeroPartsLength <= 1) {
            return StringUtils.join(formatted, COLON);
        } else {
            String init = StringUtils.join(formatted, COLON, 0, maxZeroPartsStart);
            String tail = StringUtils.join(formatted, COLON, maxZeroPartsStart + maxZeroPartsLength,
                    formatted.length);
            return init + "::" + tail;
        }
    }

    @Override
    public final BigInteger getValue() {
        return value;
    }

    @Override
    public boolean isValidNetmask() {
        int bitLength = value.bitLength();
        if (bitLength < IpResourceType.IPv6.getBitSize()) {
            return false;
        }

        int lowestSetBit = value.getLowestSetBit();
        for (int i = bitLength - 1; i >= lowestSetBit; --i) {
            if (!value.testBit(i)) {
                return false;
            }
        }

        return true;
    }
}