IPv6 validation fixes
Motivation: `NetUtil`'s methods `isValidIpV6Address` and `getIPv6ByName` incorrectly validate some IPv6 addresses. Modifications: - `getIPv6ByName`: add checks for single colon at the start or end. - `isValidIpV6Address`: fix checks for the count of colons and use `endOffset` instead of `ipAddress.length()` for the cases with the brackets or '%'. Result: More correct implementation of `NetUtil#isValidIpV6Address` and `NetUtil#getIPv6ByName`.
This commit is contained in:
parent
9e62c79574
commit
5643cc6a10
|
@ -490,7 +490,6 @@ public final class NetUtil {
|
|||
}
|
||||
|
||||
public static boolean isValidIpV6Address(String ipAddress) {
|
||||
int length = ipAddress.length();
|
||||
boolean doubleColon = false;
|
||||
int numberOfColons = 0;
|
||||
int numberOfPeriods = 0;
|
||||
|
@ -555,7 +554,7 @@ public final class NetUtil {
|
|||
|
||||
// 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 != 6 && !doubleColon) ||
|
||||
if ((numberOfColons != 6 && !doubleColon) || numberOfColons > 7 ||
|
||||
(numberOfColons == 7 && (ipAddress.charAt(startOffset) != ':' ||
|
||||
ipAddress.charAt(1 + startOffset) != ':'))) {
|
||||
return false;
|
||||
|
@ -579,12 +578,12 @@ public final class NetUtil {
|
|||
// FIX "IP6 mechanism syntax #ip6-bad1"
|
||||
// An IPV6 address cannot start with a single ":".
|
||||
// Either it can starti with "::" or with a number.
|
||||
if (i == startOffset && (ipAddress.length() <= i || ipAddress.charAt(i + 1) != ':')) {
|
||||
if (i == startOffset && (endOffset <= i || ipAddress.charAt(i + 1) != ':')) {
|
||||
return false;
|
||||
}
|
||||
// END FIX "IP6 mechanism syntax #ip6-bad1"
|
||||
numberOfColons ++;
|
||||
if (numberOfColons > 7) {
|
||||
if (numberOfColons > 8) {
|
||||
return false;
|
||||
}
|
||||
if (numberOfPeriods > 0) {
|
||||
|
@ -623,11 +622,15 @@ public final class NetUtil {
|
|||
return false;
|
||||
}
|
||||
|
||||
// If we have an empty word at the end, it means we ended in either
|
||||
// a : or a .
|
||||
// If we did not end in :: then this is invalid
|
||||
if (word.length() == 0 && ipAddress.charAt(length - 1 - startOffset) == ':' &&
|
||||
ipAddress.charAt(length - 2 - startOffset) != ':') {
|
||||
if (word.length() == 0) {
|
||||
// If we have an empty word at the end, it means we ended in either
|
||||
// a : or a .
|
||||
// If we did not end in :: then this is invalid
|
||||
if (ipAddress.charAt(endOffset - 1) == ':' &&
|
||||
ipAddress.charAt(endOffset - 2) != ':') {
|
||||
return false;
|
||||
}
|
||||
} else if (numberOfColons == 8 && ipAddress.charAt(startOffset) != ':') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -913,7 +916,9 @@ public final class NetUtil {
|
|||
(ipv6Separators == IPV6_MAX_SEPARATORS &&
|
||||
(compressBegin <= 2 && ip.charAt(0) != ':' ||
|
||||
compressBegin >= 14 && ip.charAt(tmp) != ':'))) ||
|
||||
currentIndex + 1 >= bytes.length) {
|
||||
currentIndex + 1 >= bytes.length ||
|
||||
begin < 0 && ip.charAt(tmp - 1) != ':' ||
|
||||
compressBegin > 2 && ip.charAt(0) == ':') {
|
||||
return null;
|
||||
}
|
||||
if (begin >= 0 && i - begin <= IPV6_MAX_CHAR_BETWEEN_SEPARATOR) {
|
||||
|
|
|
@ -25,11 +25,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static io.netty.util.NetUtil.bytesToIpAddress;
|
||||
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 io.netty.util.NetUtil.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
@ -94,6 +90,7 @@ public class NetUtilTest {
|
|||
"0:1:2:3::f", "0000000100020003000000000000000f",
|
||||
"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::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:10.0.0.1", "00000000000000000000ffff0a000001",
|
||||
|
@ -105,9 +102,31 @@ public class NetUtilTest {
|
|||
"[::1%1]", "00000000000000000000000000000001",
|
||||
"[::1%eth0]", "00000000000000000000000000000001",
|
||||
"[::1%%]", "00000000000000000000000000000001",
|
||||
"0:0:0:0:0:ffff:10.0.0.1%", "00000000000000000000ffff0a000001",
|
||||
"0:0:0:0:0:ffff:10.0.0.1%1", "00000000000000000000ffff0a000001",
|
||||
"[0:0:0:0:0:ffff:10.0.0.1%1]", "00000000000000000000ffff0a000001",
|
||||
"[0:0:0:0:0::10.0.0.1%1]", "00000000000000000000ffff0a000001",
|
||||
"[::0:0:0:0:ffff:10.0.0.1%1]", "00000000000000000000ffff0a000001",
|
||||
"::0:0:0:0:ffff:10.0.0.1%1", "00000000000000000000ffff0a000001",
|
||||
"::1%1", "00000000000000000000000000000001",
|
||||
"::1%eth0", "00000000000000000000000000000001",
|
||||
"::1%%", "00000000000000000000000000000001");
|
||||
"::1%%", "00000000000000000000000000000001",
|
||||
// Tests with leading or trailing compression
|
||||
"0:0:0:0:0:0:0::", "00000000000000000000000000000000",
|
||||
"0:0:0:0:0:0::", "00000000000000000000000000000000",
|
||||
"0:0:0:0:0::", "00000000000000000000000000000000",
|
||||
"0:0:0:0::", "00000000000000000000000000000000",
|
||||
"0:0:0::", "00000000000000000000000000000000",
|
||||
"0:0::", "00000000000000000000000000000000",
|
||||
"0::", "00000000000000000000000000000000",
|
||||
"::", "00000000000000000000000000000000",
|
||||
"::0", "00000000000000000000000000000000",
|
||||
"::0:0", "00000000000000000000000000000000",
|
||||
"::0:0:0", "00000000000000000000000000000000",
|
||||
"::0:0:0:0", "00000000000000000000000000000000",
|
||||
"::0:0:0:0:0", "00000000000000000000000000000000",
|
||||
"::0:0:0:0:0:0", "00000000000000000000000000000000",
|
||||
"::0:0:0:0:0:0:0", "00000000000000000000000000000000");
|
||||
|
||||
private static final Map<String, String> invalidIpV6Hosts = new TestMap(
|
||||
// Test method with garbage.
|
||||
|
@ -128,6 +147,58 @@ public class NetUtilTest {
|
|||
"1:2:3:4:5:6:7:", null,
|
||||
// Too many : separators leading
|
||||
":1:2:3:4:5:6:7", null,
|
||||
// Compression with : separators trailing
|
||||
"0:1:2:3:4:5::7:", null,
|
||||
"0:1:2:3:4::7:", null,
|
||||
"0:1:2:3::7:", null,
|
||||
"0:1:2::7:", null,
|
||||
"0:1::7:", null,
|
||||
"0::7:", null,
|
||||
// Compression at start with : separators trailing
|
||||
"::0:1:2:3:4:5:7:", null,
|
||||
"::0:1:2:3:4:7:", null,
|
||||
"::0:1:2:3:7:", null,
|
||||
"::0:1:2:7:", null,
|
||||
"::0:1:7:", null,
|
||||
"::7:", null,
|
||||
// The : separators leading and trailing
|
||||
":1:2:3:4:5:6:7:", null,
|
||||
":1:2:3:4:5:6:", null,
|
||||
":1:2:3:4:5:", null,
|
||||
":1:2:3:4:", null,
|
||||
":1:2:3:", null,
|
||||
":1:2:", null,
|
||||
":1:", null,
|
||||
// Compression with : separators leading
|
||||
":1::2:3:4:5:6:7", null,
|
||||
":1::3:4:5:6:7", null,
|
||||
":1::4:5:6:7", null,
|
||||
":1::5:6:7", null,
|
||||
":1::6:7", null,
|
||||
":1::7", null,
|
||||
":1:2:3:4:5:6::7", null,
|
||||
":1:3:4:5:6::7", null,
|
||||
":1:4:5:6::7", null,
|
||||
":1:5:6::7", null,
|
||||
":1:6::7", null,
|
||||
":1::", null,
|
||||
// Compression trailing with : separators leading
|
||||
":1:2:3:4:5:6:7::", null,
|
||||
":1:3:4:5:6:7::", null,
|
||||
":1:4:5:6:7::", null,
|
||||
":1:5:6:7::", null,
|
||||
":1:6:7::", null,
|
||||
":1:7::", null,
|
||||
// Double compression
|
||||
"1::2:3:4:5:6::", null,
|
||||
"::1:2:3:4:5::6", null,
|
||||
"::1:2:3:4:5:6::", null,
|
||||
"::1:2:3:4:5::", null,
|
||||
"::1:2:3:4::", null,
|
||||
"::1:2:3::", null,
|
||||
"::1:2::", null,
|
||||
"::0::", null,
|
||||
"12::0::12", null,
|
||||
// Too many : separators leading 0
|
||||
"0::1:2:3:4:5:6:7", null,
|
||||
// Test method with preferred style, too many digits.
|
||||
|
@ -138,8 +209,6 @@ public class NetUtilTest {
|
|||
"0:1:2:::3", null,
|
||||
// Test method with compressed style, too many digits.
|
||||
"0:1:2:3::abcde", null,
|
||||
// Test method with preferred style, too many :
|
||||
"0:1:2:3:4:5:6:7:8", null,
|
||||
// Test method with compressed style, not enough :
|
||||
"0:1", null,
|
||||
// Test method with ipv4 style, bad ipv6 digits.
|
||||
|
@ -184,8 +253,6 @@ public class NetUtilTest {
|
|||
"::ffff:192.168.0", null,
|
||||
// Test method with compressed ipv4 style, adjacent .
|
||||
"::ffff:192.168..0.1", null,
|
||||
// Test method, garbage.
|
||||
"absolute, and utter garbage", null,
|
||||
// Test method, bad ipv6 digits.
|
||||
"x:0:0:0:0:0:10.0.0.1", null,
|
||||
// Test method, bad ipv4 digits.
|
||||
|
@ -212,12 +279,11 @@ public class NetUtilTest {
|
|||
"0:0:0:0:0:0:10.0.1", null,
|
||||
// Test method, adjacent .
|
||||
"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,
|
||||
// Invalid single compression
|
||||
":", null,
|
||||
":::", null,
|
||||
// Trailing : (max number of : = 8)
|
||||
"2001:0:4136:e378:8000:63bf:3fff:fdd2:", null,
|
||||
// Leading : (max number of : = 8)
|
||||
|
@ -268,6 +334,8 @@ public class NetUtilTest {
|
|||
"0:0:0:0:0:00000: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 - too few bytes (not enough 0's)
|
||||
"ffff:192.168.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
|
||||
|
@ -289,7 +357,11 @@ public class NetUtilTest {
|
|||
// Too many digits
|
||||
"0:0:0:0:0:0:ffff:10.0.0.1", null,
|
||||
// Invalid IPv4 format
|
||||
":1.2.3.4", null);
|
||||
":1.2.3.4", null,
|
||||
// Invalid IPv4 format
|
||||
"::.2.3.4", null,
|
||||
// Invalid IPv4 format
|
||||
"::ffff:0.1.2.", null);
|
||||
|
||||
private static final Map<byte[], String> ipv6ToAddressStrings = new HashMap<byte[], String>() {
|
||||
private static final long serialVersionUID = 2999763170377573184L;
|
||||
|
@ -511,47 +583,103 @@ public class NetUtilTest {
|
|||
|
||||
@Test
|
||||
public void testLocalhost() {
|
||||
assertNotNull(NetUtil.LOCALHOST);
|
||||
assertNotNull(LOCALHOST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoopback() {
|
||||
assertNotNull(NetUtil.LOOPBACK_IF);
|
||||
assertNotNull(LOOPBACK_IF);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidIpV4Address() {
|
||||
for (String host : validIpV4Hosts.keySet()) {
|
||||
assertTrue(NetUtil.isValidIpV4Address(host));
|
||||
assertTrue(host, isValidIpV4Address(host));
|
||||
}
|
||||
for (String host : invalidIpV4Hosts.keySet()) {
|
||||
assertFalse(NetUtil.isValidIpV4Address(host));
|
||||
assertFalse(host, isValidIpV4Address(host));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidIpV6Address() {
|
||||
for (String host : validIpV6Hosts.keySet()) {
|
||||
assertTrue(host, NetUtil.isValidIpV6Address(host));
|
||||
assertTrue(host, isValidIpV6Address(host));
|
||||
if (host.charAt(0) != '[' && !host.contains("%")) {
|
||||
assertNotNull(host, getByName(host, true));
|
||||
|
||||
String hostMod = '[' + host + ']';
|
||||
assertTrue(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = host + '%';
|
||||
assertTrue(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = host + "%eth1";
|
||||
assertTrue(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = '[' + host + "%]";
|
||||
assertTrue(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = '[' + host + "%1]";
|
||||
assertTrue(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = '[' + host + "]%";
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = '[' + host + "]%1";
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
}
|
||||
}
|
||||
for (String host : invalidIpV6Hosts.keySet()) {
|
||||
assertFalse(host, NetUtil.isValidIpV6Address(host));
|
||||
assertFalse(host, isValidIpV6Address(host));
|
||||
assertNull(host, getByName(host));
|
||||
|
||||
String hostMod = '[' + host + ']';
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = host + '%';
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = host + "%eth1";
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = '[' + host + "%]";
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = '[' + host + "%1]";
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = '[' + host + "]%";
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = '[' + host + "]%1";
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = host + ']';
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
|
||||
hostMod = '[' + host;
|
||||
assertFalse(hostMod, isValidIpV6Address(hostMod));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateByteArrayFromIpAddressString() {
|
||||
for (Entry<String, String> e : validIpV4Hosts.entrySet()) {
|
||||
assertHexDumpEquals(e.getValue(), createByteArrayFromIpAddressString(e.getKey()));
|
||||
String ip = e.getKey();
|
||||
assertHexDumpEquals(e.getValue(), createByteArrayFromIpAddressString(ip), ip);
|
||||
}
|
||||
for (Entry<String, String> e : invalidIpV4Hosts.entrySet()) {
|
||||
assertHexDumpEquals(e.getValue(), createByteArrayFromIpAddressString(e.getKey()));
|
||||
String ip = e.getKey();
|
||||
assertHexDumpEquals(e.getValue(), createByteArrayFromIpAddressString(ip), ip);
|
||||
}
|
||||
for (Entry<String, String> e : validIpV6Hosts.entrySet()) {
|
||||
assertHexDumpEquals(e.getValue(), createByteArrayFromIpAddressString(e.getKey()));
|
||||
String ip = e.getKey();
|
||||
assertHexDumpEquals(e.getValue(), createByteArrayFromIpAddressString(ip), ip);
|
||||
}
|
||||
for (Entry<String, String> e : invalidIpV6Hosts.entrySet()) {
|
||||
assertHexDumpEquals(e.getValue(), createByteArrayFromIpAddressString(e.getKey()));
|
||||
String ip = e.getKey();
|
||||
assertHexDumpEquals(e.getValue(), createByteArrayFromIpAddressString(ip), ip);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -582,20 +710,22 @@ public class NetUtilTest {
|
|||
@Test
|
||||
public void testIpv4MappedIp6GetByName() {
|
||||
for (Entry<String, String> testEntry : ipv4MappedToIPv6AddressStrings.entrySet()) {
|
||||
Inet6Address inet6Address = getByName(testEntry.getKey(), true);
|
||||
assertNotNull(testEntry.getKey() + ", " + testEntry.getValue(), inet6Address);
|
||||
assertEquals(testEntry.getKey(), testEntry.getValue(), toAddressString(inet6Address, true));
|
||||
String srcIp = testEntry.getKey();
|
||||
String dstIp = testEntry.getValue();
|
||||
Inet6Address inet6Address = getByName(srcIp, true);
|
||||
assertNotNull(srcIp + ", " + dstIp, inet6Address);
|
||||
assertEquals(srcIp, dstIp, toAddressString(inet6Address, true));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testinvalidIpv4MappedIp6GetByName() {
|
||||
for (String testEntry : invalidIpV4Hosts.keySet()) {
|
||||
assertNull(testEntry, getByName(testEntry, true));
|
||||
public void testInvalidIpv4MappedIp6GetByName() {
|
||||
for (String host : invalidIpV4Hosts.keySet()) {
|
||||
assertNull(host, getByName(host, true));
|
||||
}
|
||||
|
||||
for (String testEntry : invalidIpV6Hosts.keySet()) {
|
||||
assertNull(testEntry, getByName(testEntry, true));
|
||||
for (String host : invalidIpV6Hosts.keySet()) {
|
||||
assertNull(host, getByName(host, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,8 +745,8 @@ public class NetUtilTest {
|
|||
}
|
||||
}
|
||||
|
||||
private static void assertHexDumpEquals(String expected, byte[] actual) {
|
||||
assertEquals(expected, hex(actual));
|
||||
private static void assertHexDumpEquals(String expected, byte[] actual, String message) {
|
||||
assertEquals(message, expected, hex(actual));
|
||||
}
|
||||
|
||||
private static String hex(byte[] value) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user