summaryrefslogtreecommitdiffstats
path: root/luni
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2015-03-16 11:12:51 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-03-16 11:12:51 +0000
commit1461a444bc7ef309a2499485199303e7e93bfdd0 (patch)
tree6dffba22b091cec3a22cf6b708cd3e35c9dd4c02 /luni
parentca97cdd527eceb0edb28a69f5769b3c7d701adff (diff)
parent4beb74e0ae832698c94fb298be6e122e70bebcaa (diff)
downloadlibcore-1461a444bc7ef309a2499485199303e7e93bfdd0.zip
libcore-1461a444bc7ef309a2499485199303e7e93bfdd0.tar.gz
libcore-1461a444bc7ef309a2499485199303e7e93bfdd0.tar.bz2
am 4beb74e0: am db419d06: Merge "Make it possible to use AF_INET sockets created by libcore."
* commit '4beb74e0ae832698c94fb298be6e122e70bebcaa': Make it possible to use AF_INET sockets created by libcore.
Diffstat (limited to 'luni')
-rw-r--r--luni/src/main/native/libcore_io_Posix.cpp76
-rw-r--r--luni/src/test/java/libcore/io/OsTest.java94
2 files changed, 144 insertions, 26 deletions
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index a1113ab..0f12e06 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -78,6 +78,52 @@ struct addrinfo_deleter {
}
};
+static bool isIPv4MappedAddress(const sockaddr *sa) {
+ const sockaddr_in6 *sin6 = reinterpret_cast<const sockaddr_in6*>(sa);
+ return sa != NULL && sa->sa_family == AF_INET6 &&
+ (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)); // We map 0.0.0.0 to ::, so :: is mapped.
+}
+
+/**
+ * Perform a socket operation that specifies an IP address, possibly falling back from specifying
+ * the address as an IPv4-mapped IPv6 address in a struct sockaddr_in6 to specifying it as an IPv4
+ * address in a struct sockaddr_in.
+ *
+ * This is needed because all sockets created by the java.net APIs are IPv6 sockets, and on those
+ * sockets, IPv4 operations use IPv4-mapped addresses stored in a struct sockaddr_in6. But sockets
+ * created using Posix.socket(AF_INET, ...) are IPv4 sockets and only support operations using IPv4
+ * socket addresses structures.
+ */
+#define NET_IPV4_FALLBACK(jni_env, return_type, syscall_name, java_fd, java_addr, port, null_addr_ok, args...) ({ \
+ return_type _rc = -1; \
+ do { \
+ sockaddr_storage _ss; \
+ socklen_t _salen; \
+ if (java_addr == NULL && null_addr_ok) { \
+ /* No IP address specified (e.g., sendto() on a connected socket). */ \
+ _salen = 0; \
+ } else if (!inetAddressToSockaddr(jni_env, java_addr, port, _ss, _salen)) { \
+ /* Invalid socket address, return -1. inetAddressToSockaddr has already thrown. */ \
+ break; \
+ } \
+ sockaddr* _sa = _salen ? reinterpret_cast<sockaddr*>(&_ss) : NULL; \
+ /* inetAddressToSockaddr always returns an IPv6 sockaddr. Assume that java_fd was created \
+ * by Java API calls, which always create IPv6 socket fds, and pass it in as is. */ \
+ _rc = NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ##args, _sa, _salen); \
+ if (_rc == -1 && errno == EAFNOSUPPORT && _salen && isIPv4MappedAddress(_sa)) { \
+ /* We passed in an IPv4 address in an IPv6 sockaddr and the kernel told us that we got \
+ * the address family wrong. Pass in the same address in an IPv4 sockaddr. */ \
+ jni_env->ExceptionClear(); \
+ if (!inetAddressToSockaddrVerbatim(jni_env, java_addr, port, _ss, _salen)) { \
+ break; \
+ } \
+ _sa = reinterpret_cast<sockaddr*>(&_ss); \
+ _rc = NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ##args, _sa, _salen); \
+ } \
+ } while (0); \
+ _rc; }) \
+
/**
* Used to retry networking system calls that can be interrupted with a signal. Unlike
* TEMP_FAILURE_RETRY, this also handles the case where
@@ -149,6 +195,9 @@ struct addrinfo_deleter {
} while (_rc == -1); /* && _syscallErrno == EINTR && !_wasSignaled */ \
_rc; })
+#define NULL_ADDR_OK true
+#define NULL_ADDR_FORBIDDEN false
+
static void throwException(JNIEnv* env, jclass exceptionClass, jmethodID ctor3, jmethodID ctor2,
const char* functionName, int error) {
jthrowable cause = NULL;
@@ -532,14 +581,8 @@ static jboolean Posix_access(JNIEnv* env, jobject, jstring javaPath, jint mode)
}
static void Posix_bind(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
- sockaddr_storage ss;
- socklen_t sa_len;
- if (!inetAddressToSockaddr(env, javaAddress, port, ss, sa_len)) {
- return;
- }
- const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
// We don't need the return value because we'll already have thrown.
- (void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sa_len);
+ (void) NET_IPV4_FALLBACK(env, int, bind, javaFd, javaAddress, port, NULL_ADDR_FORBIDDEN);
}
static void Posix_bindSocketAddress(
@@ -584,14 +627,7 @@ static void Posix_close(JNIEnv* env, jobject, jobject javaFd) {
}
static void Posix_connect(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
- sockaddr_storage ss;
- socklen_t sa_len;
- if (!inetAddressToSockaddr(env, javaAddress, port, ss, sa_len)) {
- return;
- }
- const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
- // We don't need the return value because we'll already have thrown.
- (void) NET_FAILURE_RETRY(env, int, connect, javaFd, sa, sa_len);
+ (void) NET_IPV4_FALLBACK(env, int, connect, javaFd, javaAddress, port, NULL_ADDR_FORBIDDEN);
}
static void Posix_connectSocketAddress(
@@ -1360,13 +1396,9 @@ static jint Posix_sendtoBytes(JNIEnv* env, jobject, jobject javaFd, jobject java
if (bytes.get() == NULL) {
return -1;
}
- sockaddr_storage ss;
- socklen_t sa_len = 0;
- if (javaInetAddress != NULL && !inetAddressToSockaddr(env, javaInetAddress, port, ss, sa_len)) {
- return -1;
- }
- const sockaddr* to = (javaInetAddress != NULL) ? reinterpret_cast<const sockaddr*>(&ss) : NULL;
- return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, to, sa_len);
+
+ return NET_IPV4_FALLBACK(env, ssize_t, sendto, javaFd, javaInetAddress, port,
+ NULL_ADDR_OK, bytes.get() + byteOffset, byteCount, flags);
}
static void Posix_setegid(JNIEnv* env, jobject, jint egid) {
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);
+ }
}