blob: 6bbfe0187a25e05429f36e27466bce13f06e3d53 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package libcore.java.net;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import libcore.libcore.util.SerializationTester;
import libcore.net.InetAddressUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(JUnitParamsRunner.class)
public class InetAddressTest {
private static final byte[] LOOPBACK4_BYTES = new byte[] { 127, 0, 0, 1 };
private static final byte[] LOOPBACK6_BYTES = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
private static final String[] INVALID_IPv4_AND_6_NUMERIC_ADDRESSES = new String[] {
// IPv4 addresses may not be surrounded by square brackets.
"[127.0.0.1]",
// Trailing dots are not allowed.
"1.2.3.4.",
// Nor is any kind of trailing junk.
"1.2.3.4hello",
// Out of range.
"256.2.3.4",
"1.256.3.4",
"1.2.256.4",
"1.2.3.256",
// Deprecated.
"1.2.3",
"1.2",
"1",
"1234",
"0", // Single out the deprecated form of the ANY address.
// Older Harmony tests expected this to be resolved to 255.255.255.255.
"4294967295", // 0xffffffffL,
// Hex. Not supported by Android but supported by the RI.
"0x1.0x2.0x3.0x4",
"0x7f.0x00.0x00.0x01",
"7f.0.0.1",
// Octal. Not supported by Android but supported by the RI. In the RI, if any of the numbers
// cannot be treated as a decimal the entire IP is interpreted differently, leading to
// "0177.00.00.01" -> 177.0.0.1, but "0177.0x0.00.01" -> 127.0.0.1.
// Android does not do this.
"0256.00.00.01", // Historically, this could have been interpreted as 174.0.0.1.
// Negative numbers.
"-1.0.0.1",
"1.-1.0.1",
"1.0.-1.1",
"1.0.0.-1",
// Invalid IPv6 addresses
"FFFF:FFFF",
};
private static final String VALID_IPv6_ADDRESSES[] = {
"::1.2.3.4",
"::",
"::",
"1::0",
"1::",
"::1",
"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255",
"0:0:0:0:0:0:0:0",
"0:0:0:0:0:0:0.0.0.0",
"::255.255.255.255",
"::FFFF:0.0.0.0",
"F:F:F:F:F:F:F:F",
};
private static Inet6Address loopback6() throws Exception {
return (Inet6Address) InetAddress.getByAddress(LOOPBACK6_BYTES);
}
private static Inet6Address localhost6() throws Exception {
return (Inet6Address) InetAddress.getByAddress("ip6-localhost", LOOPBACK6_BYTES);
}
public static String[][] validNumericAddressesAndStringRepresentation() {
return new String[][]{
// Regular IPv4.
{ "1.2.3.4", "/1.2.3.4" },
// Regular IPv6.
{ "2001:4860:800d::68", "/2001:4860:800d::68" },
// Mapped IPv4
{ "::ffff:127.0.0.1", "/127.0.0.1" },
// Optional square brackets around IPv6 addresses, including mapped IPv4.
{ "[2001:4860:800d::68]", "/2001:4860:800d::68" },
{ "[::ffff:127.0.0.1]", "/127.0.0.1" },
// Android does not recognize Octal (leading 0) cases: they are treated as decimal.
{ "0177.00.00.01", "/177.0.0.1" },
};
}
@Parameters(method = "validNumericAddressesAndStringRepresentation")
@Test
public void test_parseNumericAddress(String address, String expectedString) throws Exception {
assertEquals(expectedString, InetAddress.parseNumericAddress(address).toString());
}
@Test
public void test_parseNumericAddress_notNumeric() throws Exception {
try {
InetAddress.parseNumericAddress("example.com"); // Not numeric.
fail();
} catch (IllegalArgumentException expected) {
}
// Strange special cases, for compatibility with InetAddress.getByName.
assertTrue(InetAddress.parseNumericAddress(null).isLoopbackAddress());
assertTrue(InetAddress.parseNumericAddress("").isLoopbackAddress());
}
@Parameters(method = "invalidNumericAddresses")
@Test
public void test_parseNumericAddress_invalid(String invalid) throws Exception {
try {
InetAddress.parseNumericAddress(invalid);
fail(invalid);
} catch (IllegalArgumentException expected) {
}
}
public static String[] validNumericAddresses() {
return new String[] {
// IPv4
"1.2.3.4",
"127.0.0.1",
// IPv6
"::1",
"2001:4860:800d::68",
// Mapped IPv4
"::ffff:127.0.0.1",
// Optional square brackets around IPv6 addresses, including mapped IPv4.
"[2001:4860:800d::68]",
"[::ffff:127.0.0.1]",
// Android does not handle Octal (leading 0) cases: they are treated as decimal.
"0177.00.00.01",
};
}
@Parameters(method = "validNumericAddresses")
@Test
public void test_isNumeric(String valid) throws Exception {
assertTrue(InetAddress.isNumeric(valid));
}
@Test
public void test_isNumeric_notNumeric_null() throws Exception {
try {
boolean result = InetAddress.isNumeric(null);
fail("Expected isNumeric(null) to throw a NPE but instead returned " + result);
} catch (NullPointerException expected) {
}
}
@Test
public void test_isNumeric_notNumeric_empty() throws Exception {
assertFalse(InetAddress.isNumeric(""));
}
@Test
public void test_isNumeric_notNumeric() throws Exception {
// Negative test
assertFalse(InetAddress.isNumeric("example.com"));
}
@Parameters(method = "invalidNumericAddresses")
@Test
public void test_isNumeric_invalid(String invalid) {
assertFalse(invalid, InetAddress.isNumeric(invalid));
}
@Test
public void test_isLinkLocalAddress() throws Exception {
assertFalse(InetAddress.getByName("127.0.0.1").isLinkLocalAddress());
assertFalse(InetAddress.getByName("::ffff:127.0.0.1").isLinkLocalAddress());
assertTrue(InetAddress.getByName("169.254.1.2").isLinkLocalAddress());
assertFalse(InetAddress.getByName("fec0::").isLinkLocalAddress());
assertTrue(InetAddress.getByName("fe80::").isLinkLocalAddress());
}
@Test
public void test_isMCSiteLocalAddress() throws Exception {
assertFalse(InetAddress.getByName("239.254.255.255").isMCSiteLocal());
assertTrue(InetAddress.getByName("239.255.0.0").isMCSiteLocal());
assertTrue(InetAddress.getByName("239.255.255.255").isMCSiteLocal());
assertFalse(InetAddress.getByName("240.0.0.0").isMCSiteLocal());
assertFalse(InetAddress.getByName("ff06::").isMCSiteLocal());
assertTrue(InetAddress.getByName("ff05::").isMCSiteLocal());
assertTrue(InetAddress.getByName("ff15::").isMCSiteLocal());
}
@Test
public void test_isReachable() throws Exception {
// http://code.google.com/p/android/issues/detail?id=20203
String s = "aced0005737200146a6176612e6e65742e496e6574416464726573732d9b57af"
+ "9fe3ebdb0200034900076164647265737349000666616d696c794c0008686f737"
+ "44e616d657400124c6a6176612f6c616e672f537472696e673b78704a7d9d6300"
+ "00000274000e7777772e676f6f676c652e636f6d";
InetAddress inetAddress = InetAddress.getByName("www.google.com");
new SerializationTester<InetAddress>(inetAddress, s) {
@Override protected void verify(InetAddress deserialized) throws Exception {
deserialized.isReachable(500);
for (NetworkInterface nif
: Collections.list(NetworkInterface.getNetworkInterfaces())) {
deserialized.isReachable(nif, 20, 500);
}
}
@Override protected boolean equals(InetAddress a, InetAddress b) {
return a.getHostName().equals(b.getHostName());
}
}.test();
}
@Test
public void test_isReachable_neverThrows() throws Exception {
InetAddress inetAddress = InetAddress.getByName("www.google.com");
final NetworkInterface netIf = NetworkInterface.getByName("dummy0");
if (netIf == null) {
System.logI("Skipping test_isReachable_neverThrows because dummy0 isn't available");
return;
}
assertFalse(inetAddress.isReachable(netIf, 256, 500));
}
// IPPROTO_ICMP socket kind requires setting ping_group_range. This is set on boot on Android.
// When running on host, make sure you run the command:
// sudo sysctl -w net.ipv4.ping_group_range="0 65535"
@Test
public void test_isReachable_by_ICMP() throws Exception {
InetAddress[] inetAddresses = InetAddress.getAllByName("www.google.com");
for (InetAddress ia : inetAddresses) {
// ICMP is not reliable, allow 5 attempts to each IP address before failing.
// If any address is reachable then that's sufficient.
if (ia.isReachableByICMP(5 * 1000 /* ICMP timeout */)) {
return;
}
}
fail();
}
@Test
public void test_inUnreachable() throws Exception {
// IPv6 discard prefix. RFC 6666.
final InetAddress blackholeAddress = InetAddress.getByName("100::1");
assertFalse(blackholeAddress.isReachable(1000));
}
@Test
public void test_isSiteLocalAddress() throws Exception {
assertFalse(InetAddress.getByName("144.32.32.1").isSiteLocalAddress());
assertTrue(InetAddress.getByName("10.0.0.1").isSiteLocalAddress());
assertTrue(InetAddress.getByName("172.16.0.1").isSiteLocalAddress());
assertFalse(InetAddress.getByName("172.32.0.1").isSiteLocalAddress());
assertTrue(InetAddress.getByName("192.168.0.1").isSiteLocalAddress());
assertFalse(InetAddress.getByName("fc00::").isSiteLocalAddress());
assertTrue(InetAddress.getByName("fec0::").isSiteLocalAddress());
}
public static String[] invalidNumericAddresses() {
return INVALID_IPv4_AND_6_NUMERIC_ADDRESSES;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
@Parameters(method = "invalidNumericAddresses")
@Test
public void test_getByName_invalid(String invalid) throws Exception {
try {
InetAddress.getByName(invalid);
String msg = "Invalid IP address incorrectly recognized as valid: \"" + invalid + "\"";
if (InetAddressUtils.parseNumericAddressNoThrowStripOptionalBrackets(invalid) == null) {
msg += " (it was probably unexpectedly resolved by this network's DNS)";
}
msg += ".";
fail(msg);
} catch (UnknownHostException expected) {
}
// exercise negative cache
try {
InetAddress.getByName(invalid);
fail("Invalid IP address incorrectly recognized as valid: "
+ invalid);
} catch (Exception expected) {
}
}
public static String[] validIPv6Addresses() {
return VALID_IPv6_ADDRESSES;
}
@Parameters(method = "validIPv6Addresses")
@Test
public void test_getByName_valid(String valid) throws Exception {
InetAddress.getByName(valid);
// exercise positive cache
InetAddress.getByName(valid);
// when wrapped in [..]
String tempIPAddress = "[" + valid + "]";
InetAddress.getByName(tempIPAddress);
}
@Test
public void test_getLoopbackAddress() throws Exception {
assertTrue(InetAddress.getLoopbackAddress().isLoopbackAddress());
}
@Test
public void test_equals() throws Exception {
InetAddress addr = InetAddress.getByName("239.191.255.255");
assertTrue(addr.equals(addr));
assertTrue(loopback6().equals(localhost6()));
assertFalse(addr.equals(loopback6()));
assertTrue(Inet4Address.LOOPBACK.equals(Inet4Address.LOOPBACK));
// http://b/4328294 - the scope id isn't included when comparing Inet6Address instances.
byte[] bs = new byte[16];
assertEquals(Inet6Address.getByAddress("1", bs, 1), Inet6Address.getByAddress("2", bs, 2));
}
@Test
public void test_getHostAddress() throws Exception {
assertEquals("::1", localhost6().getHostAddress());
assertEquals("::1", InetAddress.getByName("::1").getHostAddress());
assertEquals("127.0.0.1", Inet4Address.LOOPBACK.getHostAddress());
// IPv4 mapped address
assertEquals("127.0.0.1", InetAddress.getByName("::ffff:127.0.0.1").getHostAddress());
InetAddress aAddr = InetAddress.getByName("224.0.0.0");
assertEquals("224.0.0.0", aAddr.getHostAddress());
try {
InetAddress.getByName("1");
fail();
} catch (UnknownHostException expected) {
}
byte[] bAddr = {
(byte) 0xFE, (byte) 0x80, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x02, (byte) 0x11, (byte) 0x25, (byte) 0xFF,
(byte) 0xFE, (byte) 0xF8, (byte) 0x7C, (byte) 0xB2
};
aAddr = Inet6Address.getByAddress(bAddr);
String aString = aAddr.getHostAddress();
assertTrue(aString.equals("fe80:0:0:0:211:25ff:fef8:7cb2") || aString.equals("fe80::211:25ff:fef8:7cb2"));
byte[] cAddr = {
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
};
aAddr = Inet6Address.getByAddress(cAddr);
assertEquals("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", aAddr.getHostAddress());
byte[] dAddr = {
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
};
aAddr = Inet6Address.getByAddress(dAddr);
aString = aAddr.getHostAddress();
assertTrue(aString.equals("0:0:0:0:0:0:0:0") || aString.equals("::"));
byte[] eAddr = {
(byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
(byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
(byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b,
(byte) 0x0c, (byte) 0x0d, (byte) 0x0e, (byte) 0x0f
};
aAddr = Inet6Address.getByAddress(eAddr);
assertEquals("1:203:405:607:809:a0b:c0d:e0f", aAddr.getHostAddress());
byte[] fAddr = {
(byte) 0x00, (byte) 0x10, (byte) 0x20, (byte) 0x30,
(byte) 0x40, (byte) 0x50, (byte) 0x60, (byte) 0x70,
(byte) 0x80, (byte) 0x90, (byte) 0xa0, (byte) 0xb0,
(byte) 0xc0, (byte) 0xd0, (byte) 0xe0, (byte) 0xf0
};
aAddr = Inet6Address.getByAddress(fAddr);
assertEquals("10:2030:4050:6070:8090:a0b0:c0d0:e0f0", aAddr.getHostAddress());
}
@Test
public void test_hashCode() throws Exception {
InetAddress addr1 = InetAddress.getByName("1.0.0.1");
InetAddress addr2 = InetAddress.getByName("1.0.0.1");
assertTrue(addr1.hashCode() == addr2.hashCode());
assertTrue(loopback6().hashCode() == localhost6().hashCode());
}
public static String[][] validAddressesAndStringRepresentation() {
return new String[][] {
{ "::1.2.3.4", "/::1.2.3.4" },
{ "::", "/::" },
{ "1::0", "/1::" },
{ "1::", "/1::" },
{ "::1", "/::1" },
{ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" },
{ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255", "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" },
{ "0:0:0:0:0:0:0:0", "/::" },
{ "0:0:0:0:0:0:0.0.0.0", "/::" },
};
}
@Parameters(method = "validAddressesAndStringRepresentation")
@Test
public void test_toString(String address, String expectedString) throws Exception {
InetAddress ia = InetAddress.getByName(address);
String result = ia.toString();
assertNotNull(result);
assertEquals(expectedString, result);
}
@Test
public void test_getHostNameCaches() throws Exception {
InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
// There should be no cached name.
assertEquals("::1", getHostStringWithoutReverseDns(inetAddress));
// Force the reverse-DNS lookup.
assertEquals("ip6-localhost", inetAddress.getHostName());
// The cached name should now be different.
assertEquals("ip6-localhost", getHostStringWithoutReverseDns(inetAddress));
}
@Test
public void test_getByAddress_loopbackIpv4() throws Exception {
InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK4_BYTES);
checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
@Test
public void test_getByAddress_loopbackIpv6() throws Exception {
InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
@Test
public void test_getByName_loopbackIpv4() throws Exception {
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
@Test
public void test_getByName_loopbackIpv6() throws Exception {
InetAddress inetAddress = InetAddress.getByName("::1");
checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
@Test
public void test_getByName_empty() throws Exception {
InetAddress inetAddress = InetAddress.getByName("");
checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
@Test
public void test_getAllByName_localhost() throws Exception {
InetAddress[] inetAddresses = InetAddress.getAllByName("localhost");
assertEquals(1, inetAddresses.length);
InetAddress inetAddress = inetAddresses[0];
checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
@Test
public void test_getAllByName_ip6_localhost() throws Exception {
InetAddress[] inetAddresses = InetAddress.getAllByName("ip6-localhost");
assertEquals(1, inetAddresses.length);
InetAddress inetAddress = inetAddresses[0];
checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
@Test
public void test_getByName_v6loopback() throws Exception {
InetAddress inetAddress = InetAddress.getByName("::1");
Set<InetAddress> expectedLoopbackAddresses =
createSet(Inet4Address.LOOPBACK, Inet6Address.LOOPBACK);
assertTrue(expectedLoopbackAddresses.contains(inetAddress));
}
@Test
public void test_getByName_cloning() throws Exception {
InetAddress[] addresses = InetAddress.getAllByName(null);
InetAddress[] addresses2 = InetAddress.getAllByName(null);
assertNotNull(addresses[0]);
assertNotNull(addresses[1]);
assertNotSame(addresses, addresses2);
// Also assert that changes to the return value do not affect the cache
// etc. i.e, that we return a copy.
addresses[0] = null;
addresses2 = InetAddress.getAllByName(null);
assertNotNull(addresses2[0]);
assertNotNull(addresses2[1]);
}
@Test
public void test_getAllByName_null() throws Exception {
InetAddress[] inetAddresses = InetAddress.getAllByName(null);
assertEquals(2, inetAddresses.length);
Set<InetAddress> expectedLoopbackAddresses =
createSet(Inet4Address.LOOPBACK, Inet6Address.LOOPBACK);
assertEquals(expectedLoopbackAddresses, createSet(inetAddresses));
}
// http://b/29311351
@Test
public void test_loopbackConstantsPreInitializedNames() {
// Note: Inet6Address / Inet4Address equals() does not check host name.
assertEquals("ip6-localhost", getHostStringWithoutReverseDns(Inet6Address.LOOPBACK));
assertEquals("localhost", getHostStringWithoutReverseDns(Inet4Address.LOOPBACK));
}
private static void checkInetAddress(
byte[] expectedAddressBytes, String expectedHostname, InetAddress actual) {
assertArrayEquals(expectedAddressBytes, actual.getAddress());
assertEquals(expectedHostname, actual.getHostName());
}
private static void assertArrayEquals(byte[] expected, byte[] actual) {
assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
Arrays.equals(expected, actual));
}
private static Set<InetAddress> createSet(InetAddress... members) {
return new HashSet<InetAddress>(Arrays.asList(members));
}
private static String getHostStringWithoutReverseDns(InetAddress inetAddress) {
// The InetAddress API provides no way of avoiding a DNS lookup, but InetSocketAddress
// does via InetSocketAddress.getHostString().
InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, 9999);
return inetSocketAddress.getHostString();
}
}