diff options
author | Lorenzo Colitti <lorenzo@google.com> | 2015-01-17 00:24:27 +0900 |
---|---|---|
committer | Lorenzo Colitti <lorenzo@google.com> | 2015-03-14 10:32:26 +0900 |
commit | b2a9923fa2006e384013734d3e1c6a7fbe3d9074 (patch) | |
tree | 0c393f3149013a30bfb8ace752471437b610d930 /luni/src/test/java | |
parent | a2e51f77377874735fcfb1d477dbf7f8e3503ae5 (diff) | |
download | libcore-b2a9923fa2006e384013734d3e1c6a7fbe3d9074.zip libcore-b2a9923fa2006e384013734d3e1c6a7fbe3d9074.tar.gz libcore-b2a9923fa2006e384013734d3e1c6a7fbe3d9074.tar.bz2 |
Make it possible to use AF_INET sockets created by libcore.
Currently, it's possible to create AF_INET sockets using:
Libcore.os.socket(AF_INET, ...
but such sockets can't be used for anything, because os.bind()
and os.connect() automatically convert IPv4 addresses to
IPv4-mapped addresses (e.g., 192.0.2.1 to ::ffff:192.0.2.1),
and passing a sockaddr_in6 to a system call on an AF_INET socket
causes the kernel to return EAFNOSUPPORT.
When this happens, retry using an unmapped IPv4 address. We
could also call getsockopt(SOL_SOCKET, SO_DOMAIN) before every
system call and pass in the appropriate socket address
structure, but that would cause these socket functions to make
two system calls instead of one and would probably halve the
performance of sendto(). This way, there's only one system call
for most sockets (including all AF_INET6 sockets created in
Java), and two system calls only in the rare case of a socket
that's explicitly created as AF_INET.
Bug: 18558481
Bug: 19704592
Change-Id: I71b3728b6a72b742e156d4c60db65e88b9a9e51e
Diffstat (limited to 'luni/src/test/java')
-rw-r--r-- | luni/src/test/java/libcore/io/OsTest.java | 94 |
1 files changed, 90 insertions, 4 deletions
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java index c0a18b9..e6c946c 100644 --- a/luni/src/test/java/libcore/io/OsTest.java +++ b/luni/src/test/java/libcore/io/OsTest.java @@ -16,12 +16,15 @@ package libcore.io; +import android.system.ErrnoException; import android.system.NetlinkSocketAddress; import android.system.StructUcred; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.InetUnixAddress; @@ -29,6 +32,7 @@ import java.net.ServerSocket; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Locale; import junit.framework.TestCase; import static android.system.OsConstants.*; @@ -211,9 +215,10 @@ public class OsTest extends TestCase { fis.close(); } - public void test_byteBufferPositions_sendto_recvfrom() throws Exception { - final FileDescriptor serverFd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0); - Libcore.os.bind(serverFd, InetAddress.getLoopbackAddress(), 0); + static void checkByteBufferPositions_sendto_recvfrom( + int family, InetAddress loopback) throws Exception { + final FileDescriptor serverFd = Libcore.os.socket(family, SOCK_STREAM, 0); + Libcore.os.bind(serverFd, loopback, 0); Libcore.os.listen(serverFd, 5); InetSocketAddress address = (InetSocketAddress) Libcore.os.getsockname(serverFd); @@ -247,7 +252,7 @@ public class OsTest extends TestCase { server.start(); - FileDescriptor clientFd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0); + FileDescriptor clientFd = Libcore.os.socket(family, SOCK_STREAM, 0); Libcore.os.connect(clientFd, address.getAddress(), address.getPort()); final byte[] bytes = "good bye, cruel black hole with fancy distortion".getBytes(StandardCharsets.US_ASCII); @@ -284,4 +289,85 @@ public class OsTest extends TestCase { assertEquals(0, nlPeer.getGroupsMask()); Libcore.os.close(nlSocket); } + + public void test_byteBufferPositions_sendto_recvfrom_af_inet() throws Exception { + checkByteBufferPositions_sendto_recvfrom(AF_INET, InetAddress.getByName("127.0.0.1")); + } + + public void test_byteBufferPositions_sendto_recvfrom_af_inet6() throws Exception { + checkByteBufferPositions_sendto_recvfrom(AF_INET6, InetAddress.getByName("::1")); + } + + public void test_socketFamilies() throws Exception { + FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0); + Libcore.os.bind(fd, InetAddress.getByName("::"), 0); + InetSocketAddress localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd); + assertEquals(Inet6Address.ANY, localSocketAddress.getAddress()); + + fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0); + Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0); + localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd); + assertEquals(Inet6Address.ANY, localSocketAddress.getAddress()); + + fd = Libcore.os.socket(AF_INET, SOCK_STREAM, 0); + Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0); + localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd); + assertEquals(Inet4Address.ANY, localSocketAddress.getAddress()); + try { + Libcore.os.bind(fd, InetAddress.getByName("::"), 0); + fail("Expected ErrnoException binding IPv4 socket to ::"); + } catch (ErrnoException expected) { + assertEquals("Expected EAFNOSUPPORT binding IPv4 socket to ::", EAFNOSUPPORT, expected.errno); + } + } + + private static void assertArrayEquals(byte[] expected, byte[] actual) { + assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual), + Arrays.equals(expected, actual)); + } + + private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet, + byte type, byte responseType, boolean useSendto) throws Exception { + int len = packet.length; + packet[0] = type; + if (useSendto) { + assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, to, 0)); + } else { + Libcore.os.connect(fd, to, 0); + assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, null, 0)); + } + + int icmpId = ((InetSocketAddress) Libcore.os.getsockname(fd)).getPort(); + byte[] received = new byte[4096]; + InetSocketAddress srcAddress = new InetSocketAddress(); + assertEquals(len, Libcore.os.recvfrom(fd, received, 0, received.length, 0, srcAddress)); + assertEquals(to, srcAddress.getAddress()); + assertEquals(responseType, received[0]); + assertEquals(received[4], (byte) (icmpId >> 8)); + assertEquals(received[5], (byte) (icmpId & 0xff)); + + received = Arrays.copyOf(received, len); + received[0] = (byte) type; + received[2] = received[3] = 0; // Checksum. + received[4] = received[5] = 0; // ICMP ID. + assertArrayEquals(packet, received); + } + + public void test_socketPing() throws Exception { + final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0; + final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129; + final byte[] packet = ("\000\000\000\000" + // ICMP type, code. + "\000\000\000\003" + // ICMP ID (== port), sequence number. + "Hello myself").getBytes(StandardCharsets.US_ASCII); + + FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6); + InetAddress ipv6Loopback = InetAddress.getByName("::1"); + checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, true); + checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, false); + + fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + InetAddress ipv4Loopback = InetAddress.getByName("127.0.0.1"); + checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, true); + checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false); + } } |