From 914dca36eec8fed05d9c07523425a2411cfa60cc Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Thu, 18 Feb 2021 00:03:53 +0900 Subject: Move SocketUtils out of the connectivity module SocketUtils contains system APIs for modules to interact for sockets, wrapping internal APIs. It should be part of the platform to keep access to the internal APIs. This involves splitting NetworkUtils.protectVpn to NetworkUtilsInternal, since SocketUtils and VpnService are the only users of that method. The @UnsupportedAppUsage NetworkUtils.protectVpn has low usage count, and is already available through VpnService.protect. Bug: 181512874 Test: boots, VPN working Change-Id: I7028d334975f7536c06afac7a22200c33db707ac --- core/api/system-current.txt | 13 + core/java/android/net/VpnService.java | 3 +- core/java/android/net/util/SocketUtils.java | 122 +++++++++ .../android/internal/net/NetworkUtilsInternal.java | 16 ++ core/jni/Android.bp | 2 +- core/jni/android_net_NetUtils.cpp | 299 --------------------- core/jni/android_net_NetworkUtils.cpp | 288 ++++++++++++++++++++ ...m_android_internal_net_NetworkUtilsInternal.cpp | 13 + .../Connectivity/framework/api/system-current.txt | 13 - .../framework/src/android/net/NetworkUtils.java | 16 -- .../src/android/net/util/SocketUtils.java | 121 --------- 11 files changed, 455 insertions(+), 451 deletions(-) create mode 100644 core/java/android/net/util/SocketUtils.java delete mode 100644 core/jni/android_net_NetUtils.cpp create mode 100644 core/jni/android_net_NetworkUtils.cpp delete mode 100644 packages/Connectivity/framework/src/android/net/util/SocketUtils.java diff --git a/core/api/system-current.txt b/core/api/system-current.txt index afdf3356201d..302a92a674c4 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -6449,6 +6449,19 @@ package android.net.sip { } +package android.net.util { + + public final class SocketUtils { + method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException; + method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException; + method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); + method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int); + method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); + method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]); + } + +} + package android.net.vcn { public class VcnManager { diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index f90fbaf1e0fb..fa3ff8a26862 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -41,6 +41,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import com.android.internal.net.NetworkUtilsInternal; import com.android.internal.net.VpnConfig; import java.net.DatagramSocket; @@ -254,7 +255,7 @@ public class VpnService extends Service { * @return {@code true} on success. */ public boolean protect(int socket) { - return NetworkUtils.protectFromVpn(socket); + return NetworkUtilsInternal.protectFromVpn(socket); } /** diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java new file mode 100644 index 000000000000..69edc757ce8a --- /dev/null +++ b/core/java/android/net/util/SocketUtils.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 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 android.net.util; + +import static android.system.OsConstants.SOL_SOCKET; +import static android.system.OsConstants.SO_BINDTODEVICE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.system.ErrnoException; +import android.system.NetlinkSocketAddress; +import android.system.Os; +import android.system.PacketSocketAddress; + +import com.android.internal.net.NetworkUtilsInternal; + +import libcore.io.IoBridge; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.SocketAddress; + +/** + * Collection of utilities to interact with raw sockets. + * @hide + */ +@SystemApi +public final class SocketUtils { + /** + * Create a raw datagram socket that is bound to an interface. + * + *

Data sent through the socket will go directly to the underlying network, ignoring VPNs. + */ + public static void bindSocketToInterface(@NonNull FileDescriptor socket, @NonNull String iface) + throws ErrnoException { + // SO_BINDTODEVICE actually takes a string. This works because the first member + // of struct ifreq is a NULL-terminated interface name. + // TODO: add a setsockoptString() + Os.setsockoptIfreq(socket, SOL_SOCKET, SO_BINDTODEVICE, iface); + NetworkUtilsInternal.protectFromVpn(socket); + } + + /** + * Make a socket address to communicate with netlink. + */ + @NonNull + public static SocketAddress makeNetlinkSocketAddress(int portId, int groupsMask) { + return new NetlinkSocketAddress(portId, groupsMask); + } + + /** + * Make socket address that packet sockets can bind to. + * + * @param protocol the layer 2 protocol of the packets to receive. One of the {@code ETH_P_*} + * constants in {@link android.system.OsConstants}. + * @param ifIndex the interface index on which packets will be received. + */ + @NonNull + public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex) { + return new PacketSocketAddress( + protocol /* sll_protocol */, + ifIndex /* sll_ifindex */, + null /* sll_addr */); + } + + /** + * Make a socket address that packet socket can send packets to. + * @deprecated Use {@link #makePacketSocketAddress(int, int, byte[])} instead. + * + * @param ifIndex the interface index on which packets will be sent. + * @param hwAddr the hardware address to which packets will be sent. + */ + @Deprecated + @NonNull + public static SocketAddress makePacketSocketAddress(int ifIndex, @NonNull byte[] hwAddr) { + return new PacketSocketAddress( + 0 /* sll_protocol */, + ifIndex /* sll_ifindex */, + hwAddr /* sll_addr */); + } + + /** + * Make a socket address that a packet socket can send packets to. + * + * @param protocol the layer 2 protocol of the packets to send. One of the {@code ETH_P_*} + * constants in {@link android.system.OsConstants}. + * @param ifIndex the interface index on which packets will be sent. + * @param hwAddr the hardware address to which packets will be sent. + */ + @NonNull + public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex, + @NonNull byte[] hwAddr) { + return new PacketSocketAddress( + protocol /* sll_protocol */, + ifIndex /* sll_ifindex */, + hwAddr /* sll_addr */); + } + + /** + * @see IoBridge#closeAndSignalBlockedThreads(FileDescriptor) + */ + public static void closeSocket(@Nullable FileDescriptor fd) throws IOException { + IoBridge.closeAndSignalBlockedThreads(fd); + } + + private SocketUtils() {} +} diff --git a/core/java/com/android/internal/net/NetworkUtilsInternal.java b/core/java/com/android/internal/net/NetworkUtilsInternal.java index 571d7e721094..052959abff69 100644 --- a/core/java/com/android/internal/net/NetworkUtilsInternal.java +++ b/core/java/com/android/internal/net/NetworkUtilsInternal.java @@ -22,6 +22,8 @@ import static android.system.OsConstants.AF_INET6; import android.annotation.NonNull; import android.system.Os; +import java.io.FileDescriptor; + /** @hide */ public class NetworkUtilsInternal { @@ -35,6 +37,20 @@ public class NetworkUtilsInternal { */ public static native void setAllowNetworkingForProcess(boolean allowNetworking); + /** + * Protect {@code fd} from VPN connections. After protecting, data sent through + * this socket will go directly to the underlying network, so its traffic will not be + * forwarded through the VPN. + */ + public static native boolean protectFromVpn(FileDescriptor fd); + + /** + * Protect {@code socketfd} from VPN connections. After protecting, data sent through + * this socket will go directly to the underlying network, so its traffic will not be + * forwarded through the VPN. + */ + public static native boolean protectFromVpn(int socketfd); + /** * Returns true if the hostname is weakly validated. * @param hostname Name of host to validate. diff --git a/core/jni/Android.bp b/core/jni/Android.bp index e58ad79de89e..afd19b63bc71 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -149,7 +149,7 @@ cc_library_shared { "android_os_VintfRuntimeInfo.cpp", "android_os_incremental_IncrementalManager.cpp", "android_net_LocalSocketImpl.cpp", - "android_net_NetUtils.cpp", + "android_net_NetworkUtils.cpp", "android_service_DataLoaderService.cpp", "android_util_AssetManager.cpp", "android_util_Binder.cpp", diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp deleted file mode 100644 index e2af87ee1adf..000000000000 --- a/core/jni/android_net_NetUtils.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright 2020, 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. - */ - -#define LOG_TAG "NetUtils" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // NETID_USE_LOCAL_NAMESERVERS -#include -#include -#include -#include -#include - -#include "NetdClient.h" -#include "core_jni_helpers.h" -#include "jni.h" - -extern "C" { -int ifc_enable(const char *ifname); -int ifc_disable(const char *ifname); -} - -#define NETUTILS_PKG_NAME "android/net/NetworkUtils" - -namespace android { - -constexpr int MAXPACKETSIZE = 8 * 1024; -// FrameworkListener limits the size of commands to 4096 bytes. -constexpr int MAXCMDSIZE = 4096; - -static void throwErrnoException(JNIEnv* env, const char* functionName, int error) { - ScopedLocalRef detailMessage(env, env->NewStringUTF(functionName)); - if (detailMessage.get() == NULL) { - // Not really much we can do here. We're probably dead in the water, - // but let's try to stumble on... - env->ExceptionClear(); - } - static jclass errnoExceptionClass = - MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException")); - - static jmethodID errnoExceptionCtor = - GetMethodIDOrDie(env, errnoExceptionClass, - "", "(Ljava/lang/String;I)V"); - - jobject exception = env->NewObject(errnoExceptionClass, - errnoExceptionCtor, - detailMessage.get(), - error); - env->Throw(reinterpret_cast(exception)); -} - -static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd) -{ - struct sock_filter filter_code[] = { - // Reject all. - BPF_STMT(BPF_RET | BPF_K, 0) - }; - struct sock_fprog filter = { - sizeof(filter_code) / sizeof(filter_code[0]), - filter_code, - }; - - int fd = AFileDescriptor_getFD(env, javaFd); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); - } -} - -static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd) -{ - int optval_ignored = 0; - int fd = AFileDescriptor_getFD(env, javaFd); - if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) != - 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(SO_DETACH_FILTER): %s", strerror(errno)); - } -} - -static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId) -{ - return (jboolean) !setNetworkForProcess(netId); -} - -static jint android_net_utils_getBoundNetworkForProcess(JNIEnv *env, jobject thiz) -{ - return getNetworkForProcess(); -} - -static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz, - jint netId) -{ - return (jboolean) !setNetworkForResolv(netId); -} - -static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd, - jint netId) { - return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd)); -} - -static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket) -{ - return (jboolean) !protectFromVpn(socket); -} - -static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) { - return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd)); -} - -static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId) -{ - return (jboolean) !queryUserAccess(uid, netId); -} - -static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst) -{ - if (env->GetArrayLength(addr) != len) { - return false; - } - env->GetByteArrayRegion(addr, 0, len, reinterpret_cast(dst)); - return true; -} - -static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jint netId, - jstring dname, jint ns_class, jint ns_type, jint flags) { - const jsize javaCharsCount = env->GetStringLength(dname); - const jsize byteCountUTF8 = env->GetStringUTFLength(dname); - - // Only allow dname which could be simply formatted to UTF8. - // In native layer, res_mkquery would re-format the input char array to packet. - std::vector queryname(byteCountUTF8 + 1, 0); - - env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname.data()); - int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags); - - if (fd < 0) { - throwErrnoException(env, "resNetworkQuery", -fd); - return nullptr; - } - - return jniCreateFileDescriptor(env, fd); -} - -static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jint netId, - jbyteArray msg, jint msgLen, jint flags) { - uint8_t data[MAXCMDSIZE]; - - checkLenAndCopy(env, msg, msgLen, data); - int fd = resNetworkSend(netId, data, msgLen, flags); - - if (fd < 0) { - throwErrnoException(env, "resNetworkSend", -fd); - return nullptr; - } - - return jniCreateFileDescriptor(env, fd); -} - -static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) { - int fd = AFileDescriptor_getFD(env, javaFd); - int rcode; - std::vector buf(MAXPACKETSIZE, 0); - - int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE); - jniSetFileDescriptorOfFD(env, javaFd, -1); - if (res < 0) { - throwErrnoException(env, "resNetworkResult", -res); - return nullptr; - } - - jbyteArray answer = env->NewByteArray(res); - if (answer == nullptr) { - throwErrnoException(env, "resNetworkResult", ENOMEM); - return nullptr; - } else { - env->SetByteArrayRegion(answer, 0, res, - reinterpret_cast(buf.data())); - } - - jclass class_DnsResponse = env->FindClass("android/net/DnsResolver$DnsResponse"); - jmethodID ctor = env->GetMethodID(class_DnsResponse, "", "([BI)V"); - - return env->NewObject(class_DnsResponse, ctor, answer, rcode); -} - -static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) { - int fd = AFileDescriptor_getFD(env, javaFd); - resNetworkCancel(fd); - jniSetFileDescriptorOfFD(env, javaFd, -1); -} - -static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) { - unsigned dnsNetId = 0; - if (int res = getNetworkForDns(&dnsNetId) < 0) { - throwErrnoException(env, "getDnsNetId", -res); - return nullptr; - } - bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS; - - static jclass class_Network = MakeGlobalRefOrDie( - env, FindClassOrDie(env, "android/net/Network")); - static jmethodID ctor = env->GetMethodID(class_Network, "", "(IZ)V"); - return env->NewObject( - class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass); -} - -static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) { - if (javaFd == NULL) { - jniThrowNullPointerException(env, NULL); - return NULL; - } - - int fd = AFileDescriptor_getFD(env, javaFd); - struct tcp_repair_window trw = {}; - socklen_t size = sizeof(trw); - - // Obtain the parameters of the TCP repair window. - int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size); - if (rc == -1) { - throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno); - return NULL; - } - - struct tcp_info tcpinfo = {}; - socklen_t tcpinfo_size = sizeof(tcp_info); - - // Obtain the window scale from the tcp info structure. This contains a scale factor that - // should be applied to the window size. - rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size); - if (rc == -1) { - throwErrnoException(env, "getsockopt : TCP_INFO", errno); - return NULL; - } - - jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow"); - jmethodID ctor = env->GetMethodID(class_TcpRepairWindow, "", "(IIIIII)V"); - - return env->NewObject(class_TcpRepairWindow, ctor, trw.snd_wl1, trw.snd_wnd, trw.max_window, - trw.rcv_wnd, trw.rcv_wup, tcpinfo.tcpi_rcv_wscale); -} - -// ---------------------------------------------------------------------------- - -/* - * JNI registration. - */ -// clang-format off -static const JNINativeMethod gNetworkUtilMethods[] = { - /* name, signature, funcPtr */ - { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork }, - { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess }, - { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution }, - { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork }, - { "protectFromVpn", "(I)Z", (void*) android_net_utils_protectFromVpn }, - { "protectFromVpn", "(Ljava/io/FileDescriptor;)Z", (void*) android_net_utils_protectFromVpnWithFd }, - { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess }, - { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter }, - { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter }, - { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow }, - { "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend }, - { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery }, - { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult }, - { "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel }, - { "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork }, -}; -// clang-format on - -int register_android_net_NetworkUtils(JNIEnv* env) -{ - return RegisterMethodsOrDie(env, NETUTILS_PKG_NAME, gNetworkUtilMethods, - NELEM(gNetworkUtilMethods)); -} - -}; // namespace android diff --git a/core/jni/android_net_NetworkUtils.cpp b/core/jni/android_net_NetworkUtils.cpp new file mode 100644 index 000000000000..750810840bde --- /dev/null +++ b/core/jni/android_net_NetworkUtils.cpp @@ -0,0 +1,288 @@ +/* + * Copyright 2020, 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. + */ + +#define LOG_TAG "NetworkUtils" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // NETID_USE_LOCAL_NAMESERVERS +#include +#include +#include +#include +#include + +#include "NetdClient.h" +#include "core_jni_helpers.h" +#include "jni.h" + +extern "C" { +int ifc_enable(const char *ifname); +int ifc_disable(const char *ifname); +} + +#define NETUTILS_PKG_NAME "android/net/NetworkUtils" + +namespace android { + +constexpr int MAXPACKETSIZE = 8 * 1024; +// FrameworkListener limits the size of commands to 4096 bytes. +constexpr int MAXCMDSIZE = 4096; + +static void throwErrnoException(JNIEnv* env, const char* functionName, int error) { + ScopedLocalRef detailMessage(env, env->NewStringUTF(functionName)); + if (detailMessage.get() == NULL) { + // Not really much we can do here. We're probably dead in the water, + // but let's try to stumble on... + env->ExceptionClear(); + } + static jclass errnoExceptionClass = + MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException")); + + static jmethodID errnoExceptionCtor = + GetMethodIDOrDie(env, errnoExceptionClass, + "", "(Ljava/lang/String;I)V"); + + jobject exception = env->NewObject(errnoExceptionClass, + errnoExceptionCtor, + detailMessage.get(), + error); + env->Throw(reinterpret_cast(exception)); +} + +static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd) +{ + struct sock_filter filter_code[] = { + // Reject all. + BPF_STMT(BPF_RET | BPF_K, 0) + }; + struct sock_fprog filter = { + sizeof(filter_code) / sizeof(filter_code[0]), + filter_code, + }; + + int fd = AFileDescriptor_getFD(env, javaFd); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); + } +} + +static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd) +{ + int optval_ignored = 0; + int fd = AFileDescriptor_getFD(env, javaFd); + if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) != + 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(SO_DETACH_FILTER): %s", strerror(errno)); + } +} + +static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId) +{ + return (jboolean) !setNetworkForProcess(netId); +} + +static jint android_net_utils_getBoundNetworkForProcess(JNIEnv *env, jobject thiz) +{ + return getNetworkForProcess(); +} + +static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz, + jint netId) +{ + return (jboolean) !setNetworkForResolv(netId); +} + +static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd, + jint netId) { + return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd)); +} + +static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId) +{ + return (jboolean) !queryUserAccess(uid, netId); +} + +static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst) +{ + if (env->GetArrayLength(addr) != len) { + return false; + } + env->GetByteArrayRegion(addr, 0, len, reinterpret_cast(dst)); + return true; +} + +static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jint netId, + jstring dname, jint ns_class, jint ns_type, jint flags) { + const jsize javaCharsCount = env->GetStringLength(dname); + const jsize byteCountUTF8 = env->GetStringUTFLength(dname); + + // Only allow dname which could be simply formatted to UTF8. + // In native layer, res_mkquery would re-format the input char array to packet. + std::vector queryname(byteCountUTF8 + 1, 0); + + env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname.data()); + int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags); + + if (fd < 0) { + throwErrnoException(env, "resNetworkQuery", -fd); + return nullptr; + } + + return jniCreateFileDescriptor(env, fd); +} + +static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jint netId, + jbyteArray msg, jint msgLen, jint flags) { + uint8_t data[MAXCMDSIZE]; + + checkLenAndCopy(env, msg, msgLen, data); + int fd = resNetworkSend(netId, data, msgLen, flags); + + if (fd < 0) { + throwErrnoException(env, "resNetworkSend", -fd); + return nullptr; + } + + return jniCreateFileDescriptor(env, fd); +} + +static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) { + int fd = AFileDescriptor_getFD(env, javaFd); + int rcode; + std::vector buf(MAXPACKETSIZE, 0); + + int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE); + jniSetFileDescriptorOfFD(env, javaFd, -1); + if (res < 0) { + throwErrnoException(env, "resNetworkResult", -res); + return nullptr; + } + + jbyteArray answer = env->NewByteArray(res); + if (answer == nullptr) { + throwErrnoException(env, "resNetworkResult", ENOMEM); + return nullptr; + } else { + env->SetByteArrayRegion(answer, 0, res, + reinterpret_cast(buf.data())); + } + + jclass class_DnsResponse = env->FindClass("android/net/DnsResolver$DnsResponse"); + jmethodID ctor = env->GetMethodID(class_DnsResponse, "", "([BI)V"); + + return env->NewObject(class_DnsResponse, ctor, answer, rcode); +} + +static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) { + int fd = AFileDescriptor_getFD(env, javaFd); + resNetworkCancel(fd); + jniSetFileDescriptorOfFD(env, javaFd, -1); +} + +static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) { + unsigned dnsNetId = 0; + if (int res = getNetworkForDns(&dnsNetId) < 0) { + throwErrnoException(env, "getDnsNetId", -res); + return nullptr; + } + bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS; + + static jclass class_Network = MakeGlobalRefOrDie( + env, FindClassOrDie(env, "android/net/Network")); + static jmethodID ctor = env->GetMethodID(class_Network, "", "(IZ)V"); + return env->NewObject( + class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass); +} + +static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) { + if (javaFd == NULL) { + jniThrowNullPointerException(env, NULL); + return NULL; + } + + int fd = AFileDescriptor_getFD(env, javaFd); + struct tcp_repair_window trw = {}; + socklen_t size = sizeof(trw); + + // Obtain the parameters of the TCP repair window. + int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size); + if (rc == -1) { + throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno); + return NULL; + } + + struct tcp_info tcpinfo = {}; + socklen_t tcpinfo_size = sizeof(tcp_info); + + // Obtain the window scale from the tcp info structure. This contains a scale factor that + // should be applied to the window size. + rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size); + if (rc == -1) { + throwErrnoException(env, "getsockopt : TCP_INFO", errno); + return NULL; + } + + jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow"); + jmethodID ctor = env->GetMethodID(class_TcpRepairWindow, "", "(IIIIII)V"); + + return env->NewObject(class_TcpRepairWindow, ctor, trw.snd_wl1, trw.snd_wnd, trw.max_window, + trw.rcv_wnd, trw.rcv_wup, tcpinfo.tcpi_rcv_wscale); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +// clang-format off +static const JNINativeMethod gNetworkUtilMethods[] = { + /* name, signature, funcPtr */ + { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork }, + { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess }, + { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution }, + { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork }, + { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess }, + { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter }, + { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter }, + { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow }, + { "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend }, + { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery }, + { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult }, + { "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel }, + { "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork }, +}; +// clang-format on + +int register_android_net_NetworkUtils(JNIEnv* env) +{ + return RegisterMethodsOrDie(env, NETUTILS_PKG_NAME, gNetworkUtilMethods, + NELEM(gNetworkUtilMethods)); +} + +}; // namespace android diff --git a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp index 10fc18dcd386..980e12d0bb40 100644 --- a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp +++ b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include "NetdClient.h" #include "core_jni_helpers.h" #include "jni.h" @@ -24,9 +26,20 @@ static void android_net_utils_setAllowNetworkingForProcess(JNIEnv *env, jobject setAllowNetworkingForProcess(hasConnectivity == JNI_TRUE); } +static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket) { + return (jboolean)!protectFromVpn(socket); +} + +static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) { + return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd)); +} + static const JNINativeMethod gNetworkUtilMethods[] = { {"setAllowNetworkingForProcess", "(Z)V", (void *)android_net_utils_setAllowNetworkingForProcess}, + {"protectFromVpn", "(I)Z", (void *)android_net_utils_protectFromVpn}, + {"protectFromVpn", "(Ljava/io/FileDescriptor;)Z", + (void *)android_net_utils_protectFromVpnWithFd}, }; int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv *env) { diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt index 41ebc5774f3d..b541e5238ffc 100644 --- a/packages/Connectivity/framework/api/system-current.txt +++ b/packages/Connectivity/framework/api/system-current.txt @@ -392,16 +392,3 @@ package android.net.apf { } -package android.net.util { - - public final class SocketUtils { - method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException; - method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException; - method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); - method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int); - method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); - method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]); - } - -} - diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java index b5e8a614b8ea..9e42bbecbe9d 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java +++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java @@ -86,22 +86,6 @@ public class NetworkUtils { */ public static native int bindSocketToNetwork(FileDescriptor fd, int netId); - /** - * Protect {@code fd} from VPN connections. After protecting, data sent through - * this socket will go directly to the underlying network, so its traffic will not be - * forwarded through the VPN. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553, - publicAlternatives = "Use {@link android.net.VpnService#protect} instead.") - public static native boolean protectFromVpn(FileDescriptor fd); - - /** - * Protect {@code socketfd} from VPN connections. After protecting, data sent through - * this socket will go directly to the underlying network, so its traffic will not be - * forwarded through the VPN. - */ - public native static boolean protectFromVpn(int socketfd); - /** * Determine if {@code uid} can access network designated by {@code netId}. * @return {@code true} if {@code uid} can access network, {@code false} otherwise. diff --git a/packages/Connectivity/framework/src/android/net/util/SocketUtils.java b/packages/Connectivity/framework/src/android/net/util/SocketUtils.java deleted file mode 100644 index e64060f1b220..000000000000 --- a/packages/Connectivity/framework/src/android/net/util/SocketUtils.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2015 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 android.net.util; - -import static android.system.OsConstants.SOL_SOCKET; -import static android.system.OsConstants.SO_BINDTODEVICE; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.net.NetworkUtils; -import android.system.ErrnoException; -import android.system.NetlinkSocketAddress; -import android.system.Os; -import android.system.PacketSocketAddress; - -import libcore.io.IoBridge; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.SocketAddress; - -/** - * Collection of utilities to interact with raw sockets. - * @hide - */ -@SystemApi -public final class SocketUtils { - /** - * Create a raw datagram socket that is bound to an interface. - * - *

Data sent through the socket will go directly to the underlying network, ignoring VPNs. - */ - public static void bindSocketToInterface(@NonNull FileDescriptor socket, @NonNull String iface) - throws ErrnoException { - // SO_BINDTODEVICE actually takes a string. This works because the first member - // of struct ifreq is a NULL-terminated interface name. - // TODO: add a setsockoptString() - Os.setsockoptIfreq(socket, SOL_SOCKET, SO_BINDTODEVICE, iface); - NetworkUtils.protectFromVpn(socket); - } - - /** - * Make a socket address to communicate with netlink. - */ - @NonNull - public static SocketAddress makeNetlinkSocketAddress(int portId, int groupsMask) { - return new NetlinkSocketAddress(portId, groupsMask); - } - - /** - * Make socket address that packet sockets can bind to. - * - * @param protocol the layer 2 protocol of the packets to receive. One of the {@code ETH_P_*} - * constants in {@link android.system.OsConstants}. - * @param ifIndex the interface index on which packets will be received. - */ - @NonNull - public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex) { - return new PacketSocketAddress( - protocol /* sll_protocol */, - ifIndex /* sll_ifindex */, - null /* sll_addr */); - } - - /** - * Make a socket address that packet socket can send packets to. - * @deprecated Use {@link #makePacketSocketAddress(int, int, byte[])} instead. - * - * @param ifIndex the interface index on which packets will be sent. - * @param hwAddr the hardware address to which packets will be sent. - */ - @Deprecated - @NonNull - public static SocketAddress makePacketSocketAddress(int ifIndex, @NonNull byte[] hwAddr) { - return new PacketSocketAddress( - 0 /* sll_protocol */, - ifIndex /* sll_ifindex */, - hwAddr /* sll_addr */); - } - - /** - * Make a socket address that a packet socket can send packets to. - * - * @param protocol the layer 2 protocol of the packets to send. One of the {@code ETH_P_*} - * constants in {@link android.system.OsConstants}. - * @param ifIndex the interface index on which packets will be sent. - * @param hwAddr the hardware address to which packets will be sent. - */ - @NonNull - public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex, - @NonNull byte[] hwAddr) { - return new PacketSocketAddress( - protocol /* sll_protocol */, - ifIndex /* sll_ifindex */, - hwAddr /* sll_addr */); - } - - /** - * @see IoBridge#closeAndSignalBlockedThreads(FileDescriptor) - */ - public static void closeSocket(@Nullable FileDescriptor fd) throws IOException { - IoBridge.closeAndSignalBlockedThreads(fd); - } - - private SocketUtils() {} -} -- cgit v1.2.3-59-g8ed1b