Fix bug with bind()/connect() and IPv4 addresses
Our standard utilities for converting InetAddress to sockaddr convert
IPv4 addresses to IPv4-mapped sockaddr_in6 instances. This works fine
for most use cases, but when calling functions with AF_INET (aka IPv4)
sockets returned from Libcore.os.socket(), they need to be given a
sockaddr_in or they fail with EAFNOSUPPORT.
We have a utility to deal with this (NET_IPV4_FALLBACK), but the
versions of bind() and connect() that took SocketAddress instances
instead of InetAddress instances didn't use it. Follow the lead of
sendTo() and convert InetSocketAddress instances to InetAddress/port
pairs and call the InetAddress version of the call so we get the
benefit of NET_IPV4_FALLBACK.
Bug: 123562238
Test: cts -m CtsLibcoreTestCases
Change-Id: Ib8c1fda7be3dda8e6eee31fd15f2d0c4567a5712
diff --git a/luni/src/main/native/libcore_io_Linux.cpp b/luni/src/main/native/libcore_io_Linux.cpp
index 5d621de..635619a 100644
--- a/luni/src/main/native/libcore_io_Linux.cpp
+++ b/luni/src/main/native/libcore_io_Linux.cpp
@@ -986,7 +986,15 @@
}
static void Linux_bindSocketAddress(
- JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
+ JNIEnv* env, jobject thisObj, jobject javaFd, jobject javaSocketAddress) {
+ if (env->IsInstanceOf(javaSocketAddress, JniConstants::GetInetSocketAddressClass(env))) {
+ // Use the InetAddress version so we get the benefit of NET_IPV4_FALLBACK.
+ jobject javaInetAddress;
+ jint port;
+ javaInetSocketAddressToInetAddressAndPort(env, javaSocketAddress, javaInetAddress, port);
+ Linux_bind(env, thisObj, javaFd, javaInetAddress, port);
+ return;
+ }
sockaddr_storage ss;
socklen_t sa_len;
if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) {
@@ -1160,7 +1168,15 @@
}
static void Linux_connectSocketAddress(
- JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
+ JNIEnv* env, jobject thisObj, jobject javaFd, jobject javaSocketAddress) {
+ if (env->IsInstanceOf(javaSocketAddress, JniConstants::GetInetSocketAddressClass(env))) {
+ // Use the InetAddress version so we get the benefit of NET_IPV4_FALLBACK.
+ jobject javaInetAddress;
+ jint port;
+ javaInetSocketAddressToInetAddressAndPort(env, javaSocketAddress, javaInetAddress, port);
+ Linux_connect(env, thisObj, javaFd, javaInetAddress, port);
+ return;
+ }
sockaddr_storage ss;
socklen_t sa_len;
if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) {
diff --git a/luni/src/test/java/libcore/libcore/io/OsTest.java b/luni/src/test/java/libcore/libcore/io/OsTest.java
index 5d09939..91105bf 100644
--- a/luni/src/test/java/libcore/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/libcore/io/OsTest.java
@@ -408,9 +408,7 @@
SocketAddress addrIpv6 = new InetSocketAddress(InetAddress.getByName("::1"), 0);
SocketAddress addrUnix = UnixSocketAddress.createAbstract("/abstract_name_unix_socket");
- // TODO: fix and uncomment. Currently fails with EAFNOSUPPORT because
- // Linux_bindSocketAddress uses NET_FAILURE_RETRY instead of NET_IPV4_RETRY.
- // expectBindConnectSendtoSuccess(makeIpv4Socket(), "ipv4", addrIpv4);
+ expectBindConnectSendtoSuccess(makeIpv4Socket(), "ipv4", addrIpv4);
expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EAFNOSUPPORT,
makeIpv4Socket(), "ipv4", addrIpv6);
expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EAFNOSUPPORT,