• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "NetworkUtils"
18 
19 #include <android/file_descriptor_jni.h>
20 #include <android/multinetwork.h>
21 #include <linux/filter.h>
22 #include <linux/tcp.h>
23 #include <netinet/in.h>
24 #include <string.h>
25 
26 #include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
27 #include <nativehelper/JNIPlatformHelp.h>
28 #include <utils/Log.h>
29 
30 #include "jni.h"
31 
32 #define NETUTILS_PKG_NAME "android/net/NetworkUtils"
33 
34 namespace android {
35 
36 constexpr int MAXPACKETSIZE = 8 * 1024;
37 // FrameworkListener limits the size of commands to 4096 bytes.
38 constexpr int MAXCMDSIZE = 4096;
39 
40 static volatile jclass class_Network = 0;
41 static volatile jmethodID method_fromNetworkHandle = 0;
42 
FindClassOrDie(JNIEnv * env,const char * class_name)43 static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
44     jclass clazz = env->FindClass(class_name);
45     LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);
46     return clazz;
47 }
48 
49 template <typename T>
MakeGlobalRefOrDie(JNIEnv * env,T in)50 static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) {
51     jobject res = env->NewGlobalRef(in);
52     LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to create global reference.");
53     return static_cast<T>(res);
54 }
55 
android_net_utils_attachDropAllBPFFilter(JNIEnv * env,jobject clazz,jobject javaFd)56 static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
57 {
58     struct sock_filter filter_code[] = {
59         // Reject all.
60         BPF_STMT(BPF_RET | BPF_K, 0)
61     };
62     struct sock_fprog filter = {
63         sizeof(filter_code) / sizeof(filter_code[0]),
64         filter_code,
65     };
66 
67     int fd = AFileDescriptor_getFd(env, javaFd);
68     if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
69         jniThrowExceptionFmt(env, "java/net/SocketException",
70                 "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
71     }
72 }
73 
android_net_utils_detachBPFFilter(JNIEnv * env,jobject clazz,jobject javaFd)74 static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
75 {
76     int optval_ignored = 0;
77     int fd = AFileDescriptor_getFd(env, javaFd);
78     if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) !=
79         0) {
80         jniThrowExceptionFmt(env, "java/net/SocketException",
81                 "setsockopt(SO_DETACH_FILTER): %s", strerror(errno));
82     }
83 }
84 
android_net_utils_bindProcessToNetworkHandle(JNIEnv * env,jobject thiz,jlong netHandle)85 static jboolean android_net_utils_bindProcessToNetworkHandle(JNIEnv *env, jobject thiz,
86         jlong netHandle)
87 {
88     return (jboolean) !android_setprocnetwork(netHandle);
89 }
90 
android_net_utils_getBoundNetworkHandleForProcess(JNIEnv * env,jobject thiz)91 static jlong android_net_utils_getBoundNetworkHandleForProcess(JNIEnv *env, jobject thiz)
92 {
93     net_handle_t network;
94     if (android_getprocnetwork(&network) != 0) {
95         jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
96                 "android_getprocnetwork(): %s", strerror(errno));
97         return NETWORK_UNSPECIFIED;
98     }
99     return (jlong) network;
100 }
101 
android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv * env,jobject thiz,jint netId,jlong netHandle)102 static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz,
103         jint netId, jlong netHandle)
104 {
105     return (jboolean) !android_setprocdns(netHandle);
106 }
107 
android_net_utils_bindSocketToNetworkHandle(JNIEnv * env,jobject thiz,jobject javaFd,jlong netHandle)108 static jint android_net_utils_bindSocketToNetworkHandle(JNIEnv *env, jobject thiz, jobject javaFd,
109                                                   jlong netHandle) {
110     return android_setsocknetwork(netHandle, AFileDescriptor_getFd(env, javaFd));
111 }
112 
checkLenAndCopy(JNIEnv * env,const jbyteArray & addr,int len,void * dst)113 static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
114 {
115     if (env->GetArrayLength(addr) != len) {
116         return false;
117     }
118     env->GetByteArrayRegion(addr, 0, len, reinterpret_cast<jbyte*>(dst));
119     return true;
120 }
121 
android_net_utils_resNetworkQuery(JNIEnv * env,jobject thiz,jlong netHandle,jstring dname,jint ns_class,jint ns_type,jint flags)122 static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jlong netHandle,
123         jstring dname, jint ns_class, jint ns_type, jint flags) {
124     const jsize javaCharsCount = env->GetStringLength(dname);
125     const jsize byteCountUTF8 = env->GetStringUTFLength(dname);
126 
127     // Only allow dname which could be simply formatted to UTF8.
128     // In native layer, res_mkquery would re-format the input char array to packet.
129     char queryname[byteCountUTF8 + 1];
130     memset(queryname, 0, (byteCountUTF8 + 1) * sizeof(char));
131 
132     env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname);
133     int fd = android_res_nquery(netHandle, queryname, ns_class, ns_type, flags);
134 
135     if (fd < 0) {
136         jniThrowErrnoException(env, "resNetworkQuery", -fd);
137         return nullptr;
138     }
139 
140     return jniCreateFileDescriptor(env, fd);
141 }
142 
android_net_utils_resNetworkSend(JNIEnv * env,jobject thiz,jlong netHandle,jbyteArray msg,jint msgLen,jint flags)143 static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jlong netHandle,
144         jbyteArray msg, jint msgLen, jint flags) {
145     uint8_t data[MAXCMDSIZE];
146 
147     checkLenAndCopy(env, msg, msgLen, data);
148     int fd = android_res_nsend(netHandle, data, msgLen, flags);
149 
150     if (fd < 0) {
151         jniThrowErrnoException(env, "resNetworkSend", -fd);
152         return nullptr;
153     }
154 
155     return jniCreateFileDescriptor(env, fd);
156 }
157 
android_net_utils_resNetworkResult(JNIEnv * env,jobject thiz,jobject javaFd)158 static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
159     int fd = AFileDescriptor_getFd(env, javaFd);
160     int rcode;
161     uint8_t buf[MAXPACKETSIZE] = {0};
162 
163     int res = android_res_nresult(fd, &rcode, buf, MAXPACKETSIZE);
164     jniSetFileDescriptorOfFD(env, javaFd, -1);
165     if (res < 0) {
166         jniThrowErrnoException(env, "resNetworkResult", -res);
167         return nullptr;
168     }
169 
170     jbyteArray answer = env->NewByteArray(res);
171     if (answer == nullptr) {
172         jniThrowErrnoException(env, "resNetworkResult", ENOMEM);
173         return nullptr;
174     } else {
175         env->SetByteArrayRegion(answer, 0, res, reinterpret_cast<jbyte*>(buf));
176     }
177 
178     jclass class_DnsResponse = env->FindClass("android/net/DnsResolver$DnsResponse");
179     jmethodID ctor = env->GetMethodID(class_DnsResponse, "<init>", "([BI)V");
180 
181     return env->NewObject(class_DnsResponse, ctor, answer, rcode);
182 }
183 
android_net_utils_resNetworkCancel(JNIEnv * env,jobject thiz,jobject javaFd)184 static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
185     int fd = AFileDescriptor_getFd(env, javaFd);
186     android_res_cancel(fd);
187     jniSetFileDescriptorOfFD(env, javaFd, -1);
188 }
189 
android_net_utils_getDnsNetwork(JNIEnv * env,jobject thiz)190 static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
191     net_handle_t dnsNetHandle = NETWORK_UNSPECIFIED;
192     if (int res = android_getprocdns(&dnsNetHandle) < 0) {
193         jniThrowErrnoException(env, "getDnsNetwork", -res);
194         return nullptr;
195     }
196 
197     if (method_fromNetworkHandle == 0) {
198         // This may be called multiple times concurrently but that is fine
199         class_Network = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/net/Network"));
200         method_fromNetworkHandle = env->GetStaticMethodID(class_Network, "fromNetworkHandle",
201                 "(J)Landroid/net/Network;");
202     }
203     return env->CallStaticObjectMethod(class_Network, method_fromNetworkHandle,
204             static_cast<jlong>(dnsNetHandle));
205 }
206 
android_net_utils_getTcpRepairWindow(JNIEnv * env,jobject thiz,jobject javaFd)207 static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
208     if (javaFd == NULL) {
209         jniThrowNullPointerException(env, NULL);
210         return NULL;
211     }
212 
213     int fd = AFileDescriptor_getFd(env, javaFd);
214     struct tcp_repair_window trw = {};
215     socklen_t size = sizeof(trw);
216 
217     // Obtain the parameters of the TCP repair window.
218     int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size);
219     if (rc == -1) {
220         jniThrowErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
221         return NULL;
222     }
223 
224     struct tcp_info tcpinfo = {};
225     socklen_t tcpinfo_size = sizeof(tcp_info);
226 
227     // Obtain the window scale from the tcp info structure. This contains a scale factor that
228     // should be applied to the window size.
229     rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size);
230     if (rc == -1) {
231         jniThrowErrnoException(env, "getsockopt : TCP_INFO", errno);
232         return NULL;
233     }
234 
235     jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow");
236     jmethodID ctor = env->GetMethodID(class_TcpRepairWindow, "<init>", "(IIIIII)V");
237 
238     return env->NewObject(class_TcpRepairWindow, ctor, trw.snd_wl1, trw.snd_wnd, trw.max_window,
239             trw.rcv_wnd, trw.rcv_wup, tcpinfo.tcpi_rcv_wscale);
240 }
241 
242 // ----------------------------------------------------------------------------
243 
244 /*
245  * JNI registration.
246  */
247 // clang-format off
248 static const JNINativeMethod gNetworkUtilMethods[] = {
249     /* name, signature, funcPtr */
250     { "bindProcessToNetworkHandle", "(J)Z", (void*) android_net_utils_bindProcessToNetworkHandle },
251     { "getBoundNetworkHandleForProcess", "()J", (void*) android_net_utils_getBoundNetworkHandleForProcess },
252     { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
253     { "bindSocketToNetworkHandle", "(Ljava/io/FileDescriptor;J)I", (void*) android_net_utils_bindSocketToNetworkHandle },
254     { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
255     { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
256     { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },
257     { "resNetworkSend", "(J[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
258     { "resNetworkQuery", "(JLjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
259     { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
260     { "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
261     { "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork },
262 };
263 // clang-format on
264 
register_android_net_NetworkUtils(JNIEnv * env)265 int register_android_net_NetworkUtils(JNIEnv* env)
266 {
267     return jniRegisterNativeMethods(env, NETUTILS_PKG_NAME, gNetworkUtilMethods,
268                                     NELEM(gNetworkUtilMethods));
269 }
270 
271 }; // namespace android
272