net.ripe.commons.ip.Ipv6 Maven / Gradle / Ivy
The newest version!
/**
* The MIT License (MIT)
*
* Copyright (c) 2011-2014, Yannis Gonianakis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.ripe.commons.ip;
import java.math.BigInteger;
import static java.math.BigInteger.ONE;
public final class Ipv6 extends AbstractIp {
private static final long serialVersionUID = -1L;
public static final BigInteger FOUR_OCTECT_MASK = BigInteger.valueOf(0xFFFF);
public static final int NUMBER_OF_BITS = 128;
public static final BigInteger MINIMUM_VALUE = BigInteger.ZERO;
public static final BigInteger MAXIMUM_VALUE = new BigInteger(String.valueOf((ONE.shiftLeft(NUMBER_OF_BITS)).subtract(ONE)));
public static final Ipv6 FIRST_IPV6_ADDRESS = Ipv6.of(MINIMUM_VALUE);
public static final Ipv6 LAST_IPV6_ADDRESS = Ipv6.of(MAXIMUM_VALUE);
private static final int MIN_PART_VALUE = 0x0;
private static final int MAX_PART_VALUE = 0xFFFF;
private static final int MAX_PART_LENGTH = 4;
private static final String DEFAULT_PARSING_ERROR_MESSAGE = "Invalid IPv6 address: '%s'";
private static final String COLON = ":";
private static final String ZERO = "0";
private static final int BITS_PER_PART = 16;
private static final int TOTAL_OCTETS = 8;
private static final int COLON_COUNT_IPV6 = 7;
private static final BigInteger MINUS_ONE = BigInteger.valueOf(-1);
private final BigInteger value;
protected Ipv6(BigInteger value) {
this.value = Validate.notNull(value, "value is required");
Validate.isTrue(value.compareTo(MINIMUM_VALUE) >= 0, "Value of IPv6 has to be greater than or equal to " + MINIMUM_VALUE);
Validate.isTrue(value.compareTo(MAXIMUM_VALUE) <= 0, "Value of IPv6 has to be less than or equal to " + MAXIMUM_VALUE);
}
BigInteger value() {
return value;
}
public static Ipv6 of(BigInteger value) {
return new Ipv6(value);
}
public static Ipv6 of(String value) {
return parse(value);
}
@Override
public int compareTo(Ipv6 other) {
return value.compareTo(other.value);
}
@Override
public Ipv6 next() {
return new Ipv6(value.add(ONE));
}
@Override
public Ipv6 previous() {
return new Ipv6(value.subtract(ONE));
}
@Override
public boolean hasNext() {
return this.compareTo(LAST_IPV6_ADDRESS) < 0;
}
@Override
public boolean hasPrevious() {
return this.compareTo(FIRST_IPV6_ADDRESS) > 0;
}
@Override
public Ipv6Range asRange() {
return new Ipv6Range(this, this);
}
@Override
public String toString() {
long[] parts = new long[8];
// 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) {
parts[i] = value().shiftRight((7 - i) * BITS_PER_PART).and(FOUR_OCTECT_MASK).longValue();
if (parts[i] == 0) {
if (currentZeroPartsLength == 0) {
currentZeroPartsStart = i;
}
++currentZeroPartsLength;
if (currentZeroPartsLength > maxZeroPartsLength) {
maxZeroPartsLength = currentZeroPartsLength;
maxZeroPartsStart = currentZeroPartsStart;
}
} else {
currentZeroPartsLength = 0;
}
}
StringBuilder sb = new StringBuilder(39);
if (maxZeroPartsStart == 0 && maxZeroPartsLength > 1) {
sb.append(COLON);
}
String delimiter = "";
for (int i = 0; i < parts.length; ++i) {
if (i == maxZeroPartsStart && maxZeroPartsLength > 1) {
i += maxZeroPartsLength;
sb.append(COLON);
}
sb.append(delimiter);
if (i <= 7) {
sb.append(Long.toHexString(parts[i]));
} else {
break;
}
delimiter = COLON;
}
return sb.toString();
}
/**
* Parses a String into an {@link Ipv6} address.
*
* @param ipv6Address a text representation of an IPv6 address as defined in rfc4291
* @return a new {@link Ipv6}
* @throws NullPointerException if the string argument is null
* @throws IllegalArgumentException if the string cannot be parsed
* @see rfc4291 - IP Version 6 Addressing Architecture
*/
public static Ipv6 parse(final String ipv6Address) {
try {
String ipv6String = Validate.notNull(ipv6Address).trim();
Validate.isTrue(!ipv6String.isEmpty());
final boolean isIpv6AddressWithEmbeddedIpv4 = ipv6String.contains(".");
if (isIpv6AddressWithEmbeddedIpv4) {
ipv6String = getIpv6AddressWithIpv4SectionInIpv6Notation(ipv6String);
}
final int indexOfDoubleColons = ipv6String.indexOf("::");
final boolean isShortened = indexOfDoubleColons != -1;
if (isShortened) {
Validate.isTrue(indexOfDoubleColons == ipv6String.lastIndexOf("::"));
ipv6String = expandMissingColons(ipv6String, indexOfDoubleColons);
}
final String[] split = ipv6String.split(COLON, TOTAL_OCTETS);
Validate.isTrue(split.length == TOTAL_OCTETS);
BigInteger ipv6value = BigInteger.ZERO;
for (String part : split) {
Validate.isTrue(part.length() <= MAX_PART_LENGTH);
Validate.checkRange(Integer.parseInt(part, BITS_PER_PART), MIN_PART_VALUE, MAX_PART_VALUE);
ipv6value = ipv6value.shiftLeft(BITS_PER_PART).add(new BigInteger(part, BITS_PER_PART));
}
return new Ipv6(ipv6value);
} catch (Exception e) {
throw new IllegalArgumentException(String.format(DEFAULT_PARSING_ERROR_MESSAGE, ipv6Address), e);
}
}
private static String expandMissingColons(final String ipv6String, final int indexOfDoubleColons) {
final int colonCount = countColons(ipv6String);
Validate.isTrue(colonCount >= 2 && colonCount <= COLON_COUNT_IPV6 + 1);
final int missingZeros = COLON_COUNT_IPV6 - colonCount + 1;
String leftPart = ipv6String.substring(0, indexOfDoubleColons);
String rightPart = ipv6String.substring(indexOfDoubleColons + 2);
if (missingZeros == 0) {
Validate.isTrue(leftPart.isEmpty() || rightPart.isEmpty());
}
if (leftPart.isEmpty()) {
leftPart = ZERO;
}
if (rightPart.isEmpty()) {
rightPart = ZERO;
}
StringBuilder sb = new StringBuilder();
sb.append(leftPart);
for (int i = 0; i < missingZeros; i++) {
sb.append(COLON).append(ZERO);
}
sb.append(COLON).append(rightPart);
return sb.toString();
}
private static int countColons(String ipv6String) {
int count = 0;
for (char c : ipv6String.toCharArray()) {
if (c == ':') {
count++;
}
}
return count;
}
private static String getIpv6AddressWithIpv4SectionInIpv6Notation(String ipv6String) {
final int indexOfLastColon = ipv6String.lastIndexOf(COLON);
final String ipv6Section = ipv6String.substring(0, indexOfLastColon);
final String ipv4Section = ipv6String.substring(indexOfLastColon + 1);
final Ipv4 ipv4 = Ipv4.parse(ipv4Section);
final Ipv6 ipv6FromIpv4 = new Ipv6(BigInteger.valueOf(ipv4.value()));
return ipv6Section + ipv6FromIpv4.toString().substring(1);
}
@Override
public int bitSize() {
return NUMBER_OF_BITS;
}
@Override
public BigInteger asBigInteger() {
return value;
}
@Override
public Ipv6 lowerBoundForPrefix(int prefixLength) {
Validate.checkRange(prefixLength, 0, NUMBER_OF_BITS);
BigInteger mask = bitMask(0).xor(bitMask(prefixLength));
return new Ipv6(value.and(mask));
}
@Override
public Ipv6 upperBoundForPrefix(int prefixLength) {
Validate.checkRange(prefixLength, 0, NUMBER_OF_BITS);
return new Ipv6(value.or(bitMask(prefixLength)));
}
private BigInteger bitMask(int prefixLength) {
return ONE.shiftLeft(NUMBER_OF_BITS - prefixLength).add(MINUS_ONE);
}
@Override
public int getCommonPrefixLength(Ipv6 other) {
BigInteger temp = value.xor(other.value);
return NUMBER_OF_BITS - temp.bitLength();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Ipv6 that = (Ipv6) o;
return value.equals(that.value);
}
@Override
public int hashCode() {
return value.hashCode();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy