NetUtil IPv6 bugs related to IPv4 and compression
Motivation: NetUtil#getByName and NetUtil#isValidIpV6Address do not strictly enforce the format of IPv4 addresses that are allowed to be embedded in IPv6 addresses as specified in https://tools.ietf.org/html/rfc4291#section-2.5.5. This may lead to invalid addresses being parsed, or invalid addresses being considered valid. Compression of a single IPv6 word was also not handled correctly if there are 7 : characters. Modifications: - NetUtil#isValidIpV6Address should enforce the IPv4-Compatible and IPv4-Mapped are the only valid formats for including IPv4 addresses as specified in https://tools.ietf.org/html/rfc4291#section-2.5.5 - NetUtil#getByName should more stritcly parse IPv6 addresses which contain IPv4 addresses as specified in https://tools.ietf.org/html/rfc4291#section-2.5.5 - NetUtil should allow compression even if the number of : characters is 7. - NetUtil#createByteArrayFromIpAddressString should use the same IP string to byte[] translation which is used in NetUtil#getByName Result: NetUtil#getByName and NetUtil#isValidIpV6Address respect the IPv6 RFC which defines the valid formats for embedding IPv4 addresses.
This commit is contained in:
parent
a45f9d7939
commit
9cb858fcf6
@ -314,8 +314,7 @@ public final class NetUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an byte[] based on an ipAddressString. No error handling is
|
||||
* performed here.
|
||||
* Creates an byte[] based on an ipAddressString. No error handling is performed here.
|
||||
*/
|
||||
public static byte[] createByteArrayFromIpAddressString(String ipAddressString) {
|
||||
|
||||
@ -343,112 +342,11 @@ public final class NetUtil {
|
||||
ipAddressString = ipAddressString.substring(0, percentPos);
|
||||
}
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ":.", true);
|
||||
ArrayList<String> hexStrings = new ArrayList<String>();
|
||||
ArrayList<String> decStrings = new ArrayList<String>();
|
||||
String token = "";
|
||||
String prevToken = "";
|
||||
int doubleColonIndex = -1; // If a double colon exists, we need to
|
||||
// insert 0s.
|
||||
|
||||
// Go through the tokens, including the seperators ':' and '.'
|
||||
// When we hit a : or . the previous token will be added to either
|
||||
// the hex list or decimal list. In the case where we hit a ::
|
||||
// we will save the index of the hexStrings so we can add zeros
|
||||
// in to fill out the string
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
prevToken = token;
|
||||
token = tokenizer.nextToken();
|
||||
|
||||
if (":".equals(token)) {
|
||||
if (":".equals(prevToken)) {
|
||||
doubleColonIndex = hexStrings.size();
|
||||
} else if (!prevToken.isEmpty()) {
|
||||
hexStrings.add(prevToken);
|
||||
}
|
||||
} else if (".".equals(token)) {
|
||||
decStrings.add(prevToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (":".equals(prevToken)) {
|
||||
if (":".equals(token)) {
|
||||
doubleColonIndex = hexStrings.size();
|
||||
} else {
|
||||
hexStrings.add(token);
|
||||
}
|
||||
} else if (".".equals(prevToken)) {
|
||||
decStrings.add(token);
|
||||
}
|
||||
|
||||
// figure out how many hexStrings we should have
|
||||
// also check if it is a IPv4 address
|
||||
int hexStringsLength = 8;
|
||||
|
||||
// If we have an IPv4 address tagged on at the end, subtract
|
||||
// 4 bytes, or 2 hex words from the total
|
||||
if (!decStrings.isEmpty()) {
|
||||
hexStringsLength -= 2;
|
||||
}
|
||||
|
||||
// if we hit a double Colon add the appropriate hex strings
|
||||
if (doubleColonIndex != -1) {
|
||||
int numberToInsert = hexStringsLength - hexStrings.size();
|
||||
for (int i = 0; i < numberToInsert; i ++) {
|
||||
hexStrings.add(doubleColonIndex, "0");
|
||||
}
|
||||
}
|
||||
|
||||
byte[] ipByteArray = new byte[IPV6_BYTE_COUNT];
|
||||
|
||||
// Finally convert these strings to bytes...
|
||||
for (int i = 0; i < hexStrings.size(); i ++) {
|
||||
convertToBytes(hexStrings.get(i), ipByteArray, i << 1);
|
||||
}
|
||||
|
||||
// Now if there are any decimal values, we know where they go...
|
||||
for (int i = 0; i < decStrings.size(); i ++) {
|
||||
ipByteArray[i + 12] = (byte) (Integer.parseInt(decStrings.get(i)) & 255);
|
||||
}
|
||||
return ipByteArray;
|
||||
return getIPv6ByName(ipAddressString, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 4 character hex word into a 2 byte word equivalent
|
||||
*/
|
||||
private static void convertToBytes(String hexWord, byte[] ipByteArray, int byteIndex) {
|
||||
|
||||
int hexWordLength = hexWord.length();
|
||||
int hexWordIndex = 0;
|
||||
ipByteArray[byteIndex] = 0;
|
||||
ipByteArray[byteIndex + 1] = 0;
|
||||
int charValue;
|
||||
|
||||
// high order 4 bits of first byte
|
||||
if (hexWordLength > 3) {
|
||||
charValue = getIntValue(hexWord.charAt(hexWordIndex ++));
|
||||
ipByteArray[byteIndex] |= charValue << 4;
|
||||
}
|
||||
|
||||
// low order 4 bits of the first byte
|
||||
if (hexWordLength > 2) {
|
||||
charValue = getIntValue(hexWord.charAt(hexWordIndex ++));
|
||||
ipByteArray[byteIndex] |= charValue;
|
||||
}
|
||||
|
||||
// high order 4 bits of second byte
|
||||
if (hexWordLength > 1) {
|
||||
charValue = getIntValue(hexWord.charAt(hexWordIndex ++));
|
||||
ipByteArray[byteIndex + 1] |= charValue << 4;
|
||||
}
|
||||
|
||||
// low order 4 bits of the first byte
|
||||
charValue = getIntValue(hexWord.charAt(hexWordIndex));
|
||||
ipByteArray[byteIndex + 1] |= charValue & 15;
|
||||
}
|
||||
|
||||
private static int getIntValue(char c) {
|
||||
switch (c) {
|
||||
case '0':
|
||||
@ -581,6 +479,35 @@ public final class NetUtil {
|
||||
numberOfPeriods ++;
|
||||
if (numberOfPeriods > 3) {
|
||||
return false;
|
||||
} else if (numberOfPeriods == 1) {
|
||||
// Verify this address is of the correct structure to contain an IPv4 address.
|
||||
// It must be IPv4-Mapped or IPv4-Compatible
|
||||
// (see https://tools.ietf.org/html/rfc4291#section-2.5.5).
|
||||
int j = i - word.length() - 2; // index of character before the previous ':'.
|
||||
final int beginColonIndex = ipAddress.lastIndexOf(':', j);
|
||||
if (beginColonIndex == -1) {
|
||||
return false;
|
||||
}
|
||||
char tmpChar = ipAddress.charAt(j);
|
||||
if (isValidIPv4MappedChar(tmpChar)) {
|
||||
if (j - beginColonIndex != 4 ||
|
||||
!isValidIPv4MappedChar(ipAddress.charAt(j - 1)) ||
|
||||
!isValidIPv4MappedChar(ipAddress.charAt(j - 2)) ||
|
||||
!isValidIPv4MappedChar(ipAddress.charAt(j - 3))) {
|
||||
return false;
|
||||
}
|
||||
j -= 5;
|
||||
} else if (tmpChar == '0' || tmpChar == ':') {
|
||||
--j;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
for (; j >= startOffset; --j) {
|
||||
tmpChar = ipAddress.charAt(j);
|
||||
if (tmpChar != '0' && tmpChar != ':') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isValidIp4Word(word.toString())) {
|
||||
return false;
|
||||
@ -588,12 +515,6 @@ public final class NetUtil {
|
||||
if (numberOfColons != 6 && !doubleColon) {
|
||||
return false;
|
||||
}
|
||||
// a special case ::1:2:3:4:5:d.d.d.d allows 7 colons with an
|
||||
// IPv4 ending, otherwise 7 :'s is bad
|
||||
if (numberOfColons == 7 && ipAddress.charAt(startOffset) != ':' &&
|
||||
ipAddress.charAt(1 + startOffset) != ':') {
|
||||
return false;
|
||||
}
|
||||
word.delete(0, word.length());
|
||||
break;
|
||||
|
||||
@ -635,7 +556,7 @@ public final class NetUtil {
|
||||
// Check if we have an IPv4 ending
|
||||
if (numberOfPeriods > 0) {
|
||||
// There is a test case with 7 colons and valid ipv4 this should resolve it
|
||||
if (numberOfPeriods != 3 || !(isValidIp4Word(word.toString()) && numberOfColons < 7)) {
|
||||
if (numberOfPeriods != 3 || !(isValidIp4Word(word.toString()) && (numberOfColons < 7 || doubleColon))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@ -679,6 +600,23 @@ public final class NetUtil {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
private static boolean isValidIPv4MappedChar(char c) {
|
||||
return c == 'f' || c == 'F';
|
||||
}
|
||||
|
||||
private static boolean isValidIPv4MappedSeparators(byte b0, byte b1, boolean mustBeZero) {
|
||||
// We allow IPv4 Mapped (https://tools.ietf.org/html/rfc4291#section-2.5.5.1)
|
||||
// and IPv4 compatible (https://tools.ietf.org/html/rfc4291#section-2.5.5.1).
|
||||
// The IPv4 compatible is deprecated, but it allows parsing of plain IPv4 addressed into IPv6-Mapped addresses.
|
||||
return b0 == b1 && (b0 == 0 || !mustBeZero && b1 == -1);
|
||||
}
|
||||
|
||||
private static boolean isValidIPv4Mapped(byte[] bytes, int currentIndex, int compressBegin, int compressLength) {
|
||||
return currentIndex <= 12 && currentIndex >= 2 &&
|
||||
isValidIPv4MappedSeparators(bytes[currentIndex - 1], bytes[currentIndex - 2],
|
||||
(compressBegin + compressLength) >= 14) && PlatformDependent.isZero(bytes, 0, currentIndex - 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string and parses it to see if it is a valid IPV4 address.
|
||||
*
|
||||
@ -748,11 +686,37 @@ public final class NetUtil {
|
||||
* @param ipv4Mapped
|
||||
* <ul>
|
||||
* <li>{@code true} To allow IPv4 mapped inputs to be translated into {@link Inet6Address}</li>
|
||||
* <li>{@code false} Don't turn IPv4 addressed to mapped addresses</li>
|
||||
* <li>{@code false} Consider IPv4 mapped addresses as invalid.</li>
|
||||
* </ul>
|
||||
* @return {@link Inet6Address} representation of the {@code ip} or {@code null} if not a valid IP address.
|
||||
*/
|
||||
public static Inet6Address getByName(CharSequence ip, boolean ipv4Mapped) {
|
||||
byte[] bytes = getIPv6ByName(ip, ipv4Mapped);
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Inet6Address.getByAddress(null, bytes, -1);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e); // Should never happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte array representation of a {@link CharSequence} IP address.
|
||||
* <p>
|
||||
* The {@code ipv4Mapped} parameter specifies how IPv4 addresses should be treated.
|
||||
* "IPv4 mapped" format as
|
||||
* defined in <a href="http://tools.ietf.org/html/rfc4291#section-2.5.5">rfc 4291 section 2</a> is supported.
|
||||
* @param ip {@link CharSequence} IP address to be converted to a {@link Inet6Address}
|
||||
* @param ipv4Mapped
|
||||
* <ul>
|
||||
* <li>{@code true} To allow IPv4 mapped inputs to be translated into {@link Inet6Address}</li>
|
||||
* <li>{@code false} Consider IPv4 mapped addresses as invalid.</li>
|
||||
* </ul>
|
||||
* @return byte array representation of the {@code ip} or {@code null} if not a valid IP address.
|
||||
*/
|
||||
private static byte[] getIPv6ByName(CharSequence ip, boolean ipv4Mapped) {
|
||||
final byte[] bytes = new byte[IPV6_BYTE_COUNT];
|
||||
final int ipLength = ip.length();
|
||||
int compressBegin = 0;
|
||||
@ -761,17 +725,17 @@ public final class NetUtil {
|
||||
int value = 0;
|
||||
int begin = -1;
|
||||
int i = 0;
|
||||
int ipv6Seperators = 0;
|
||||
int ipv4Seperators = 0;
|
||||
int ipv6Separators = 0;
|
||||
int ipv4Separators = 0;
|
||||
int tmp;
|
||||
boolean needsShift = false;
|
||||
for (; i < ipLength; ++i) {
|
||||
final char c = ip.charAt(i);
|
||||
switch (c) {
|
||||
case ':':
|
||||
++ipv6Seperators;
|
||||
++ipv6Separators;
|
||||
if (i - begin > IPV6_MAX_CHAR_BETWEEN_SEPARATOR ||
|
||||
ipv4Seperators > 0 || ipv6Seperators > IPV6_MAX_SEPARATORS ||
|
||||
ipv4Separators > 0 || ipv6Separators > IPV6_MAX_SEPARATORS ||
|
||||
currentIndex + 1 >= bytes.length) {
|
||||
return null;
|
||||
}
|
||||
@ -792,8 +756,8 @@ public final class NetUtil {
|
||||
if (compressBegin != 0 || (tmp < ipLength && ip.charAt(tmp) == ':')) {
|
||||
return null;
|
||||
}
|
||||
++ipv6Seperators;
|
||||
needsShift = ipv6Seperators == 2 && value == 0;
|
||||
++ipv6Separators;
|
||||
needsShift = ipv6Separators == 2 && value == 0;
|
||||
compressBegin = currentIndex;
|
||||
compressLength = bytes.length - compressBegin - 2;
|
||||
++i;
|
||||
@ -802,22 +766,27 @@ public final class NetUtil {
|
||||
begin = -1;
|
||||
break;
|
||||
case '.':
|
||||
++ipv4Seperators;
|
||||
if (i - begin > IPV4_MAX_CHAR_BETWEEN_SEPARATOR
|
||||
|| ipv4Seperators > IPV4_SEPARATORS
|
||||
|| (ipv6Seperators > 0 && (currentIndex + compressLength < 12))
|
||||
++ipv4Separators;
|
||||
tmp = i - begin; // tmp is the length of the current segment.
|
||||
if (tmp > IPV4_MAX_CHAR_BETWEEN_SEPARATOR
|
||||
|| begin < 0
|
||||
|| ipv4Separators > IPV4_SEPARATORS
|
||||
|| (ipv6Separators > 0 && (currentIndex + compressLength < 12))
|
||||
|| i + 1 >= ipLength
|
||||
|| currentIndex >= bytes.length
|
||||
|| begin < 0
|
||||
|| (begin == 0 && (i == 3 && (!isValidNumericChar(ip.charAt(2)) ||
|
||||
!isValidNumericChar(ip.charAt(1)) ||
|
||||
!isValidNumericChar(ip.charAt(0))) ||
|
||||
i == 2 && (!isValidNumericChar(ip.charAt(1)) ||
|
||||
!isValidNumericChar(ip.charAt(0))) ||
|
||||
i == 1 && !isValidNumericChar(ip.charAt(0))))) {
|
||||
|| ipv4Separators == 1 &&
|
||||
// We also parse pure IPv4 addresses as IPv4-Mapped for ease of use.
|
||||
((!ipv4Mapped || currentIndex != 0 && !isValidIPv4Mapped(bytes, currentIndex,
|
||||
compressBegin, compressLength)) ||
|
||||
(tmp == 3 && (!isValidNumericChar(ip.charAt(i - 1)) ||
|
||||
!isValidNumericChar(ip.charAt(i - 2)) ||
|
||||
!isValidNumericChar(ip.charAt(i - 3))) ||
|
||||
tmp == 2 && (!isValidNumericChar(ip.charAt(i - 1)) ||
|
||||
!isValidNumericChar(ip.charAt(i - 2))) ||
|
||||
tmp == 1 && !isValidNumericChar(ip.charAt(i - 1))))) {
|
||||
return null;
|
||||
}
|
||||
value <<= (IPV4_MAX_CHAR_BETWEEN_SEPARATOR - (i - begin)) << 2;
|
||||
value <<= (IPV4_MAX_CHAR_BETWEEN_SEPARATOR - tmp) << 2;
|
||||
|
||||
// The value integer holds at most 3 bytes from right (most significant) to left (least significant).
|
||||
// The following bit shifting is to restructure the bytes to be left (most significant) to
|
||||
@ -831,7 +800,7 @@ public final class NetUtil {
|
||||
begin = -1;
|
||||
break;
|
||||
default:
|
||||
if (!isValidHexChar(c) || (ipv4Seperators > 0 && !isValidNumericChar(c))) {
|
||||
if (!isValidHexChar(c) || (ipv4Separators > 0 && !isValidNumericChar(c))) {
|
||||
return null;
|
||||
}
|
||||
if (begin < 0) {
|
||||
@ -850,18 +819,17 @@ public final class NetUtil {
|
||||
|
||||
final boolean isCompressed = compressBegin > 0;
|
||||
// Finish up last set of data that was accumulated in the loop (or before the loop)
|
||||
if (ipv4Seperators > 0) {
|
||||
if (ipv4Separators > 0) {
|
||||
if (begin > 0 && i - begin > IPV4_MAX_CHAR_BETWEEN_SEPARATOR ||
|
||||
ipv4Seperators != IPV4_SEPARATORS ||
|
||||
ipv4Separators != IPV4_SEPARATORS ||
|
||||
currentIndex >= bytes.length) {
|
||||
return null;
|
||||
}
|
||||
if (ipv6Seperators == 0) {
|
||||
if (ipv6Separators == 0) {
|
||||
compressLength = 12;
|
||||
} else if (ipv6Seperators >= IPV6_MIN_SEPARATORS &&
|
||||
ip.charAt(ipLength - 1) != ':' &&
|
||||
(!isCompressed && (ipv6Seperators == 6 && ip.charAt(0) != ':') ||
|
||||
isCompressed && (ipv6Seperators + 1 < IPV6_MAX_SEPARATORS &&
|
||||
} else if (ipv6Separators >= IPV6_MIN_SEPARATORS &&
|
||||
(!isCompressed && (ipv6Separators == 6 && ip.charAt(0) != ':') ||
|
||||
isCompressed && (ipv6Separators < IPV6_MAX_SEPARATORS &&
|
||||
(ip.charAt(0) != ':' || compressBegin <= 2)))) {
|
||||
compressLength -= 2;
|
||||
} else {
|
||||
@ -880,11 +848,11 @@ public final class NetUtil {
|
||||
} else {
|
||||
tmp = ipLength - 1;
|
||||
if (begin > 0 && i - begin > IPV6_MAX_CHAR_BETWEEN_SEPARATOR ||
|
||||
ipv6Seperators < IPV6_MIN_SEPARATORS ||
|
||||
!isCompressed && (ipv6Seperators + 1 != IPV6_MAX_SEPARATORS ||
|
||||
ipv6Separators < IPV6_MIN_SEPARATORS ||
|
||||
!isCompressed && (ipv6Separators + 1 != IPV6_MAX_SEPARATORS ||
|
||||
ip.charAt(0) == ':' || ip.charAt(tmp) == ':') ||
|
||||
isCompressed && (ipv6Seperators > IPV6_MAX_SEPARATORS ||
|
||||
(ipv6Seperators == IPV6_MAX_SEPARATORS &&
|
||||
isCompressed && (ipv6Separators > IPV6_MAX_SEPARATORS ||
|
||||
(ipv6Separators == IPV6_MAX_SEPARATORS &&
|
||||
(compressBegin <= 2 && ip.charAt(0) != ':' ||
|
||||
compressBegin >= 14 && ip.charAt(tmp) != ':'))) ||
|
||||
currentIndex + 1 >= bytes.length) {
|
||||
@ -927,17 +895,14 @@ public final class NetUtil {
|
||||
}
|
||||
}
|
||||
|
||||
if (ipv4Mapped && ipv4Seperators > 0 &&
|
||||
bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 0 && bytes[3] == 0 && bytes[4] == 0 &&
|
||||
bytes[5] == 0 && bytes[6] == 0 && bytes[7] == 0 && bytes[8] == 0 && bytes[9] == 0) {
|
||||
if (ipv4Separators > 0) {
|
||||
// We only support IPv4-Mapped addresses [1] because IPv4-Compatible addresses are deprecated [2].
|
||||
// [1] https://tools.ietf.org/html/rfc4291#section-2.5.5.2
|
||||
// [2] https://tools.ietf.org/html/rfc4291#section-2.5.5.1
|
||||
bytes[10] = bytes[11] = (byte) 0xff;
|
||||
}
|
||||
|
||||
try {
|
||||
return Inet6Address.getByAddress(null, bytes, -1);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e); // Should never happen
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,8 +95,8 @@ public final class ConstantTimeUtils {
|
||||
// Benchmarking demonstrates that using an int to accumulate is faster than other data types.
|
||||
int b = 0;
|
||||
final int end = startPos1 + length;
|
||||
for (int i = startPos1, j = startPos2; i < end; ++i, ++j) {
|
||||
b |= bytes1[i] ^ bytes2[j];
|
||||
for (; startPos1 < end; ++startPos1, ++startPos2) {
|
||||
b |= bytes1[startPos1] ^ bytes2[startPos2];
|
||||
}
|
||||
return equalsConstantTime(b, 0);
|
||||
}
|
||||
|
@ -645,6 +645,19 @@ public final class PlatformDependent {
|
||||
PlatformDependent0.equals(bytes1, startPos1, bytes2, startPos2, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a subsection of an array is zero.
|
||||
* @param bytes The byte array.
|
||||
* @param startPos The starting index (inclusive) in {@code bytes}.
|
||||
* @param length The amount of bytes to check for zero.
|
||||
* @return {@code false} if {@code bytes[startPos:startsPos+length)} contains a value other than zero.
|
||||
*/
|
||||
public static boolean isZero(byte[] bytes, int startPos, int length) {
|
||||
return !hasUnsafe() || !unalignedAccess() ?
|
||||
isZeroSafe(bytes, startPos, length) :
|
||||
PlatformDependent0.isZero(bytes, startPos, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two {@code byte} arrays for equality without leaking timing information.
|
||||
* For performance reasons no bounds checking on the parameters is performed.
|
||||
@ -1179,8 +1192,18 @@ public final class PlatformDependent {
|
||||
|
||||
private static boolean equalsSafe(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
|
||||
final int end = startPos1 + length;
|
||||
for (int i = startPos1, j = startPos2; i < end; ++i, ++j) {
|
||||
if (bytes1[i] != bytes2[j]) {
|
||||
for (; startPos1 < end; ++startPos1, ++startPos2) {
|
||||
if (bytes1[startPos1] != bytes2[startPos2]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isZeroSafe(byte[] bytes, int startPos, int length) {
|
||||
final int end = startPos + length;
|
||||
for (; startPos < end; ++startPos) {
|
||||
if (bytes[startPos] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -552,7 +552,7 @@ final class PlatformDependent0 {
|
||||
}
|
||||
|
||||
static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
|
||||
if (length == 0) {
|
||||
if (length <= 0) {
|
||||
return true;
|
||||
}
|
||||
final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
|
||||
@ -620,6 +620,32 @@ final class PlatformDependent0 {
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isZero(byte[] bytes, int startPos, int length) {
|
||||
if (length <= 0) {
|
||||
return true;
|
||||
}
|
||||
final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
|
||||
int remainingBytes = length & 7;
|
||||
final long end = baseOffset + remainingBytes;
|
||||
for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
|
||||
if (UNSAFE.getLong(bytes, i) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingBytes >= 4) {
|
||||
remainingBytes -= 4;
|
||||
if (UNSAFE.getInt(bytes, baseOffset + remainingBytes) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (remainingBytes >= 2) {
|
||||
return UNSAFE.getChar(bytes, baseOffset) == 0 &&
|
||||
(remainingBytes == 2 || bytes[startPos + 2] == 0);
|
||||
}
|
||||
return bytes[startPos] == 0;
|
||||
}
|
||||
|
||||
static int hashCodeAscii(byte[] bytes, int startPos, int length) {
|
||||
int hash = HASH_CODE_ASCII_SEED;
|
||||
final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
|
||||
|
@ -17,6 +17,7 @@ package io.netty.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
@ -29,7 +30,11 @@ import static io.netty.util.NetUtil.createByteArrayFromIpAddressString;
|
||||
import static io.netty.util.NetUtil.getByName;
|
||||
import static io.netty.util.NetUtil.toAddressString;
|
||||
import static io.netty.util.NetUtil.toSocketAddressString;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class NetUtilTest {
|
||||
|
||||
@ -87,7 +92,11 @@ public class NetUtilTest {
|
||||
"[2001:0000:4136:e378:8000:63bf:3fff:fdd2]", "200100004136e378800063bf3ffffdd2",
|
||||
"0:1:2:3:4:5:6:789a", "0000000100020003000400050006789a",
|
||||
"0:1:2:3::f", "0000000100020003000000000000000f",
|
||||
"0:0:0:0:0:0:10.0.0.1", "0000000000000000000000000a000001",
|
||||
"0:0:0:0:0:0:10.0.0.1", "00000000000000000000ffff0a000001",
|
||||
"0:0:0:0:0::10.0.0.1", "00000000000000000000ffff0a000001",
|
||||
"::0:0:0:0:0:10.0.0.1", "00000000000000000000ffff0a000001",
|
||||
"0:0:0:0:0:0::10.0.0.1", "00000000000000000000ffff0a000001",
|
||||
"0:0:0:0:0:ffff:10.0.0.1", "00000000000000000000ffff0a000001",
|
||||
"::ffff:192.168.0.1", "00000000000000000000ffffc0a80001",
|
||||
// Test if various interface names after the percent sign are recognized.
|
||||
"[::1%1]", "00000000000000000000000000000001",
|
||||
@ -134,8 +143,6 @@ public class NetUtilTest {
|
||||
"0:0:0:0:0:x:10.0.0.1", null,
|
||||
// Test method with ipv4 style, bad ipv4 digits.
|
||||
"0:0:0:0:0:0:10.0.0.x", null,
|
||||
// Test method with ipv4 style, adjacent :
|
||||
"0:0:0:0:0::0:10.0.0.1", null,
|
||||
// Test method with ipv4 style, too many ipv6 digits.
|
||||
"0:0:0:0:0:00000:10.0.0.1", null,
|
||||
// Test method with ipv4 style, too many :
|
||||
@ -204,6 +211,8 @@ public class NetUtilTest {
|
||||
"0:0:0:0:0:0:10.0.0..1", null,
|
||||
// Double compression symbol
|
||||
"::0::", null,
|
||||
// Double compression symbol
|
||||
"12::0::12", null,
|
||||
// Empty contents
|
||||
"", null,
|
||||
// Trailing : (max number of : = 8)
|
||||
@ -220,6 +229,14 @@ public class NetUtilTest {
|
||||
"::ffff:0.0..0", null,
|
||||
// Not enough IPv4 entries trailing .
|
||||
"::ffff:127.0.0.", null,
|
||||
// Invalid trailing IPv4 character
|
||||
"::ffff:127.0.0.a", null,
|
||||
// Invalid leading IPv4 character
|
||||
"::ffff:a.0.0.1", null,
|
||||
// Invalid middle IPv4 character
|
||||
"::ffff:127.a.0.1", null,
|
||||
// Invalid middle IPv4 character
|
||||
"::ffff:127.0.a.1", null,
|
||||
// Not enough IPv4 entries no trailing .
|
||||
"::ffff:1.2.4", null,
|
||||
// Extra IPv4 entry
|
||||
@ -227,7 +244,43 @@ public class NetUtilTest {
|
||||
// Not enough IPv6 content
|
||||
":ffff:192.168.0.1.255", null,
|
||||
// Intermixed IPv4 and IPv6 symbols
|
||||
"::ffff:255.255:255.255.", null);
|
||||
"::ffff:255.255:255.255.", null,
|
||||
// Invalid IPv4 mapped address - invalid ipv4 separator
|
||||
"0:0:0::0:0:00f.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - not enough f's
|
||||
"0:0:0:0:0:fff:1.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - not IPv4 mapped, not IPv4 compatible
|
||||
"0:0:0:0:0:ff00:1.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - not IPv4 mapped, not IPv4 compatible
|
||||
"0:0:0:0:0:ff:1.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - too many f's
|
||||
"0:0:0:0:0:fffff:1.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - too many bytes (too many 0's)
|
||||
"0:0:0:0:0:0:ffff:1.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - too few bytes (not enough 0's)
|
||||
"0:0:0:0:ffff:1.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - 0's after the mapped ffff indicator
|
||||
"0:0:0:0:0:ffff::10.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - 0's after the mapped ffff indicator
|
||||
"0:0:0:0:ffff::10.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - 0's after the mapped ffff indicator
|
||||
"0:0:0:ffff::10.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - 0's after the mapped ffff indicator
|
||||
"0:0:ffff::10.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - 0's after the mapped ffff indicator
|
||||
"0:ffff::10.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - 0's after the mapped ffff indicator
|
||||
"ffff::10.0.0.1", null,
|
||||
// Invalid IPv4 mapped address - not all 0's before the mapped separator
|
||||
"1:0:0:0:0:ffff:10.0.0.1", null,
|
||||
// Address that is similar to IPv4 mapped, but is invalid
|
||||
"0:0:0:0:ffff:ffff:1.0.0.1", null,
|
||||
// Valid number of separators, but invalid IPv4 format
|
||||
"::1:2:3:4:5:6.7.8.9", null,
|
||||
// Too many digits
|
||||
"0:0:0:0:0:0:ffff:10.0.0.1", null,
|
||||
// Invalid IPv4 format
|
||||
":1.2.3.4", null);
|
||||
|
||||
private static final Map<byte[], String> ipv6ToAddressStrings = new HashMap<byte[], String>() {
|
||||
private static final long serialVersionUID = 2999763170377573184L;
|
||||
@ -344,6 +397,24 @@ public class NetUtilTest {
|
||||
"1.2.3.4", "::ffff:1.2.3.4",
|
||||
"192.168.0.1", "::ffff:192.168.0.1",
|
||||
|
||||
// IPv4 compatible addresses are deprecated [1], so we don't support outputting them, but we do support
|
||||
// parsing them into IPv4 mapped addresses. These values are treated the same as a plain IPv4 address above.
|
||||
// [1] https://tools.ietf.org/html/rfc4291#section-2.5.5.1
|
||||
"0:0:0:0:0:0:255.254.253.252", "::ffff:255.254.253.252",
|
||||
"0:0:0:0:0::1.2.3.4", "::ffff:1.2.3.4",
|
||||
"0:0:0:0::1.2.3.4", "::ffff:1.2.3.4",
|
||||
"0:0:0:0:0:0::1.2.3.4", "::ffff:1.2.3.4",
|
||||
"::0:0:0:0:0:1.2.3.4", "::ffff:1.2.3.4",
|
||||
"::0:0:0:0:1.2.3.4", "::ffff:1.2.3.4",
|
||||
"::0:0:0:0:1.2.3.4", "::ffff:1.2.3.4",
|
||||
"::0:0:0:1.2.3.4", "::ffff:1.2.3.4",
|
||||
"::0:0:1.2.3.4", "::ffff:1.2.3.4",
|
||||
"::0:1.2.3.4", "::ffff:1.2.3.4",
|
||||
"::1.2.3.4", "::ffff:1.2.3.4",
|
||||
|
||||
// IPv4 mapped (fully specified)
|
||||
"0:0:0:0:0:ffff:1.2.3.4", "::ffff:1.2.3.4",
|
||||
|
||||
// IPv6 addresses
|
||||
// Fully specified
|
||||
"2001:0:4136:e378:8000:63bf:3fff:fdd2", "2001:0:4136:e378:8000:63bf:3fff:fdd2",
|
||||
@ -435,10 +506,10 @@ public class NetUtilTest {
|
||||
@Test
|
||||
public void testIsValidIpV6Address() {
|
||||
for (String host : validIpV6Hosts.keySet()) {
|
||||
assertTrue(NetUtil.isValidIpV6Address(host));
|
||||
assertTrue(host, NetUtil.isValidIpV6Address(host));
|
||||
}
|
||||
for (String host : invalidIpV6Hosts.keySet()) {
|
||||
assertFalse(NetUtil.isValidIpV6Address(host));
|
||||
assertFalse(host, NetUtil.isValidIpV6Address(host));
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,20 +556,20 @@ public class NetUtilTest {
|
||||
@Test
|
||||
public void testIpv4MappedIp6GetByName() {
|
||||
for (Entry<String, String> testEntry : ipv4MappedToIPv6AddressStrings.entrySet()) {
|
||||
assertEquals(
|
||||
testEntry.getValue(),
|
||||
toAddressString(getByName(testEntry.getKey(), true), true));
|
||||
Inet6Address inet6Address = getByName(testEntry.getKey(), true);
|
||||
assertNotNull(testEntry.getKey() + ", " + testEntry.getValue(), inet6Address);
|
||||
assertEquals(testEntry.getKey(), testEntry.getValue(), toAddressString(inet6Address, true));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testinvalidIpv4MappedIp6GetByName() {
|
||||
for (String testEntry : invalidIpV4Hosts.keySet()) {
|
||||
assertNull(getByName(testEntry, true));
|
||||
assertNull(testEntry, getByName(testEntry, true));
|
||||
}
|
||||
|
||||
for (String testEntry : invalidIpV6Hosts.keySet()) {
|
||||
assertNull(getByName(testEntry, true));
|
||||
assertNull(testEntry, getByName(testEntry, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,20 @@ public class PlatformDependentTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsZero() {
|
||||
byte[] bytes = new byte[100];
|
||||
assertTrue(PlatformDependent.isZero(bytes, 0, 0));
|
||||
assertTrue(PlatformDependent.isZero(bytes, 0, -1));
|
||||
assertTrue(PlatformDependent.isZero(bytes, 0, 100));
|
||||
assertTrue(PlatformDependent.isZero(bytes, 10, 90));
|
||||
bytes[10] = 1;
|
||||
assertTrue(PlatformDependent.isZero(bytes, 0, 10));
|
||||
assertFalse(PlatformDependent.isZero(bytes, 0, 11));
|
||||
assertFalse(PlatformDependent.isZero(bytes, 10, 1));
|
||||
assertTrue(PlatformDependent.isZero(bytes, 11, 89));
|
||||
}
|
||||
|
||||
private interface EqualityChecker {
|
||||
boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length);
|
||||
}
|
||||
@ -105,6 +119,9 @@ public class PlatformDependentTest {
|
||||
bytes2 = bytes1.clone();
|
||||
assertTrue(equalsChecker.equals(bytes1, 0, bytes2, 0, bytes1.length));
|
||||
}
|
||||
|
||||
assertTrue(equalsChecker.equals(bytes1, 0, bytes2, 0, 0));
|
||||
assertTrue(equalsChecker.equals(bytes1, 0, bytes2, 0, -1));
|
||||
}
|
||||
|
||||
private static char randomCharInByteRange() {
|
||||
|
Loading…
Reference in New Issue
Block a user