Add GSO and GRO Support Via recvmsg and sendmsg
Create new wrappers for sendmsg and recvmsg so that apps
that know about UDP GRO can call these directly.
Bug: 172945027
Test: atest libcore.android.system.OsTest#test_sendmsg_af_inet_4K
libcore.android.system.OsTest#test_sendmsg_af_inet6_4K
libcore.android.system.OsTest#test_sendmsg_af_inet_16K_recvparts
libcore.android.system.OsTest#test_sendmsg_af_inet_16K_reciveall
libcore.android.system.OsTest#test_sendmsg_af_inet_16K_reciveall_without_recv_msgname
libcore.android.system.OsTest#test_sendmsg_af_inet_16K_without_send_msgcontrl
libcore.android.system.OsTest#test_sendmsg_af_inet_abnormal
Change-Id: Ib65bfba7361139d49049c5f2cae9f2b0da6e27cb
diff --git a/api/current.txt b/api/current.txt
old mode 100644
new mode 100755
index c3e758c..4abf0c5
--- a/api/current.txt
+++ b/api/current.txt
@@ -85,10 +85,12 @@
method public static int readv(java.io.FileDescriptor, Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+ method public static int recvmsg(@NonNull java.io.FileDescriptor, @NonNull android.system.StructMsghdr, int) throws android.system.ErrnoException, java.net.SocketException;
method public static void remove(String) throws android.system.ErrnoException;
method public static void removexattr(String, String) throws android.system.ErrnoException;
method public static void rename(String, String) throws android.system.ErrnoException;
method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
+ method public static int sendmsg(@NonNull java.io.FileDescriptor, @NonNull android.system.StructMsghdr, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(@NonNull java.io.FileDescriptor, @NonNull byte[], int, int, int, @Nullable java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException;
@@ -636,6 +638,22 @@
field public static final int _SC_XOPEN_XCU_VERSION;
}
+ public final class StructCmsghdr {
+ ctor public StructCmsghdr(int, int, short);
+ ctor public StructCmsghdr(int, int, @NonNull byte[]);
+ field @NonNull public final byte[] cmsg_data;
+ field public final int cmsg_level;
+ field public final int cmsg_type;
+ }
+
+ public final class StructMsghdr {
+ ctor public StructMsghdr(@Nullable java.net.SocketAddress, @NonNull java.nio.ByteBuffer[], @Nullable android.system.StructCmsghdr[], int);
+ field @Nullable public android.system.StructCmsghdr[] msg_control;
+ field public int msg_flags;
+ field @NonNull public final java.nio.ByteBuffer[] msg_iov;
+ field @Nullable public java.net.SocketAddress msg_name;
+ }
+
public final class StructPollfd {
ctor public StructPollfd();
field public short events;
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
old mode 100644
new mode 100755
index 80a700c..d999ad7
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -510,6 +510,11 @@
public static int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
/**
+ * See <a href="http://man7.org/linux/man-pages/man2/recvmsg.2.html">recvmsg(2)</a>.
+ */
+ public static int recvmsg(@NonNull FileDescriptor fd, @NonNull StructMsghdr msg, int flags) throws ErrnoException, SocketException { return Libcore.os.recvmsg(fd, msg, flags); }
+
+ /**
* See <a href="http://man7.org/linux/man-pages/man3/remove.3.html">remove(3)</a>.
*/
public static void remove(String path) throws ErrnoException { Libcore.os.remove(path); }
@@ -532,6 +537,13 @@
}
/**
+ * See <a href="http://man7.org/linux/man-pages/man2/sendmsg.2.html">sendmsg(2)</a>.
+ */
+ public static int sendmsg(@NonNull FileDescriptor fd, @NonNull StructMsghdr msg, int flags) throws ErrnoException, SocketException {
+ return Libcore.os.sendmsg(fd, msg, flags);
+ }
+
+ /**
* See <a href="http://man7.org/linux/man-pages/man2/sendto.2.html">sendto(2)</a>.
*/
public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, buffer, flags, inetAddress, port); }
diff --git a/luni/src/main/java/android/system/StructCmsghdr.java b/luni/src/main/java/android/system/StructCmsghdr.java
new file mode 100755
index 0000000..be77481
--- /dev/null
+++ b/luni/src/main/java/android/system/StructCmsghdr.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * *Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * *Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * *Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package android.system;
+
+import libcore.util.NonNull;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Corresponds to C's {@code struct cmsghdr}.
+ *
+ */
+public final class StructCmsghdr {
+ /** Originating protocol */
+ public final int cmsg_level;
+
+ /** Protocol-specific type */
+ public final int cmsg_type;
+
+ /** message data sent/received */
+ @NonNull public final byte[] cmsg_data;
+
+ public StructCmsghdr(int cmsg_level, int cmsg_type, short value) {
+ // Short.Size unit is bits, ByteBuffer data unit is bytes
+ ByteBuffer buf = ByteBuffer.allocate(Short.SIZE / 8);
+ buf.order(ByteOrder.nativeOrder());
+ buf.putShort(value);
+
+ this.cmsg_level = cmsg_level;
+ this.cmsg_type = cmsg_type;
+ this.cmsg_data = buf.array();
+ }
+
+ public StructCmsghdr(int cmsg_level, int cmsg_type, @NonNull byte[] value) {
+ this.cmsg_level = cmsg_level;
+ this.cmsg_type = cmsg_type;
+ this.cmsg_data = value;
+ }
+
+}
diff --git a/luni/src/main/java/android/system/StructMsghdr.java b/luni/src/main/java/android/system/StructMsghdr.java
new file mode 100755
index 0000000..60a4d97
--- /dev/null
+++ b/luni/src/main/java/android/system/StructMsghdr.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * *Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * *Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * *Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package android.system;
+
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * Corresponds to C's {@code struct msghdr}
+ *
+ */
+public final class StructMsghdr{
+ /**
+ * Optional address.
+ * <p>Sendmsg: Caller must populate to specify the target address for a datagram, or pass
+ * {@code null} to send to the destination of an already-connected socket.
+ * Recvmsg: Populated by the system to specify the source address.
+ */
+ @Nullable public SocketAddress msg_name;
+
+ /** Scatter/gather array */
+ @NonNull public final ByteBuffer[] msg_iov;
+
+ /** Ancillary data */
+ @Nullable public StructCmsghdr[] msg_control;
+
+ /** Flags on received message. */
+ public int msg_flags;
+
+ /**
+ * Constructs an instance with the given field values
+ */
+ public StructMsghdr(@Nullable SocketAddress msg_name, @NonNull ByteBuffer[] msg_iov,
+ @Nullable StructCmsghdr[] msg_control, int msg_flags) {
+ this.msg_name = msg_name;
+ this.msg_iov = msg_iov;
+ this.msg_control = msg_control;
+ this.msg_flags = msg_flags;
+ }
+}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
old mode 100644
new mode 100755
index 3112c16..5b331b1
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -23,6 +23,7 @@
import android.system.OsConstants;
import android.system.StructAddrinfo;
import android.system.StructLinger;
+import android.system.StructMsghdr;
import android.system.StructPollfd;
import android.system.StructStat;
import android.system.StructStatVfs;
@@ -353,6 +354,11 @@
return super.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
}
+ @Override public int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException {
+ BlockGuard.getThreadPolicy().onNetwork();
+ return super.recvmsg(fd, msg, flags);
+ }
+
@UnsupportedAppUsage
@Override public void remove(String path) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
@@ -373,6 +379,11 @@
return super.sendfile(outFd, inFd, offset, byteCount);
}
+ @Override public int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException {
+ BlockGuard.getThreadPolicy().onNetwork();
+ return super.sendmsg(fd, msg, flags);
+ }
+
@Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
return super.sendto(fd, buffer, flags, inetAddress, port);
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
old mode 100644
new mode 100755
index 7e8a40e..346a199
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -26,6 +26,7 @@
import android.system.StructGroupReq;
import android.system.StructIfaddrs;
import android.system.StructLinger;
+import android.system.StructMsghdr;
import android.system.StructPasswd;
import android.system.StructPollfd;
import android.system.StructRlimit;
@@ -180,6 +181,7 @@
public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return os.readv(fd, buffers, offsets, byteCounts); }
public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, buffer, flags, srcAddress); }
public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
+ public int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException { return os.recvmsg(fd, msg, flags); }
@UnsupportedAppUsage
@libcore.api.CorePlatformApi
public void remove(String path) throws ErrnoException { os.remove(path); }
@@ -189,6 +191,7 @@
@libcore.api.CorePlatformApi
public void rename(String oldPath, String newPath) throws ErrnoException { os.rename(oldPath, newPath); }
public long sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref offset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, offset, byteCount); }
+ public int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException { return os.sendmsg(fd, msg, flags); }
public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, buffer, flags, inetAddress, port); }
public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, address); }
diff --git a/luni/src/main/java/libcore/io/Linux.java b/luni/src/main/java/libcore/io/Linux.java
old mode 100644
new mode 100755
index 37e5f5e..4ecb785
--- a/luni/src/main/java/libcore/io/Linux.java
+++ b/luni/src/main/java/libcore/io/Linux.java
@@ -26,6 +26,7 @@
import android.system.StructGroupReq;
import android.system.StructIfaddrs;
import android.system.StructLinger;
+import android.system.StructMsghdr;
import android.system.StructPasswd;
import android.system.StructPollfd;
import android.system.StructRlimit;
@@ -210,10 +211,12 @@
return recvfromBytes(fd, bytes, byteOffset, byteCount, flags, srcAddress);
}
private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
+ public native int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
public native void remove(String path) throws ErrnoException;
public native void removexattr(String path, String name) throws ErrnoException;
public native void rename(String oldPath, String newPath) throws ErrnoException;
public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref offset, long byteCount) throws ErrnoException;
+ public native int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
final int bytesSent;
final int position = buffer.position();
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
old mode 100644
new mode 100755
index 87e5d78..341e917
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -26,6 +26,7 @@
import android.system.StructGroupReq;
import android.system.StructIfaddrs;
import android.system.StructLinger;
+import android.system.StructMsghdr;
import android.system.StructPasswd;
import android.system.StructPollfd;
import android.system.StructRlimit;
@@ -153,10 +154,12 @@
public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
+ public int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
@UnsupportedAppUsage
public void remove(String path) throws ErrnoException;
public void removexattr(String path, String name) throws ErrnoException;
public void rename(String oldPath, String newPath) throws ErrnoException;
+ public int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException;
diff --git a/luni/src/main/native/JniConstants.cpp b/luni/src/main/native/JniConstants.cpp
old mode 100644
new mode 100755
index 8e0a0f6..fc1607f
--- a/luni/src/main/native/JniConstants.cpp
+++ b/luni/src/main/native/JniConstants.cpp
@@ -45,6 +45,7 @@
// Constants
jclass booleanClass;
+jclass byteBufferClass;
jclass doubleClass;
jclass errnoExceptionClass;
jclass fileDescriptorClass;
@@ -63,9 +64,11 @@
jclass primitiveByteArrayClass;
jclass stringClass;
jclass structAddrinfoClass;
+jclass structCmsghdrClass;
jclass structGroupReqClass;
jclass structIfaddrsClass;
jclass structLingerClass;
+jclass structMsghdrClass;
jclass structPasswdClass;
jclass structPollfdClass;
jclass structStatClass;
@@ -91,6 +94,7 @@
}
booleanClass = findClass(env, "java/lang/Boolean");
+ byteBufferClass = findClass(env, "java/nio/ByteBuffer");
doubleClass = findClass(env, "java/lang/Double");
errnoExceptionClass = findClass(env, "android/system/ErrnoException");
fileDescriptorClass = findClass(env, "java/io/FileDescriptor");
@@ -109,9 +113,11 @@
primitiveByteArrayClass = findClass(env, "[B");
stringClass = findClass(env, "java/lang/String");
structAddrinfoClass = findClass(env, "android/system/StructAddrinfo");
+ structCmsghdrClass = findClass(env, "android/system/StructCmsghdr");
structGroupReqClass = findClass(env, "android/system/StructGroupReq");
structIfaddrsClass = findClass(env, "android/system/StructIfaddrs");
structLingerClass = findClass(env, "android/system/StructLinger");
+ structMsghdrClass = findClass(env, "android/system/StructMsghdr");
structPasswdClass = findClass(env, "android/system/StructPasswd");
structPollfdClass = findClass(env, "android/system/StructPollfd");
structStatClass = findClass(env, "android/system/StructStat");
@@ -150,6 +156,11 @@
return booleanClass;
}
+jclass JniConstants::GetByteBufferClass(JNIEnv* env) {
+ EnsureJniConstantsInitialized(env);
+ return byteBufferClass;
+}
+
jclass JniConstants::GetDoubleClass(JNIEnv* env) {
EnsureJniConstantsInitialized(env);
return doubleClass;
@@ -240,6 +251,11 @@
return structAddrinfoClass;
}
+jclass JniConstants::GetStructCmsghdrClass(JNIEnv* env) {
+ EnsureJniConstantsInitialized(env);
+ return structCmsghdrClass;
+}
+
jclass JniConstants::GetStructGroupReqClass(JNIEnv* env) {
EnsureJniConstantsInitialized(env);
return structGroupReqClass;
@@ -255,6 +271,11 @@
return structLingerClass;
}
+jclass JniConstants::GetStructMsghdrClass(JNIEnv* env) {
+ EnsureJniConstantsInitialized(env);
+ return structMsghdrClass;
+}
+
jclass JniConstants::GetStructPasswdClass(JNIEnv* env) {
EnsureJniConstantsInitialized(env);
return structPasswdClass;
diff --git a/luni/src/main/native/JniConstants.h b/luni/src/main/native/JniConstants.h
old mode 100644
new mode 100755
index f77095e..1aa801e
--- a/luni/src/main/native/JniConstants.h
+++ b/luni/src/main/native/JniConstants.h
@@ -30,6 +30,7 @@
static void Invalidate();
static jclass GetBooleanClass(JNIEnv* env);
+ static jclass GetByteBufferClass(JNIEnv* env);
static jclass GetDoubleClass(JNIEnv* env);
static jclass GetErrnoExceptionClass(JNIEnv* env);
static jclass GetFileDescriptorClass(JNIEnv* env);
@@ -48,10 +49,12 @@
static jclass GetPrimitiveByteArrayClass(JNIEnv* env);
static jclass GetStringClass(JNIEnv* env);
static jclass GetStructAddrinfoClass(JNIEnv* env);
+ static jclass GetStructCmsghdrClass(JNIEnv* env);
static jclass GetStructFlockClass(JNIEnv* env);
static jclass GetStructGroupReqClass(JNIEnv* env);
static jclass GetStructIfaddrsClass(JNIEnv* env);
static jclass GetStructLingerClass(JNIEnv* env);
+ static jclass GetStructMsghdrClass(JNIEnv* env);
static jclass GetStructPasswdClass(JNIEnv* env);
static jclass GetStructPollfdClass(JNIEnv* env);
static jclass GetStructStatClass(JNIEnv* env);
diff --git a/luni/src/main/native/NetworkUtilities.cpp b/luni/src/main/native/NetworkUtilities.cpp
old mode 100644
new mode 100755
index b765c4e..cd695b7
--- a/luni/src/main/native/NetworkUtilities.cpp
+++ b/luni/src/main/native/NetworkUtilities.cpp
@@ -21,6 +21,7 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -30,6 +31,9 @@
#include "JniConstants.h"
+#include <log/log.h>
+
+
jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage& ss, jint* port) {
// Convert IPv4-mapped IPv6 addresses to IPv4 addresses.
// The RI states "Java will never return an IPv4-mapped address".
@@ -199,6 +203,325 @@
return inetAddressToSockaddr(env, inetAddress, port, ss, sa_len, true);
}
+/*
+ * Fill msg_contrl data from structCmsghdr[]
+ */
+bool structCmsghdrArrayToMsgcontrol(JNIEnv* env, jobjectArray cmsgArray, struct msghdr& mhdr) {
+ struct cmsghdr *cm = NULL;
+ int i = 0;
+ jclass structCmsghdrClass = JniConstants::GetStructCmsghdrClass(env);
+ static jfieldID cmsgDataFid = env->GetFieldID(structCmsghdrClass, "cmsg_data", "[B");
+ if (!cmsgDataFid) {
+ return false;
+ }
+ static jfieldID cmsgLevelFid = env->GetFieldID(structCmsghdrClass, "cmsg_level", "I");
+ if (!cmsgLevelFid) {
+ return false;
+ }
+ static jfieldID cmsgTypeFid = env->GetFieldID(structCmsghdrClass, "cmsg_type", "I");
+ if (!cmsgTypeFid) {
+ return false;
+ }
+
+ int cmsgArrayize = env->GetArrayLength(cmsgArray);
+ if (!cmsgArrayize) {
+ // Return true since msg_control is optional parameter.
+ return true;
+ }
+
+ for (int i = 0; i < cmsgArrayize; ++i) {
+ ScopedLocalRef<jobject> cmsg(env, env->GetObjectArrayElement(cmsgArray, i));
+ ScopedLocalRef<jbyteArray> cmsgData(env, reinterpret_cast<jbyteArray>(
+ env->GetObjectField(cmsg.get(), cmsgDataFid)));
+
+ mhdr.msg_controllen += CMSG_SPACE(env->GetArrayLength(cmsgData.get()));
+ }
+
+ mhdr.msg_control = (unsigned char*)malloc(mhdr.msg_controllen);
+ if (mhdr.msg_control == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+ return false;
+ }
+ memset(mhdr.msg_control, 0, mhdr.msg_controllen);
+
+ // Loop over each cmsghdr header and set data.
+ for (cm = CMSG_FIRSTHDR(&mhdr), i = 0; (cm != NULL); cm = CMSG_NXTHDR(&mhdr, cm), ++i)
+ {
+ size_t data_len = 0;
+ ScopedLocalRef<jobject> cmsg(env, env->GetObjectArrayElement(cmsgArray, i));
+ ScopedLocalRef<jbyteArray> cmsgData(env, reinterpret_cast<jbyteArray>(
+ env->GetObjectField(cmsg.get(), cmsgDataFid)));
+
+ cm->cmsg_level = env->GetIntField(cmsg.get(), cmsgLevelFid);
+ cm->cmsg_type = env->GetIntField(cmsg.get(), cmsgTypeFid);
+ data_len = env->GetArrayLength(cmsgData.get());
+ cm->cmsg_len = CMSG_LEN(data_len);
+ env->GetByteArrayRegion(cmsgData.get(), 0,
+ data_len, reinterpret_cast<jbyte*>CMSG_DATA(cm));
+ }
+ return true;
+}
+
+/*
+ * Fill structCmsghdr[] data per msgcontrol data, used when recvmsg
+ */
+bool msgcontrolToStructCmsghdrArray(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr) {
+ struct cmsghdr *cm = NULL;
+ int i = 0;
+
+ static jfieldID msgControlFid = env->GetFieldID(JniConstants::GetStructMsghdrClass(env),
+ "msg_control", "[Landroid/system/StructCmsghdr;");
+ if (!msgControlFid) {
+ return false;
+ }
+
+ static jmethodID cmsgInitMid = env->GetMethodID(JniConstants::GetStructCmsghdrClass(env),
+ "<init>", "(II[B)V");
+ if (!cmsgInitMid) {
+ return false;
+ }
+
+ int cmsghdrNumber = 0;
+ for (cm = CMSG_FIRSTHDR(&mhdr); (cm != NULL); cm = CMSG_NXTHDR(&mhdr, cm)) {
+ cmsghdrNumber++;
+ }
+ if (!cmsghdrNumber)
+ return true;
+
+ jobjectArray structCmsghdrArray = env->NewObjectArray(cmsghdrNumber,
+ JniConstants::GetStructCmsghdrClass(env), NULL);
+ if (!structCmsghdrArray) {
+ return false;
+ }
+
+ // Loop over each cmsghdr header and set data.
+ for (cm = CMSG_FIRSTHDR(&mhdr),i=0; (cm!=NULL); cm = CMSG_NXTHDR(&mhdr, cm),i++) {
+ // copy out cmsg_data
+ ScopedLocalRef<jbyteArray> msgData(env,
+ env->NewByteArray(cm->cmsg_len - sizeof(struct cmsghdr)));
+ env->SetByteArrayRegion(msgData.get(),
+ 0,
+ env->GetArrayLength(msgData.get()),
+ reinterpret_cast<jbyte*>CMSG_DATA(cm));
+
+ ScopedLocalRef<jobject> objItem(env, env->NewObject(
+ JniConstants::GetStructCmsghdrClass(env),
+ cmsgInitMid, cm->cmsg_level, cm->cmsg_type, msgData.get()));
+
+ env->SetObjectArrayElement(structCmsghdrArray, i, objItem.get());
+ }
+
+ env->SetObjectField(structMsghdr, msgControlFid, structCmsghdrArray);
+
+ return true;
+}
+
+/*
+ * generate ScopedBytes object per ByteBuffer.isDirect
+ * if ByteBuffer.isDirect, generate ScopedBytes object by ByteBuffer itself;
+ * else, generate ScopedBytes object by ByteBuffer.array;
+ *
+ * Input: ByteBuffer object, isRW(R only or RW)
+ * Output: byte_len, length of the byte data per ByteBuffer.remaining;
+ * return value: pointer of new ScopedBytesRW or ScopedBytesRO
+ */
+static void* getScopedBytesFromByteBuffer(JNIEnv* env,
+ jobject byteBuffer, int& byteLen, bool isRW) {
+
+ jclass byteBufferClass = JniConstants::GetByteBufferClass(env);
+ static jmethodID isDirectMid = env->GetMethodID(byteBufferClass, "isDirect", "()Z");
+ static jmethodID remainingMid = env->GetMethodID(byteBufferClass, "remaining", "()I");
+ static jmethodID arrayMid = env->GetMethodID(byteBufferClass, "array", "()[B");
+
+ if (!isDirectMid || !remainingMid || !arrayMid) {
+ return NULL;
+ }
+
+ byteLen = env->CallIntMethod(byteBuffer, remainingMid);
+ bool isDirect = env->CallBooleanMethod(byteBuffer, isDirectMid);
+ jobject objBuff;
+ if (isDirect == true) {
+ objBuff = env->NewLocalRef(byteBuffer); // Add LocalRef to align with CallObjectMethod
+ } else {
+ // return array
+ objBuff = env->CallObjectMethod(byteBuffer, arrayMid);
+ }
+
+ if (isRW) {
+ return (void*)(new ScopedBytesRW(env, objBuff));
+ } else {
+ return (void*)(new ScopedBytesRO(env, objBuff));
+ }
+
+}
+
+/*
+ * Convert ByteBuffer[] to mhdr.msg_iov/msg_iovlen
+ */
+bool byteBufferArrayToIOV(JNIEnv* env, jobjectArray msgiovArray, struct msghdr& mhdr,
+ ScopedByteBufferArray& scopeBufArray) {
+ int msgIovArraySize = env->GetArrayLength(msgiovArray);
+ if (!msgIovArraySize) {
+ /* would not happen since msg_iov is marked as NonNull */
+ mhdr.msg_iov = NULL;
+ mhdr.msg_iovlen = 0;
+ }
+
+ struct iovec* iovarr = (struct iovec*)malloc(sizeof(iovec)*msgIovArraySize);
+ if (!iovarr) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+ return false;
+ }
+
+ if (scopeBufArray.initArray(msgIovArraySize) == false) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+ return false;
+ }
+
+ // Set memory of each msg_iov item by the original bytes address.
+ for (int i=0; i<msgIovArraySize; i++)
+ {
+ jobject msgiovItem = env->GetObjectArrayElement(msgiovArray, i);
+ int byteLen = 0;
+ void* ptr = getScopedBytesFromByteBuffer(env, msgiovItem, byteLen, scopeBufArray.isRW());
+ if (!ptr) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+ return false;
+ }
+
+ scopeBufArray.setArrayItem(i, ptr);
+
+ if (scopeBufArray.isRW()) {
+ iovarr[i].iov_base = (unsigned char*)(((ScopedBytesRW*)ptr)->get());
+ }
+ else {
+ iovarr[i].iov_base = (unsigned char*)(((ScopedBytesRO*)ptr)->get());
+ }
+
+ iovarr[i].iov_len = byteLen;
+ }
+
+ mhdr.msg_iov = iovarr;
+ mhdr.msg_iovlen = msgIovArraySize;
+
+ return true;
+}
+
+/*
+ * Function: convertStructMsghdrAndmsghdr
+ * Description: convert between Java#StructMsghdr and C#msghdr for sendmsg/recvmsg
+ *
+ * Function Parameters:
+ * StructMsghdr, input, StructMsghdr
+ * for sendmsg,
+ * StructMsghdr.msg_name input(mandatory),
+ * StructMsghdr.msg_iov iput(mandatory)
+ * StructMsghdr.msg_control input(optional)
+ * StructMsghdr.msg_flags input(mandatory)
+ * for recvmsg,
+ * StructMsghdr.msg_name input/output(optional),
+ * StructMsghdr.msg_iov input/output(mandatory)
+ * StructMsghdr.msg_control input/output(optional)
+ * StructMsghdr.msg_flags input
+ * mhdr, input, struct msghdr
+ * scopeBufArray, output, store buffer array of ScopedBytesRW or ScopedBytesRO
+ * isFromStructCMsghdrTomsghdr, input, indicate StructMsghdr->msghdr or msghdr->StructMsghdr
+ *
+ * then in sendmsg scenario, call sequence will be:
+ * 1. convert(StructMsg->msghdr)
+ * 2. sendmsg
+ * in recvmsg scenario, call sequence will be:
+ * 1. convert(StructMsg->msghdr)
+ * 2. recvmsg
+ * 3. convert again(msghdr->StructMsg)
+ */
+bool convertStructMsghdrAndmsghdr(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+ ScopedByteBufferArray& scopeBufArray,
+ bool isFromStructCMsghdrTomsghdr) {
+ if (!structMsghdr) {
+ jniThrowNullPointerException(env, "missing structMsghdr");
+ return false;
+ }
+
+ jclass StructMsghdrClass = JniConstants::GetStructMsghdrClass(env);
+
+ // Get fieldID of each item in StructMsghdr.
+ static jfieldID msgIovFid = env->GetFieldID(StructMsghdrClass,
+ "msg_iov",
+ "[Ljava/nio/ByteBuffer;");
+ if (!msgIovFid) {
+ return false;
+ }
+ static jfieldID msgControlFid = env->GetFieldID(StructMsghdrClass,
+ "msg_control",
+ "[Landroid/system/StructCmsghdr;");
+ if (!msgControlFid) {
+ return false;
+ }
+ static jfieldID msgFlagsFid = env->GetFieldID(StructMsghdrClass,
+ "msg_flags",
+ "I");
+ if (!msgFlagsFid) {
+ return false;
+ }
+
+ if (isFromStructCMsghdrTomsghdr) {
+ // Pick StructMsghdr.msg_iov[].
+ jobjectArray msgIovArray = reinterpret_cast<jobjectArray>(
+ env->GetObjectField(structMsghdr, msgIovFid));
+ if (!msgIovArray) {
+ jniThrowNullPointerException(env, "null StructMsghdr.msg_iov");
+ return false;
+ }
+ // In case sendmsg, IOV buffer are RO to send data,
+ // in case recvmsg, IOV buffer are RW to store received data.
+ if (byteBufferArrayToIOV(env, msgIovArray, mhdr, scopeBufArray) == false) {
+ return false;
+ }
+
+ if (!scopeBufArray.isRW()) {
+ jobjectArray structCmsghdrObjArray = reinterpret_cast<jobjectArray>(
+ env->GetObjectField(structMsghdr, msgControlFid));
+ if (structCmsghdrObjArray != NULL) {
+ // convert StrucCmsg[] <-> msghdr.msgcontrl
+ if (structCmsghdrArrayToMsgcontrol(env, structCmsghdrObjArray, mhdr) == false) {
+ return false;
+ }
+ }
+ } else {
+ // hardcode 512 for recvmsg/msg_controllen, it should be enough for recvmsg
+ mhdr.msg_controllen = 512;
+ mhdr.msg_control = (unsigned char*)malloc(mhdr.msg_controllen);
+ }
+
+ mhdr.msg_flags = env->GetIntField(structMsghdr, msgFlagsFid);
+ } else {
+ // StructMsghdr.msg_iov[]/msg_control[] are output paramenter.
+ // StructMsghdr.msg_iov[] data are already updated by recvmsg syscall directly.
+ // StructMsghdr.msg_control[] are set below.
+ if (msgcontrolToStructCmsghdrArray(env, structMsghdr, mhdr) == false)
+ return false;
+ env->SetIntField(structMsghdr, msgFlagsFid, mhdr.msg_flags);
+ }
+
+ return true;
+
+}
+
+// Convert Java StructMsghdr to C msghdr.
+bool msghdrJavaToC(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+ ScopedByteBufferArray& scopedBufArray) {
+ return convertStructMsghdrAndmsghdr(env, structMsghdr, mhdr,
+ scopedBufArray, true);
+}
+
+// Convert C msghdr to Java StructMsghdr.
+bool msghdrCToJava(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+ ScopedByteBufferArray& scopedBufArray) {
+ return convertStructMsghdrAndmsghdr(env, structMsghdr, mhdr,
+ scopedBufArray, false);
+}
+
bool setBlocking(int fd, bool blocking) {
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
diff --git a/luni/src/main/native/NetworkUtilities.h b/luni/src/main/native/NetworkUtilities.h
old mode 100644
new mode 100755
index db1ad1f..da35036
--- a/luni/src/main/native/NetworkUtilities.h
+++ b/luni/src/main/native/NetworkUtilities.h
@@ -16,6 +16,7 @@
#include "jni.h"
#include <sys/socket.h>
+#include "ScopedByteBufferArray.h"
// Convert from sockaddr_storage to Inet4Address (AF_INET) or Inet6Address (AF_INET6).
// If 'port' is non-NULL and the address family includes a notion
@@ -38,7 +39,15 @@
bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port,
sockaddr_storage& ss, socklen_t& sa_len);
+// Convert from StructMsghdr to msghdr,
+// set all fields except msg_name which would be set outside since IPv4 fallback handled outside
+bool msghdrJavaToC(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+ ScopedByteBufferArray& scopedBufArray);
+// Convert from msghdr to StructMsghdr,
+// msg_iov/msg_control need to be set since they are output parameters;
+bool msghdrCToJava(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+ ScopedByteBufferArray& scopedBufArray);
// Changes 'fd' to be blocking/non-blocking. Returns false and sets errno on failure.
// @Deprecated - use IoUtils.setBlocking
diff --git a/luni/src/main/native/ScopedByteBufferArray.h b/luni/src/main/native/ScopedByteBufferArray.h
new file mode 100755
index 0000000..7b21845
--- /dev/null
+++ b/luni/src/main/native/ScopedByteBufferArray.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * *Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * *Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * *Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+
+#include <nativehelper/ScopedLocalRef.h>
+#include "ScopedBytes.h"
+
+/**
+ * ScopedByteBufferArray manages the dynamic buffer array of ScopedBytesRW/ScopedBytesRO.
+ */
+class ScopedByteBufferArray {
+public:
+ ScopedByteBufferArray(JNIEnv* env, int isRW)
+ : mEnv(env), mIsRW(isRW)
+ {
+ mArrayPtr = NULL;
+ mArraySize = 0;
+ }
+
+ ~ScopedByteBufferArray() {
+ if(!mArrayPtr) {
+ return;
+ }
+
+ // Loop over arrary and release memory.
+ for (int i = 0; i < mArraySize; ++i) {
+ if (!mArrayPtr[i])
+ continue;
+
+ if (mIsRW) {
+ jobject tmp = ((ScopedBytesRW*)mArrayPtr[i])->getObject();
+ delete (ScopedBytesRW*)mArrayPtr[i];
+ mEnv->DeleteLocalRef(tmp);
+ } else {
+ jobject tmp = ((ScopedBytesRO*)mArrayPtr[i])->getObject();
+ delete (ScopedBytesRO*)mArrayPtr[i];
+ mEnv->DeleteLocalRef(tmp);
+ }
+ }
+ delete[] mArrayPtr;
+ }
+
+ bool initArray(int size) {
+ if (mArrayPtr) {
+ return false;
+ }
+
+ mArraySize = size;
+
+ if (mIsRW) {
+ mArrayPtr = (void**)(new ScopedBytesRW*[size]);
+ }
+ else {
+ mArrayPtr = (void**)(new ScopedBytesRO*[size]);
+ }
+
+ if (!mArrayPtr) {
+ return false;
+ }
+
+ for (int i=0; i<size; ++i) {
+ mArrayPtr[i] = 0;
+ }
+
+ return true;
+ }
+
+ bool isRW() const {
+ return mIsRW;
+ }
+
+ bool setArrayItem(int itemNo, void* item) {
+ if (itemNo >= mArraySize || itemNo < 0) {
+ return false;
+ }
+ mArrayPtr[itemNo] = item;
+ return true;
+ }
+
+private:
+ JNIEnv* const mEnv;
+ int mIsRW;
+ int mArraySize;
+ void** mArrayPtr;
+};
diff --git a/luni/src/main/native/ScopedBytes.h b/luni/src/main/native/ScopedBytes.h
index 5258a57..951ef4b 100644
--- a/luni/src/main/native/ScopedBytes.h
+++ b/luni/src/main/native/ScopedBytes.h
@@ -51,6 +51,10 @@
}
}
+ jobject getObject() {
+ return mObject;
+ }
+
private:
JNIEnv* const mEnv;
const jobject mObject;
diff --git a/luni/src/main/native/ScopedMsghdr.h b/luni/src/main/native/ScopedMsghdr.h
new file mode 100755
index 0000000..e64077f
--- /dev/null
+++ b/luni/src/main/native/ScopedMsghdr.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * *Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * *Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * *Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+
+#include <nativehelper/JNIHelp.h>
+
+#include "JniConstants.h"
+#include "ScopedBytes.h"
+
+/**
+ * Wrapper for managing the name and length of msghdr.
+ */
+class ScopedMsghdr {
+public:
+ ScopedMsghdr() {
+ }
+
+ ~ScopedMsghdr() {
+ if (mMsghdrValue.msg_iov)
+ free(mMsghdrValue.msg_iov);
+ if (mMsghdrValue.msg_control)
+ free(mMsghdrValue.msg_control);
+ }
+
+ struct msghdr& getObject() {
+ return mMsghdrValue;
+ }
+
+ void setMsgNameAndLen(sockaddr* ss, socklen_t sa_len) {
+ mMsghdrValue.msg_name = ss;
+ mMsghdrValue.msg_namelen = sa_len;
+ }
+
+ bool isNameLenValid() const {
+ if(mMsghdrValue.msg_name == NULL) {
+ return false;
+ }
+ if ((mMsghdrValue.msg_namelen != sizeof(sockaddr_in6)) &&
+ (mMsghdrValue.msg_namelen != sizeof(sockaddr_in))) {
+ return false;
+ }
+ return true;
+ }
+
+private:
+ struct msghdr mMsghdrValue = {};
+
+};
+
+
diff --git a/luni/src/main/native/libcore_io_Linux.cpp b/luni/src/main/native/libcore_io_Linux.cpp
old mode 100644
new mode 100755
index 9b82b48..23c1742
--- a/luni/src/main/native/libcore_io_Linux.cpp
+++ b/luni/src/main/native/libcore_io_Linux.cpp
@@ -73,6 +73,9 @@
#include "NetworkUtilities.h"
#include "Portability.h"
#include "ScopedBytes.h"
+#include "ScopedByteBufferArray.h"
+#include "ScopedMsghdr.h"
+
#ifndef __unused
#define __unused __attribute__((__unused__))
@@ -2134,6 +2137,62 @@
return recvCount;
}
+static jint Linux_recvmsg(JNIEnv* env, jobject, jobject javaFd, jobject structMsghdr, jint flags) {
+ ssize_t rc = -1;
+ ScopedMsghdr scopedMsghdrValue;
+ ScopedByteBufferArray scopedBytesArray(env, true);
+ sockaddr_storage ss = {};
+
+ static jfieldID msgNameFid = env->GetFieldID(JniConstants::GetStructMsghdrClass(env),
+ "msg_name", "Ljava/net/SocketAddress;");
+ if (!msgNameFid) {
+ return -1;
+ }
+
+ // Initialize msghdr with everything from StructCMsghdr except msg_name.
+ if (msghdrJavaToC(env, structMsghdr, scopedMsghdrValue.getObject(),
+ scopedBytesArray) == false) {
+ return -1;
+ }
+
+ jobject javaSocketAddress = env->GetObjectField(structMsghdr, msgNameFid);
+ if (javaSocketAddress) {
+ // client want to get source address, then set msg_name and msg_namelen.
+ scopedMsghdrValue.setMsgNameAndLen(reinterpret_cast<sockaddr*>(&ss),
+ sizeof(sockaddr_in6));
+ }
+
+ rc = NET_FAILURE_RETRY(env, ssize_t, recvmsg, javaFd, \
+ &scopedMsghdrValue.getObject(), flags);
+
+ if (rc < 0) {
+ return rc;
+ }
+
+ if (javaSocketAddress) {
+ sockaddr_storage* interfaceAddr = NULL;
+ if (!scopedMsghdrValue.isNameLenValid()) {
+ jniThrowException(env, "java/net/SocketException",
+ "unknown socket address type");
+ return -1;
+ }
+
+ interfaceAddr = reinterpret_cast<sockaddr_storage*>(scopedMsghdrValue.getObject().msg_name);
+ if (fillSocketAddress(env, javaSocketAddress, *interfaceAddr,
+ scopedMsghdrValue.getObject().msg_namelen) == false) {
+ return -1;
+ }
+ }
+
+ if (msghdrCToJava(env, structMsghdr, scopedMsghdrValue.getObject(),
+ scopedBytesArray) == false) {
+ return -1;
+ }
+
+ return rc;
+}
+
+
static void Linux_remove(JNIEnv* env, jobject, jstring javaPath) {
ScopedUtfChars path(env, javaPath);
if (path.c_str() == NULL) {
@@ -2190,6 +2249,66 @@
return result;
}
+static jint Linux_sendmsg(JNIEnv* env, jobject, jobject javaFd, jobject structMsghdr, jint flags) {
+
+ ssize_t rc = -1;
+ ScopedMsghdr scopedMsghdrValue = {};
+ ScopedByteBufferArray scopedBytesArray(env, false);
+ static jfieldID msgNameFid = env->GetFieldID(JniConstants::GetStructMsghdrClass(env),
+ "msg_name",
+ "Ljava/net/SocketAddress;");
+ if (!msgNameFid) {
+ return -1;
+ }
+
+ jobject sockAddrObj = env->GetObjectField(structMsghdr, msgNameFid);
+
+ // Initialize structMsghdr with everything from C msghdr except msg_name.
+ if (msghdrJavaToC(env, structMsghdr, scopedMsghdrValue.getObject(),
+ scopedBytesArray) == false) {
+ return -1;
+ }
+
+ sockaddr_storage ss = {};
+ socklen_t sa_len = 0;
+ if (sockAddrObj && javaSocketAddressToSockaddr(env, sockAddrObj, ss, sa_len) == false) {
+ return -1;
+ }
+
+ sockaddr* _sa = sa_len ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+ scopedMsghdrValue.setMsgNameAndLen(_sa, sa_len);
+ rc = NET_FAILURE_RETRY(env, ssize_t, sendmsg, javaFd, \
+ &(scopedMsghdrValue.getObject()), flags);
+
+ if (sockAddrObj &&
+ !env->IsInstanceOf(sockAddrObj, JniConstants::GetInetSocketAddressClass(env))) {
+ // non InetSockAddress case, return now;
+ return rc;
+ }
+
+ // InetSocket and IPv6 didn't work, fallback to IPv4.
+ if (rc == -1 && errno == EAFNOSUPPORT && sa_len && isIPv4MappedAddress(_sa)) {
+ env->ExceptionClear();
+
+ jobject javaInetAddress;
+ jint port = 0;
+ javaInetSocketAddressToInetAddressAndPort(env, sockAddrObj, javaInetAddress, port);
+
+ // Get IPv4 sockaddr.
+ if (!inetAddressToSockaddrVerbatim(env, javaInetAddress, port, ss, sa_len)) {
+ return rc;
+ }
+
+ // Use IPv4 msghdr.msg_name.
+ _sa = sa_len ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+ scopedMsghdrValue.setMsgNameAndLen(_sa, sa_len);
+ rc = NET_FAILURE_RETRY(env, ssize_t, sendmsg, javaFd, \
+ &(scopedMsghdrValue.getObject()), flags);
+ }
+
+ return rc;
+}
+
static jint Linux_sendtoBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetAddress, jint port) {
ScopedBytesRO bytes(env, javaBytes);
if (bytes.get() == NULL) {
@@ -2669,10 +2788,12 @@
NATIVE_METHOD(Linux, realpath, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(Linux, readv, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"),
NATIVE_METHOD(Linux, recvfromBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetSocketAddress;)I"),
+ NATIVE_METHOD(Linux, recvmsg, "(Ljava/io/FileDescriptor;Landroid/system/StructMsghdr;I)I"),
NATIVE_METHOD(Linux, remove, "(Ljava/lang/String;)V"),
NATIVE_METHOD(Linux, removexattr, "(Ljava/lang/String;Ljava/lang/String;)V"),
NATIVE_METHOD(Linux, rename, "(Ljava/lang/String;Ljava/lang/String;)V"),
NATIVE_METHOD(Linux, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Landroid/system/Int64Ref;J)J"),
+ NATIVE_METHOD(Linux, sendmsg, "(Ljava/io/FileDescriptor;Landroid/system/StructMsghdr;I)I"),
NATIVE_METHOD(Linux, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetAddress;I)I"),
NATIVE_METHOD_OVERLOAD(Linux, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/SocketAddress;)I", SocketAddress),
NATIVE_METHOD(Linux, setegid, "(I)V"),
diff --git a/luni/src/test/java/libcore/android/system/OsTest.java b/luni/src/test/java/libcore/android/system/OsTest.java
old mode 100644
new mode 100755
index 6d1a135..67b92f8
--- a/luni/src/test/java/libcore/android/system/OsTest.java
+++ b/luni/src/test/java/libcore/android/system/OsTest.java
@@ -22,6 +22,8 @@
import android.system.Os;
import android.system.OsConstants;
import android.system.PacketSocketAddress;
+import android.system.StructCmsghdr;
+import android.system.StructMsghdr;
import android.system.StructRlimit;
import android.system.StructStat;
import android.system.StructTimeval;
@@ -45,6 +47,7 @@
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Arrays;
@@ -734,6 +737,241 @@
checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
}
+ /*
+ * Test case for sendmsg with/without GSO in loopback iface,
+ * recvmsg/gro would not happen since in loopback
+ */
+ private void checkSendmsgSocketAddress(int family, InetSocketAddress loopbackAddr,
+ StructMsghdr sendmsgHdr, StructMsghdr recvmsgHdr, int sendSize) throws Exception {
+
+ FileDescriptor sendFd = Os.socket(family, SOCK_DGRAM, 0);
+ FileDescriptor recvFd = Os.socket(family, SOCK_DGRAM, 0);
+ int rc = 0;
+
+ //recvmsg cleanup data
+ if (loopbackAddr.getAddress() instanceof Inet6Address) {
+ Os.bind(recvFd, Inet6Address.ANY, loopbackAddr.getPort());
+ } else {
+ Os.bind(recvFd, Inet4Address.ANY, loopbackAddr.getPort());
+ }
+
+ StructTimeval tv = StructTimeval.fromMillis(20);
+ Os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
+ Os.setsockoptInt(recvFd, IPPROTO_UDP, UDP_GRO, 1); //enable GRO
+ Os.setsockoptInt(recvFd, SOL_SOCKET, SO_RCVBUF, 1024 * 1024);
+
+ try {
+ assertEquals(sendSize, Os.sendmsg(sendFd, sendmsgHdr, 0));
+ rc = 0;
+ do {
+ int temp_rc = Os.recvmsg(recvFd, recvmsgHdr, OsConstants.MSG_TRUNC);
+ rc += temp_rc;
+ if (recvmsgHdr.msg_control != null && recvmsgHdr.msg_control.length > 0) {
+ byte[] sendCmsgByte = sendmsgHdr.msg_control[0].cmsg_data;
+ byte[] recvCmsgByte = recvmsgHdr.msg_control[0].cmsg_data;
+ /* Note:
+ * GSO: is set with Short(2Byte) values;
+ * GRO: IP stack return with Int(4Bytes) value;
+ */
+ assertEquals(
+ ByteBuffer.wrap(sendCmsgByte).order(
+ ByteOrder.nativeOrder()).getShort(0),
+ ByteBuffer.wrap(recvCmsgByte).order(
+ ByteOrder.nativeOrder()).getInt(0));
+ }
+
+ recvmsgHdr = new StructMsghdr(recvmsgHdr.msg_name, recvmsgHdr.msg_iov,
+ null,
+ recvmsgHdr.msg_flags);
+ }while(rc < sendSize);
+ } finally {
+ Os.close(sendFd);
+ Os.close(recvFd);
+ }
+ }
+
+ @Test
+ public void test_sendmsg_af_inet_4K() throws Exception {
+ InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+ 10234);
+ StructCmsghdr[] cmsg = new StructCmsghdr[1];
+ cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+ //sendmsg/recvmsg with 1*4K ByteBuffer
+ ByteBuffer[] bufferArray = new ByteBuffer[1];
+ ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+ bufferArray[0] = ByteBuffer.allocate(4096);
+ bufferArrayRecv[0] = ByteBuffer.allocate(4096);
+
+ StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+ bufferArray,
+ cmsg, 0);
+ StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+ bufferArrayRecv,
+ null, 0);
+
+ checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096);
+ }
+
+ @Test
+ public void test_sendmsg_af_inet6_4K() throws Exception {
+ InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("::1"), 10234);
+ StructCmsghdr[] cmsg = new StructCmsghdr[1];
+ cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+ //sendmsg/recvmsg with 1*4K ByteBuffer
+ ByteBuffer[] bufferArray = new ByteBuffer[1];
+ ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+ bufferArray[0] = ByteBuffer.allocate(4096);
+ bufferArrayRecv[0] = ByteBuffer.allocate(4096);
+
+ StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+ bufferArray,
+ cmsg, 0);
+ StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+ bufferArrayRecv,
+ null, 0);
+
+ checkSendmsgSocketAddress(AF_INET6, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096);
+ }
+
+ @Test
+ public void test_sendmsg_af_inet6_4K_directBuffer() throws Exception {
+ InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+ 10234);
+ StructCmsghdr[] cmsg = new StructCmsghdr[1];
+ cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+ //sendmsg/recvmsg with 1*4K ByteBuffer
+ ByteBuffer[] bufferArray = new ByteBuffer[1];
+ ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+ bufferArray[0] = ByteBuffer.allocateDirect(4096); // DirectBuffer
+ bufferArrayRecv[0] = ByteBuffer.allocateDirect(4096); // DirectBuffer
+
+ StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+ bufferArray,
+ cmsg, 0);
+ StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+ bufferArrayRecv,
+ null, 0);
+
+ checkSendmsgSocketAddress(AF_INET6, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096);
+ }
+
+ @Test
+ public void test_sendmsg_af_inet_16K_recvparts() throws Exception {
+ InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+ 10234);
+ StructCmsghdr[] cmsg = new StructCmsghdr[1];
+ cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+ //sendmsg with 4*4K ByteBuffer, recv with 1*4K ByteBuffer(already with MSG_TRUNC option)
+ ByteBuffer[] bufferArray = new ByteBuffer[4];
+ ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+ bufferArray[0] = ByteBuffer.allocate(4096);
+ bufferArray[1] = ByteBuffer.allocate(4096);
+ bufferArray[2] = ByteBuffer.allocate(4096);
+ bufferArray[3] = ByteBuffer.allocate(4096);
+ bufferArrayRecv[0] = ByteBuffer.allocate(4096); //receive only part of data
+
+ StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+ bufferArray,
+ cmsg, 0);
+ StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+ bufferArrayRecv,
+ null, 0);
+
+ checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+ }
+
+ @Test
+ public void test_sendmsg_af_inet_16K_reciveall() throws Exception {
+ InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+ 10234);
+ StructCmsghdr[] cmsg = new StructCmsghdr[1];
+ cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+ // Create sendmsg/recvmsg with 4*4K ByteBuffer
+ ByteBuffer[] bufferArray = new ByteBuffer[4];
+ bufferArray[0] = ByteBuffer.allocate(4096);
+ bufferArray[1] = ByteBuffer.allocate(4096);
+ bufferArray[2] = ByteBuffer.allocate(4096);
+ bufferArray[3] = ByteBuffer.allocate(4096);
+
+ StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr, bufferArray, cmsg, 0);
+ StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(), bufferArray, null, 0);
+
+ checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+ }
+
+ @Test
+ public void test_sendmsg_af_inet_16K_reciveall_without_recv_msgname() throws Exception {
+ InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+ 10234);
+ StructCmsghdr[] cmsg = new StructCmsghdr[1];
+ cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+ // Create sendmsg/recvmsg with 4*4K ByteBuffer
+ ByteBuffer[] bufferArray = new ByteBuffer[4];
+ bufferArray[0] = ByteBuffer.allocate(4096);
+ bufferArray[1] = ByteBuffer.allocate(4096);
+ bufferArray[2] = ByteBuffer.allocate(4096);
+ bufferArray[3] = ByteBuffer.allocate(4096);
+
+ StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr, bufferArray, cmsg, 0);
+ // msg_name is unnecessary.
+ StructMsghdr recvmsgHdr = new StructMsghdr(null, bufferArray, null, 0);
+
+ checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+ }
+
+ @Test
+ public void test_sendmsg_af_inet_16K_without_send_msgcontrl() throws Exception {
+ InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+ 10234);
+
+ // Create sendmsg/recvmsg with 4*4K ByteBuffer
+ ByteBuffer[] bufferArray = new ByteBuffer[4];
+ bufferArray[0] = ByteBuffer.allocate(4096);
+ bufferArray[1] = ByteBuffer.allocate(4096);
+ bufferArray[2] = ByteBuffer.allocate(4096);
+ bufferArray[3] = ByteBuffer.allocate(4096);
+
+ // GSO will not happen without msgcontrol.
+ StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr, bufferArray, null, 0);
+ StructMsghdr recvmsgHdr = new StructMsghdr(null, bufferArray, null, 0);
+
+ checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+ }
+
+ @Test
+ public void test_sendmsg_af_inet_abnormal() throws Exception {
+ //sendmsg socket set
+ InetSocketAddress address = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+ 10234);
+ FileDescriptor sendFd = Os.socket(AF_INET, SOCK_DGRAM, 0);
+
+ ByteBuffer[] bufferArray = new ByteBuffer[1];
+ bufferArray[0] = ByteBuffer.allocate(8192);
+
+ try {
+ StructMsghdr msgHdr = new StructMsghdr(address, null, null, 0);
+ Os.sendmsg(sendFd, msgHdr, 0);
+ fail("Expected NullPointerException due to invalid StructMsghdr.msg_iov(NULL)");
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ StructMsghdr msgHdr = new StructMsghdr(null, bufferArray, null, 0);
+ Os.sendmsg(sendFd, msgHdr, 0);
+ fail("Expected ErrnoException due to invalid StructMsghdr.msg_name(NULL)");
+ } catch (ErrnoException expected) {
+ assertEquals("Expected EDESTADDRREQ binding IPv4 socket to ::", EDESTADDRREQ,
+ expected.errno);
+ }
+
+ }
+
@Test
public void test_socketFamilies() throws Exception {
FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
diff --git a/non_openjdk_java_files.bp b/non_openjdk_java_files.bp
old mode 100644
new mode 100755
index b40556c..7929e62
--- a/non_openjdk_java_files.bp
+++ b/non_openjdk_java_files.bp
@@ -140,9 +140,11 @@
"luni/src/main/java/android/system/StructAddrinfo.java",
"luni/src/main/java/android/system/StructCapUserData.java",
"luni/src/main/java/android/system/StructCapUserHeader.java",
+ "luni/src/main/java/android/system/StructCmsghdr.java",
"luni/src/main/java/android/system/StructGroupReq.java",
"luni/src/main/java/android/system/StructIfaddrs.java",
"luni/src/main/java/android/system/StructLinger.java",
+ "luni/src/main/java/android/system/StructMsghdr.java",
"luni/src/main/java/android/system/StructPasswd.java",
"luni/src/main/java/android/system/StructPollfd.java",
"luni/src/main/java/android/system/StructRlimit.java",