Skip to content

Commit

Permalink
Add support for scope IDs to InetAddresses.isInetAddress().
Browse files Browse the repository at this point in the history
Fixes #2587

RELNOTES=n/a

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=280418121
  • Loading branch information
kluever authored and cpovirk committed Nov 14, 2019
1 parent d7a0b3d commit 8a7d36a
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 0 deletions.
Expand Up @@ -164,6 +164,58 @@ public void testConvertDottedQuadToHex() throws UnknownHostException {
}
}

// see https://github.com/google/guava/issues/2587
private static final ImmutableSet<String> SCOPE_IDS =
ImmutableSet.of("eno1", "en1", "eth0", "X", "1", "2", "14", "20");

public void testIPv4AddressWithScopeId() {
ImmutableSet<String> ipStrings = ImmutableSet.of("1.2.3.4", "192.168.0.1");
for (String ipString : ipStrings) {
for (String scopeId : SCOPE_IDS) {
String withScopeId = ipString + "%" + scopeId;
assertFalse(
"InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
InetAddresses.isInetAddress(withScopeId));
}
}
}

public void testDottedQuadAddressWithScopeId() {
ImmutableSet<String> ipStrings =
ImmutableSet.of("7::0.128.0.127", "7::0.128.0.128", "7::128.128.0.127", "7::0.128.128.127");
for (String ipString : ipStrings) {
for (String scopeId : SCOPE_IDS) {
String withScopeId = ipString + "%" + scopeId;
assertFalse(
"InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
InetAddresses.isInetAddress(withScopeId));
}
}
}

public void testIPv6AddressWithScopeId() {
ImmutableSet<String> ipStrings =
ImmutableSet.of(
"0:0:0:0:0:0:0:1",
"fe80::a",
"fe80::1",
"fe80::2",
"fe80::42",
"fe80::3dd0:7f8e:57b7:34d5",
"fe80::71a3:2b00:ddd3:753f",
"fe80::8b2:d61e:e5c:b333",
"fe80::b059:65f4:e877:c40");
for (String ipString : ipStrings) {
for (String scopeId : SCOPE_IDS) {
String withScopeId = ipString + "%" + scopeId;
assertTrue(
"InetAddresses.isInetAddress(" + withScopeId + ") should be true but was false",
InetAddresses.isInetAddress(withScopeId));
assertEquals(InetAddresses.forString(withScopeId), InetAddresses.forString(ipString));
}
}
}

