-
-
Notifications
You must be signed in to change notification settings - Fork 15.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NetUtil IPv6 bugs related to IPv4 and compression #6613
Conversation
* </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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add support for IPv6 addresses with public static byte[] createByteArrayFromIpAddressString(String ipAddressString) {
if (isValidIpV4Address(ipAddressString)) {
....
}
return getIPv6ByName(ipAddressString, true);
}
private static byte[] getIPv6ByName(CharSequence ip, boolean ipv4Mapped) {
...
int startIdx; // instead of '0' below
int endIdx; // instead of 'ipLength - 1' below
if (ip.charAt(0) == '[') {
startIdx = 1;
endIdx = ip.length() - 2;
} else {
startIdx = 0;
endIdx = ip.length() - 1;
}
...
loop: for (int i = startIdx; i <= endIdx; i++) {
...
case '%':
endIdx = i - 1;
break loop;
}
...
}
...
if (startIdx == 1 && (ipv6Seperators == 0 || ip.charAt(ip.length() - 1) != ']')) {
// "[x.x.x.x]" - invalid ipv4
// "[x:x:x::x" - invalid ipv6
return null;
}
...
} This will allow us not to call a @Test
public void testIsValidIpV4Address() {
for (String host : validIpV4Hosts.keySet()) {
assertTrue(host, NetUtil.isValidIpV4Address(host));
assertNotNull(host, NetUtil.getByName(host));
}
for (String host : invalidIpV4Hosts.keySet()) {
assertFalse(host, NetUtil.isValidIpV4Address(host));
assertNull(host, NetUtil.getByName(host));
}
}
@Test
public void testIsValidIpV6Address() {
for (String host : validIpV6Hosts.keySet()) {
assertTrue(host, NetUtil.isValidIpV6Address(host));
assertNotNull(host, NetUtil.getByName(host));
}
for (String host : invalidIpV6Hosts.keySet()) {
assertFalse(host, NetUtil.isValidIpV6Address(host));
assertNull(host, NetUtil.getByName(host));
}
} |
In addition to the previous comment - |
!isValidIPv4MappedSeparators(bytes[currentIndex - 1], bytes[currentIndex - 2])) { | ||
return false; | ||
} | ||
currentIndex -= 2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe -= 2
with an Unsafe
and -= 3
without?
With -2
test failed for ip "::ffff:1.2.3.4" whithout an Unsafe
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Netkins doesn't run tests with -Dio.netty.noUnsafe=true
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch ... should be -=3
... the indexing is off the for unsafe case.
Netkins doesn't run tests with -Dio.netty.noUnsafe=true?
No. It just assumes the default for the platform.
!isValidNumericChar(ip.charAt(i - 3))) || | ||
tmp == 2 && (!isValidNumericChar(ip.charAt(i - 1)) || | ||
!isValidNumericChar(ip.charAt(i - 2))) || | ||
tmp == 1 && !isValidNumericChar(ip.charAt(i - 1)))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all of last part need only for the ipv4Seperators == 1
:
|| (
ipv4Seperators == 1 &&
(
(!ipv4Mapped || currentIndex != 0 && !isValidIPv4Mapped(bytes, currentIndex))
|| (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)))
)
) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. The validation after we see the first ipv4Separators
is done in the default case below.
@Scottmitch fix the typos |
Lets do a followup PR for this enhancement. |
|| (ipv6Seperators > 0 && (currentIndex + compressLength < 12)) | ||
++ipv4Separators; | ||
tmp = i - begin; // tmp is the length of the current segment. | ||
if (tmp > IPV4_MAX_CHAR_BETWEEN_SEPARATOR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. |
+1 |
0:0:0:0:0:ffff::10.0.0.1
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm...
Let me merge this... |
The |
I can fix it in another PR if @Scottmitch have not time.. |
@fenik17 - Thanks for pointing out this unit test .. let me add this unit test and fix the issue |
compressLength = 12; | ||
} else if (ipv6Seperators >= IPV6_MIN_SEPARATORS && | ||
} else if (ipv6Separators >= IPV6_MIN_SEPARATORS && | ||
ip.charAt(ipLength - 1) != ':' && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this check - ip.charAt(ipLength - 1) != ':'
- seems superfluous or wrong for the ipv4Separators > 0
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed ... this should be caught in the loop above.
ff2b72d
to
9f0c151
Compare
One more unit test for invalid address: "1:0:0:0:0:ffff:10.0.0.1". |
Maybe better use |
ip.charAt(ipLength - 1) != ':' && | ||
(!isCompressed && (ipv6Seperators == 6 && ip.charAt(0) != ':') || | ||
isCompressed && (ipv6Seperators + 1 < IPV6_MAX_SEPARATORS && | ||
} else if (ipv6Separators >= IPV6_MIN_SEPARATORS && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered doing this initially ... I will just add a |
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.
9f0c151
to
2751c01
Compare
"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", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Scottmitch oops. Is it really valid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we allow IPv4 compatible
addresses as input. The rational is to be liberal in what we accept but more strict in what we generate (we only generate IPv4 mapped).
// 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm about of count of zeros and "::" after. There can not be more bits in place of "::".
This is confirmed by examples [1] and some online-validators: [2] and [3].
3. An alternative form that is sometimes more convenient when dealing
with a mixed environment of IPv4 and IPv6 nodes is
x:x:x:x:x:x:d.d.d.d, where the 'x's are the hexadecimal values of
the six high-order 16-bit pieces of the address, and the 'd's are
the decimal values of the four low-order 8-bit pieces of the
address (standard IPv4 representation). Examples:
0:0:0:0:0:0:13.1.68.3
0:0:0:0:0:FFFF:129.144.52.38
or in compressed form:
::13.1.68.3
::FFFF:129.144.52.38
[1] https://tools.ietf.org/html/rfc4291#section-2.2
[2] http://formvalidation.io/validators/ip/ ("Try it" in the end)
[3] https://www.helpsystems.com/intermapper/ipv6-test-address-validation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing this out. I agree there is a bug. Let me come up with a PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #6680
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.
Modifications:
Result:
NetUtil#getByName and NetUtil#isValidIpV6Address respect the IPv6 RFC which defines the valid formats for embedding IPv4 addresses.