Skip to content

Commit 8a7d36a

Browse files
kluevercpovirk
authored andcommitted
Add support for scope IDs to InetAddresses.isInetAddress().
Fixes #2587 RELNOTES=n/a ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=280418121
1 parent d7a0b3d commit 8a7d36a

File tree

4 files changed

+124
-0
lines changed

4 files changed

+124
-0
lines changed

android/guava-tests/test/com/google/common/net/InetAddressesTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,58 @@ public void testConvertDottedQuadToHex() throws UnknownHostException {
164164
}
165165
}
166166

167+
// see https://github.com/google/guava/issues/2587
168+
private static final ImmutableSet<String> SCOPE_IDS =
169+
ImmutableSet.of("eno1", "en1", "eth0", "X", "1", "2", "14", "20");
170+
171+
public void testIPv4AddressWithScopeId() {
172+
ImmutableSet<String> ipStrings = ImmutableSet.of("1.2.3.4", "192.168.0.1");
173+
for (String ipString : ipStrings) {
174+
for (String scopeId : SCOPE_IDS) {
175+
String withScopeId = ipString + "%" + scopeId;
176+
assertFalse(
177+
"InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
178+
InetAddresses.isInetAddress(withScopeId));
179+
}
180+
}
181+
}
182+
183+
public void testDottedQuadAddressWithScopeId() {
184+
ImmutableSet<String> ipStrings =
185+
ImmutableSet.of("7::0.128.0.127", "7::0.128.0.128", "7::128.128.0.127", "7::0.128.128.127");
186+
for (String ipString : ipStrings) {
187+
for (String scopeId : SCOPE_IDS) {
188+
String withScopeId = ipString + "%" + scopeId;
189+
assertFalse(
190+
"InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
191+
InetAddresses.isInetAddress(withScopeId));
192+
}
193+
}
194+
}
195+
196+
public void testIPv6AddressWithScopeId() {
197+
ImmutableSet<String> ipStrings =
198+
ImmutableSet.of(
199+
"0:0:0:0:0:0:0:1",
200+
"fe80::a",
201+
"fe80::1",
202+
"fe80::2",
203+
"fe80::42",
204+
"fe80::3dd0:7f8e:57b7:34d5",
205+
"fe80::71a3:2b00:ddd3:753f",
206+
"fe80::8b2:d61e:e5c:b333",
207+
"fe80::b059:65f4:e877:c40");
208+
for (String ipString : ipStrings) {
209+
for (String scopeId : SCOPE_IDS) {
210+
String withScopeId = ipString + "%" + scopeId;
211+
assertTrue(
212+
"InetAddresses.isInetAddress(" + withScopeId + ") should be true but was false",
213+
InetAddresses.isInetAddress(withScopeId));
214+
assertEquals(InetAddresses.forString(withScopeId), InetAddresses.forString(ipString));
215+
}
216+
}
217+
}
218+
167219
public void testToAddrStringIPv4() {
168220
// Don't need to test IPv4 much; it just calls getHostAddress().
169221
assertEquals("1.2.3.4", InetAddresses.toAddrString(InetAddresses.forString("1.2.3.4")));

android/guava/src/com/google/common/net/InetAddresses.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ private static Inet4Address getInet4Address(byte[] bytes) {
131131
*
132132
* <p>This deliberately avoids all nameservice lookups (e.g. no DNS).
133133
*
134+
* <p>Anything after a {@code %} in an IPv6 address is ignored (assumed to be a Scope ID).
135+
*
134136
* @param ipString {@code String} containing an IPv4 or IPv6 string literal, e.g. {@code
135137
* "192.168.0.1"} or {@code "2001:db8::1"}
136138
* @return {@link InetAddress} representing the argument
@@ -158,11 +160,13 @@ public static boolean isInetAddress(String ipString) {
158160
return ipStringToBytes(ipString) != null;
159161
}
160162

163+
/** Returns {@code null} if unable to parse into a {@code byte[]}. */
161164
@NullableDecl
162165
private static byte[] ipStringToBytes(String ipString) {
163166
// Make a first pass to categorize the characters in this string.
164167
boolean hasColon = false;
165168
boolean hasDot = false;
169+
int percentIndex = -1;
166170
for (int i = 0; i < ipString.length(); i++) {
167171
char c = ipString.charAt(i);
168172
if (c == '.') {
@@ -172,6 +176,9 @@ private static byte[] ipStringToBytes(String ipString) {
172176
return null; // Colons must not appear after dots.
173177
}
174178
hasColon = true;
179+
} else if (c == '%') {
180+
percentIndex = i;
181+
break; // everything after a '%' is ignored (it's a Scope ID): http://superuser.com/a/99753
175182
} else if (Character.digit(c, 16) == -1) {
176183
return null; // Everything else must be a decimal or hex digit.
177184
}
@@ -185,6 +192,9 @@ private static byte[] ipStringToBytes(String ipString) {
185192
return null;
186193
}
187194
}
195+
if (percentIndex != -1) {
196+
ipString = ipString.substring(0, percentIndex);
197+
}
188198
return textToNumericFormatV6(ipString);
189199
} else if (hasDot) {
190200
return textToNumericFormatV4(ipString);

guava-tests/test/com/google/common/net/InetAddressesTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,58 @@ public void testConvertDottedQuadToHex() throws UnknownHostException {
164164
}
165165
}
166166

167+
// see https://github.com/google/guava/issues/2587
168+
private static final ImmutableSet<String> SCOPE_IDS =
169+
ImmutableSet.of("eno1", "en1", "eth0", "X", "1", "2", "14", "20");
170+
171+
public void testIPv4AddressWithScopeId() {
172+
ImmutableSet<String> ipStrings = ImmutableSet.of("1.2.3.4", "192.168.0.1");
173+
for (String ipString : ipStrings) {
174+
for (String scopeId : SCOPE_IDS) {
175+
String withScopeId = ipString + "%" + scopeId;
176+
assertFalse(
177+
"InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
178+
InetAddresses.isInetAddress(withScopeId));
179+
}
180+
}
181+
}
182+
183+
public void testDottedQuadAddressWithScopeId() {
184+
ImmutableSet<String> ipStrings =
185+
ImmutableSet.of("7::0.128.0.127", "7::0.128.0.128", "7::128.128.0.127", "7::0.128.128.127");
186+
for (String ipString : ipStrings) {
187+
for (String scopeId : SCOPE_IDS) {
188+
String withScopeId = ipString + "%" + scopeId;
189+
assertFalse(
190+
"InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
191+
InetAddresses.isInetAddress(withScopeId));
192+
}
193+
}
194+
}
195+
196+
public void testIPv6AddressWithScopeId() {
197+
ImmutableSet<String> ipStrings =
198+
ImmutableSet.of(
199+
"0:0:0:0:0:0:0:1",
200+
"fe80::a",
201+
"fe80::1",
202+
"fe80::2",
203+
"fe80::42",
204+
"fe80::3dd0:7f8e:57b7:34d5",
205+
"fe80::71a3:2b00:ddd3:753f",
206+
"fe80::8b2:d61e:e5c:b333",
207+
"fe80::b059:65f4:e877:c40");
208+
for (String ipString : ipStrings) {
209+
for (String scopeId : SCOPE_IDS) {
210+
String withScopeId = ipString + "%" + scopeId;
211+
assertTrue(
212+
"InetAddresses.isInetAddress(" + withScopeId + ") should be true but was false",
213+
InetAddresses.isInetAddress(withScopeId));
214+
assertEquals(InetAddresses.forString(withScopeId), InetAddresses.forString(ipString));
215+
}
216+
}
217+
}
218+
167219
public void testToAddrStringIPv4() {
168220
// Don't need to test IPv4 much; it just calls getHostAddress().
169221
assertEquals("1.2.3.4", InetAddresses.toAddrString(InetAddresses.forString("1.2.3.4")));

guava/src/com/google/common/net/InetAddresses.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ private static Inet4Address getInet4Address(byte[] bytes) {
131131
*
132132
* <p>This deliberately avoids all nameservice lookups (e.g. no DNS).
133133
*
134+
* <p>Anything after a {@code %} in an IPv6 address is ignored (assumed to be a Scope ID).
135+
*
134136
* @param ipString {@code String} containing an IPv4 or IPv6 string literal, e.g. {@code
135137
* "192.168.0.1"} or {@code "2001:db8::1"}
136138
* @return {@link InetAddress} representing the argument
@@ -158,10 +160,12 @@ public static boolean isInetAddress(String ipString) {
158160
return ipStringToBytes(ipString) != null;
159161
}
160162

163+
/** Returns {@code null} if unable to parse into a {@code byte[]}. */
161164
private static byte @Nullable [] ipStringToBytes(String ipString) {
162165
// Make a first pass to categorize the characters in this string.
163166
boolean hasColon = false;
164167
boolean hasDot = false;
168+
int percentIndex = -1;
165169
for (int i = 0; i < ipString.length(); i++) {
166170
char c = ipString.charAt(i);
167171
if (c == '.') {
@@ -171,6 +175,9 @@ public static boolean isInetAddress(String ipString) {
171175
return null; // Colons must not appear after dots.
172176
}
173177
hasColon = true;
178+
} else if (c == '%') {
179+
percentIndex = i;
180+
break; // everything after a '%' is ignored (it's a Scope ID): http://superuser.com/a/99753
174181
} else if (Character.digit(c, 16) == -1) {
175182
return null; // Everything else must be a decimal or hex digit.
176183
}
@@ -184,6 +191,9 @@ public static boolean isInetAddress(String ipString) {
184191
return null;
185192
}
186193
}
194+
if (percentIndex != -1) {
195+
ipString = ipString.substring(0, percentIndex);
196+
}
187197
return textToNumericFormatV6(ipString);
188198
} else if (hasDot) {
189199
return textToNumericFormatV4(ipString);

0 commit comments

Comments
 (0)