public void testToAddrStringIPv4() {
// Don't need to test IPv4 much; it just calls getHostAddress().
assertEquals("1.2.3.4", InetAddresses.toAddrString(InetAddresses.forString("1.2.3.4")));
Expand Down
10 changes: 10 additions & 0 deletions android/guava/src/com/google/common/net/InetAddresses.java
Expand Up @@ -131,6 +131,8 @@ private static Inet4Address getInet4Address(byte[] bytes) {
*
* <p>This deliberately avoids all nameservice lookups (e.g. no DNS).
*
* <p>Anything after a {@code %} in an IPv6 address is ignored (assumed to be a Scope ID).
*
* @param ipString {@code String} containing an IPv4 or IPv6 string literal, e.g. {@code
* "192.168.0.1"} or {@code "2001:db8::1"}
* @return {@link InetAddress} representing the argument
Expand Down Expand Up @@ -158,11 +160,13 @@ public static boolean isInetAddress(String ipString) {
return ipStringToBytes(ipString) != null;
}

/** Returns {@code null} if unable to parse into a {@code byte[]}. */
@NullableDecl
private static byte[] ipStringToBytes(String ipString) {
// Make a first pass to categorize the characters in this string.
boolean hasColon = false;
boolean hasDot = false;
int percentIndex = -1;
for (int i = 0; i < ipString.length(); i++) {
char c = ipString.charAt(i);
if (c == '.') {
Expand All @@ -172,6 +176,9 @@ private static byte[] ipStringToBytes(String ipString) {
return null; // Colons must not appear after dots.
}
hasColon = true;
} else if (c == '%') {
percentIndex = i;
break; // everything after a '%' is ignored (it's a Scope ID): http://superuser.com/a/99753
} else if (Character.digit(c, 16) == -1) {
return null; // Everything else must be a decimal or hex digit.
}
Expand All @@ -185,6 +192,9 @@ private static byte[] ipStringToBytes(String ipString) {
return null;
}
}
if (percentIndex != -1) {
ipString = ipString.substring(0, percentIndex);
}
return textToNumericFormatV6(ipString);
} else if (hasDot) {
return textToNumericFormatV4(ipString);
Expand Down
52 changes: 52 additions & 0 deletions guava-tests/test/com/google/common/net/InetAddressesTest.java
Expand Up @@ -164,6 +164,58 @@ public void testConvertDottedQuadToHex() throws UnknownHostException {
}
}

// see https://github.com/google/guava/issues/2587
private static final ImmutableSet<String> SCOPE_IDS =
ImmutableSet.of("eno1", "en1", "eth0", "X", "1", "2", "14", "20");

public void testIPv4AddressWithScopeId() {
ImmutableSet<String> ipStrings = ImmutableSet.of("1.2.3.4", "192.168.0.1");
for (String ipString : ipStrings) {
for (String scopeId : SCOPE_IDS) {
String withScopeId = ipString + "%" + scopeId;
assertFalse(
"InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
InetAddresses.isInetAddress(withScopeId));
}
}
}

public void testDottedQuadAddressWithScopeId() {
ImmutableSet<String> ipStrings =
ImmutableSet.of("7::0.128.0.127", "7::0.128.0.128", "7::128.128.0.127", "7::0.128.128.127");
for (String ipString : ipStrings) {
for (String scopeId : SCOPE_IDS) {
String withScopeId = ipString + "%" + scopeId;
assertFalse(
"InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
InetAddresses.isInetAddress(withScopeId));
}
}
}

public void testIPv6AddressWithScopeId() {
ImmutableSet<String> ipStrings =
ImmutableSet.of(
"0:0:0:0:0:0:0:1",
"fe80::a",
"fe80::1",
"fe80::2",
"fe80::42",
"fe80::3dd0:7f8e:57b7:34d5",
"fe80::71a3:2b00:ddd3:753f",
"fe80::8b2:d61e:e5c:b333",
"fe80::b059:65f4:e877:c40");
for (String ipString : ipStrings) {
for (String scopeId : SCOPE_IDS) {
String withScopeId = ipString + "%" + scopeId;
assertTrue(
"InetAddresses.isInetAddress(" + withScopeId + ") should be true but was false",
InetAddresses.isInetAddress(withScopeId));
assertEquals(InetAddresses.forString(withScopeId), InetAddresses.forString(ipString));
}
}
}

public void testToAddrStringIPv4() {
// Don't need to test IPv4 much; it just calls getHostAddress().
assertEquals("1.2.3.4", InetAddresses.toAddrString(InetAddresses.forString("1.2.3.4")));
Expand Down
10 changes: 10 additions & 0 deletions guava/src/com/google/common/net/InetAddresses.java
Expand Up @@ -131,6 +131,8 @@ private static Inet4Address getInet4Address(byte[] bytes) {
*
* <p>This deliberately avoids all nameservice lookups (e.g. no DNS).
*
* <p>Anything after a {@code %} in an IPv6 address is ignored (assumed to be a Scope ID).
*
* @param ipString {@code String} containing an IPv4 or IPv6 string literal, e.g. {@code
* "192.168.0.1"} or {@code "2001:db8::1"}
* @return {@link InetAddress} representing the argument
Expand Down Expand Up @@ -158,10 +160,12 @@ public static boolean isInetAddress(String ipString) {
return ipStringToBytes(ipString) != null;
}

/** Returns {@code null} if unable to parse into a {@code byte[]}. */
private static byte @Nullable [] ipStringToBytes(String ipString) {
// Make a first pass to categorize the characters in this string.
boolean hasColon = false;
boolean hasDot = false;
int percentIndex = -1;
for (int i = 0; i < ipString.length(); i++) {
char c = ipString.charAt(i);
if (c == '.') {
Expand All @@ -171,6 +175,9 @@ public static boolean isInetAddress(String ipString) {
return null; // Colons must not appear after dots.
}
hasColon = true;
} else if (c == '%') {
percentIndex = i;
break; // everything after a '%' is ignored (it's a Scope ID): http://superuser.com/a/99753
} else if (Character.digit(c, 16) == -1) {
return null; // Everything else must be a decimal or hex digit.
}
Expand All @@ -184,6 +191,9 @@ public static boolean isInetAddress(String ipString) {
return null;
}
}
if (percentIndex != -1) {
ipString = ipString.substring(0, percentIndex);
}
return textToNumericFormatV6(ipString);
} else if (hasDot) {
return textToNumericFormatV4(ipString);
Expand Down

0 comments on commit 8a7d36a

Please sign in to comment.