Skip to content

Commit

Permalink
Change InetAddress-String conversion methods to preserve the scop…
Browse files Browse the repository at this point in the history
…e ID.

This matches the behavior in https://bugs.openjdk.org/browse/JDK-8272215 (except still not supporting brackets [])

RELNOTES=`net`: Changed `InetAddress`-`String` conversion methods to preserve the scope ID. This may lead to two kinds of problems: First, callers of those methods may be relying on the returned values _not_ to include the scope ID. For example, they might compensate for the old behavior of the methods by appending the scope ID to a returned string themselves. (If so, you can update your code to stop doing so at the same time as you upgrade Guava. Of, if your code might run against multiple versions of Guava, you can check whether Guava included a scope ID before adding one yourself.) Or they may pass the returned string to another system that does not understand scope IDs. (If so, you can strip the scope ID off, whether by truncating the string form at a `%` character (leaving behind any trailing `]` character in the case of `forUriString`) or by replacing the returned `InetAddress` with a new instance constructed by calling `InetAddress.getByAddress(addr)`. The other possible cause for problems is that `java.net.InetAddress` validates any provided scope ID against the interfaces available on the machine. As a result, methods in `InetAddresses` may now fail if the scope ID fails validation, including if the code runs in an Android app without networking permission. If this is not the behavior that you want, then you can strip off the scope ID from the input string before passing it to Guava, as discussed above.
PiperOrigin-RevId: 630457079
  • Loading branch information
java-team-github-bot authored and Google Java Core Libraries committed May 23, 2024
1 parent f238ae4 commit 66b4b9b
Show file tree
Hide file tree
Showing 4 changed files with 420 additions and 88 deletions.
130 changes: 110 additions & 20 deletions android/guava-tests/test/com/google/common/net/InetAddressesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import junit.framework.TestCase;

/**
Expand Down Expand Up @@ -191,14 +194,10 @@ 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() {
public void testIPv4AddressWithScopeId() throws SocketException {
ImmutableSet<String> ipStrings = ImmutableSet.of("1.2.3.4", "192.168.0.1");
for (String ipString : ipStrings) {
for (String scopeId : SCOPE_IDS) {
for (String scopeId : getMachineScopesAndInterfaces()) {
String withScopeId = ipString + "%" + scopeId;
assertFalse(
"InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
Expand All @@ -207,11 +206,11 @@ public void testIPv4AddressWithScopeId() {
}
}

public void testDottedQuadAddressWithScopeId() {
public void testDottedQuadAddressWithScopeId() throws SocketException {
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) {
for (String scopeId : getMachineScopesAndInterfaces()) {
String withScopeId = ipString + "%" + scopeId;
assertFalse(
"InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
Expand All @@ -220,29 +219,108 @@ public void testDottedQuadAddressWithScopeId() {
}
}

public void testIPv6AddressWithScopeId() {
public void testIPv6AddressWithScopeId() throws SocketException, UnknownHostException {
ImmutableSet<String> ipStrings =
ImmutableSet.of(
"::1",
"1180::a",
"1180::1",
"1180::2",
"1180::42",
"1180::3dd0:7f8e:57b7:34d5",
"1180::71a3:2b00:ddd3:753f",
"1180::8b2:d61e:e5c:b333",
"1180::b059:65f4:e877:c40",
"fe80::34",
"fec0::34");
boolean processedNamedInterface = false;
for (String ipString : ipStrings) {
for (String scopeId : getMachineScopesAndInterfaces()) {
String withScopeId = ipString + "%" + scopeId;
assertTrue(
"InetAddresses.isInetAddress(" + withScopeId + ") should be true but was false",
InetAddresses.isInetAddress(withScopeId));
Inet6Address parsed;
boolean isNumeric = scopeId.matches("\\d+");
try {
parsed = (Inet6Address) InetAddresses.forString(withScopeId);
} catch (IllegalArgumentException e) {
if (!isNumeric) {
// Android doesn't recognize %interface as valid
continue;
}
throw e;
}
processedNamedInterface |= !isNumeric;
assertThat(InetAddresses.toAddrString(parsed)).contains("%");
if (isNumeric) {
assertEquals(Integer.parseInt(scopeId), parsed.getScopeId());
} else {
assertEquals(scopeId, parsed.getScopedInterface().getName());
}
Inet6Address reparsed =
(Inet6Address) InetAddresses.forString(InetAddresses.toAddrString(parsed));
assertEquals(reparsed, parsed);
assertEquals(reparsed.getScopeId(), parsed.getScopeId());
}
}
assertTrue(processedNamedInterface);
}

public void testIPv6AddressWithScopeId_platformEquivalence()
throws SocketException, UnknownHostException {
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");
"::1",
"1180::a",
"1180::1",
"1180::2",
"1180::42",
"1180::3dd0:7f8e:57b7:34d5",
"1180::71a3:2b00:ddd3:753f",
"1180::8b2:d61e:e5c:b333",
"1180::b059:65f4:e877:c40",
"fe80::34",
"fec0::34");
for (String ipString : ipStrings) {
for (String scopeId : SCOPE_IDS) {
for (String scopeId : getMachineScopesAndInterfaces()) {
String withScopeId = ipString + "%" + scopeId;
assertTrue(
"InetAddresses.isInetAddress(" + withScopeId + ") should be true but was false",
InetAddresses.isInetAddress(withScopeId));
assertEquals(InetAddresses.forString(withScopeId), InetAddresses.forString(ipString));
Inet6Address parsed;
boolean isNumeric = scopeId.matches("\\d+");
try {
parsed = (Inet6Address) InetAddresses.forString(withScopeId);
} catch (IllegalArgumentException e) {
if (!isNumeric) {
// Android doesn't recognize %interface as valid
continue;
}
throw e;
}
Inet6Address platformValue;
try {
platformValue = (Inet6Address) InetAddress.getByName(withScopeId);
} catch (UnknownHostException e) {
// Android doesn't recognize %interface as valid
if (!isNumeric) {
continue;
}
throw e;
}
assertEquals(platformValue, parsed);
assertEquals(platformValue.getScopeId(), parsed.getScopeId());
}
}
}

public void testIPv6AddressWithBadScopeId() throws SocketException, UnknownHostException {
assertThrows(
IllegalArgumentException.class,
() -> InetAddresses.forString("1180::b059:65f4:e877:c40%eth9"));
}

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 Expand Up @@ -792,6 +870,18 @@ public void testFromIpv6BigIntegerInputTooLarge() {
expected.getMessage());
}

// see https://github.com/google/guava/issues/2587
private static ImmutableSet<String> getMachineScopesAndInterfaces() throws SocketException {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
assertTrue(interfaces.hasMoreElements());
while (interfaces.hasMoreElements()) {
NetworkInterface i = interfaces.nextElement();
builder.add(i.getName()).add(String.valueOf(i.getIndex()));
}
return builder.build();
}

/** Checks that the IP converts to the big integer and the big integer converts to the IP. */
private static void checkBigIntegerConversion(String ip, BigInteger bigIntegerIp) {
InetAddress address = InetAddresses.forString(ip);
Expand Down

0 comments on commit 66b4b9b

Please sign in to comment.