1 /*
2  * Copyright 2008, 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 "NetUtils"
18 
19 #include "jni.h"
20 #include "JNIHelp.h"
21 #include "NetdClient.h"
22 #include "resolv_netid.h"
23 #include <utils/misc.h>
24 #include <android_runtime/AndroidRuntime.h>
25 #include <utils/Log.h>
26 #include <arpa/inet.h>
27 #include <cutils/properties.h>
28 
29 extern "C" {
30 int ifc_enable(const char *ifname);
31 int ifc_disable(const char *ifname);
32 int ifc_reset_connections(const char *ifname, int reset_mask);
33 
34 int dhcp_do_request(const char * const ifname,
35                     const char *ipaddr,
36                     const char *gateway,
37                     uint32_t *prefixLength,
38                     const char *dns[],
39                     const char *server,
40                     uint32_t *lease,
41                     const char *vendorInfo,
42                     const char *domains,
43                     const char *mtu);
44 
45 int dhcp_do_request_renew(const char * const ifname,
46                     const char *ipaddr,
47                     const char *gateway,
48                     uint32_t *prefixLength,
49                     const char *dns[],
50                     const char *server,
51                     uint32_t *lease,
52                     const char *vendorInfo,
53                     const char *domains,
54                     const char *mtu);
55 
56 int dhcp_stop(const char *ifname);
57 int dhcp_release_lease(const char *ifname);
58 char *dhcp_get_errmsg();
59 }
60 
61 #define NETUTILS_PKG_NAME "android/net/NetworkUtils"
62 
63 namespace android {
64 
65 /*
66  * The following remembers the jfieldID's of the fields
67  * of the DhcpInfo Java object, so that we don't have
68  * to look them up every time.
69  */
70 static struct fieldIds {
71     jmethodID clear;
72     jmethodID setInterfaceName;
73     jmethodID addLinkAddress;
74     jmethodID addGateway;
75     jmethodID addDns;
76     jmethodID setDomains;
77     jmethodID setServerAddress;
78     jmethodID setLeaseDuration;
79     jmethodID setVendorInfo;
80 } dhcpResultsFieldIds;
81 
82 static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname)
83 {
84     int result;
85 
86     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
87     result = ::ifc_enable(nameStr);
88     env->ReleaseStringUTFChars(ifname, nameStr);
89     return (jint)result;
90 }
91 
92 static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstring ifname)
93 {
94     int result;
95 
96     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
97     result = ::ifc_disable(nameStr);
98     env->ReleaseStringUTFChars(ifname, nameStr);
99     return (jint)result;
100 }
101 
102 static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
103       jstring ifname, jint mask)
104 {
105     int result;
106 
107     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
108 
109     LOGD("android_net_utils_resetConnections in env=%p clazz=%p iface=%s mask=0x%x\n",
110           env, clazz, nameStr, mask);
111 
112     result = ::ifc_reset_connections(nameStr, mask);
113     env->ReleaseStringUTFChars(ifname, nameStr);
114     return (jint)result;
115 }
116 
117 static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstring ifname,
118         jobject dhcpResults, bool renew)
119 {
120     int result;
121     char  ipaddr[PROPERTY_VALUE_MAX];
122     uint32_t prefixLength;
123     char gateway[PROPERTY_VALUE_MAX];
124     char    dns1[PROPERTY_VALUE_MAX];
125     char    dns2[PROPERTY_VALUE_MAX];
126     char    dns3[PROPERTY_VALUE_MAX];
127     char    dns4[PROPERTY_VALUE_MAX];
128     const char *dns[5] = {dns1, dns2, dns3, dns4, NULL};
129     char  server[PROPERTY_VALUE_MAX];
130     uint32_t lease;
131     char vendorInfo[PROPERTY_VALUE_MAX];
132     char domains[PROPERTY_VALUE_MAX];
133     char mtu[PROPERTY_VALUE_MAX];
134 
135     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
136     if (nameStr == NULL) return (jboolean)false;
137 
138     if (renew) {
139         result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
140                 dns, server, &lease, vendorInfo, domains, mtu);
141     } else {
142         result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
143                 dns, server, &lease, vendorInfo, domains, mtu);
144     }
145     if (result != 0) {
146         ALOGD("dhcp_do_request failed : %s (%s)", nameStr, renew ? "renew" : "new");
147     }
148 
149     env->ReleaseStringUTFChars(ifname, nameStr);
150     if (result == 0) {
151         env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.clear);
152 
153         // set mIfaceName
154         // dhcpResults->setInterfaceName(ifname)
155         env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.setInterfaceName, ifname);
156 
157         // set the linkAddress
158         // dhcpResults->addLinkAddress(inetAddress, prefixLength)
159         result = env->CallBooleanMethod(dhcpResults, dhcpResultsFieldIds.addLinkAddress,
160                 env->NewStringUTF(ipaddr), prefixLength);
161     }
162 
163     if (result == 0) {
164         // set the gateway
165         // dhcpResults->addGateway(gateway)
166         result = env->CallBooleanMethod(dhcpResults,
167                 dhcpResultsFieldIds.addGateway, env->NewStringUTF(gateway));
168     }
169 
170     if (result == 0) {
171         // dhcpResults->addDns(new InetAddress(dns1))
172         result = env->CallBooleanMethod(dhcpResults,
173                 dhcpResultsFieldIds.addDns, env->NewStringUTF(dns1));
174     }
175 
176     if (result == 0) {
177         env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.setDomains,
178                 env->NewStringUTF(domains));
179 
180         result = env->CallBooleanMethod(dhcpResults,
181                 dhcpResultsFieldIds.addDns, env->NewStringUTF(dns2));
182 
183         if (result == 0) {
184             result = env->CallBooleanMethod(dhcpResults,
185                     dhcpResultsFieldIds.addDns, env->NewStringUTF(dns3));
186             if (result == 0) {
187                 result = env->CallBooleanMethod(dhcpResults,
188                         dhcpResultsFieldIds.addDns, env->NewStringUTF(dns4));
189             }
190         }
191     }
192 
193     if (result == 0) {
194         // dhcpResults->setServerAddress(new InetAddress(server))
195         result = env->CallBooleanMethod(dhcpResults, dhcpResultsFieldIds.setServerAddress,
196                 env->NewStringUTF(server));
197     }
198 
199     if (result == 0) {
200         // dhcpResults->setLeaseDuration(lease)
201         env->CallVoidMethod(dhcpResults,
202                 dhcpResultsFieldIds.setLeaseDuration, lease);
203 
204         // dhcpResults->setVendorInfo(vendorInfo)
205         env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.setVendorInfo,
206                 env->NewStringUTF(vendorInfo));
207     }
208     return (jboolean)(result == 0);
209 }
210 
211 
212 static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
213 {
214     return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false);
215 }
216 
217 static jboolean android_net_utils_runDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
218 {
219     return android_net_utils_runDhcpCommon(env, clazz, ifname, info, true);
220 }
221 
222 
223 static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname)
224 {
225     int result;
226 
227     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
228     result = ::dhcp_stop(nameStr);
229     env->ReleaseStringUTFChars(ifname, nameStr);
230     return (jboolean)(result == 0);
231 }
232 
233 static jboolean android_net_utils_releaseDhcpLease(JNIEnv* env, jobject clazz, jstring ifname)
234 {
235     int result;
236 
237     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
238     result = ::dhcp_release_lease(nameStr);
239     env->ReleaseStringUTFChars(ifname, nameStr);
240     return (jboolean)(result == 0);
241 }
242 
243 static jstring android_net_utils_getDhcpError(JNIEnv* env, jobject clazz)
244 {
245     return env->NewStringUTF(::dhcp_get_errmsg());
246 }
247 
248 static void android_net_utils_markSocket(JNIEnv *env, jobject thiz, jint socket, jint mark)
249 {
250     if (setsockopt(socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
251         jniThrowException(env, "java/lang/IllegalStateException", "Error marking socket");
252     }
253 }
254 
255 static void android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
256 {
257     setNetworkForProcess(netId);
258 }
259 
260 static void android_net_utils_unbindProcessToNetwork(JNIEnv *env, jobject thiz)
261 {
262     setNetworkForProcess(NETID_UNSET);
263 }
264 
265 static jint android_net_utils_getNetworkBoundToProcess(JNIEnv *env, jobject thiz)
266 {
267     return getNetworkForProcess();
268 }
269 
270 static void android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz, jint netId)
271 {
272     setNetworkForResolv(netId);
273 }
274 
275 static void android_net_utils_unbindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz)
276 {
277     setNetworkForResolv(NETID_UNSET);
278 }
279 
280 static void android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket, jint netId)
281 {
282     setNetworkForSocket(netId, socket);
283 }
284 
285 // ----------------------------------------------------------------------------
286 
287 /*
288  * JNI registration.
289  */
290 static JNINativeMethod gNetworkUtilMethods[] = {
291     /* name, signature, funcPtr */
292 
293     { "enableInterface", "(Ljava/lang/String;)I",  (void *)android_net_utils_enableInterface },
294     { "disableInterface", "(Ljava/lang/String;)I",  (void *)android_net_utils_disableInterface },
295     { "resetConnections", "(Ljava/lang/String;I)I",  (void *)android_net_utils_resetConnections },
296     { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z",  (void *)android_net_utils_runDhcp },
297     { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z",  (void *)android_net_utils_runDhcpRenew },
298     { "stopDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_stopDhcp },
299     { "releaseDhcpLease", "(Ljava/lang/String;)Z",  (void *)android_net_utils_releaseDhcpLease },
300     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
301     { "markSocket", "(II)V", (void*) android_net_utils_markSocket },
302     { "bindProcessToNetwork", "(I)V", (void*) android_net_utils_bindProcessToNetwork },
303     { "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess },
304     { "unbindProcessToNetwork", "()V", (void*) android_net_utils_unbindProcessToNetwork },
305     { "bindProcessToNetworkForHostResolution", "(I)V", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
306     { "unbindProcessToNetworkForHostResolution", "()V", (void*) android_net_utils_unbindProcessToNetworkForHostResolution },
307     { "bindSocketToNetwork", "(II)V", (void*) android_net_utils_bindSocketToNetwork },
308 };
309 
310 int register_android_net_NetworkUtils(JNIEnv* env)
311 {
312     jclass dhcpResultsClass = env->FindClass("android/net/DhcpResults");
313     LOG_FATAL_IF(dhcpResultsClass == NULL, "Unable to find class android/net/DhcpResults");
314     dhcpResultsFieldIds.clear =
315             env->GetMethodID(dhcpResultsClass, "clear", "()V");
316     dhcpResultsFieldIds.setInterfaceName =
317             env->GetMethodID(dhcpResultsClass, "setInterfaceName", "(Ljava/lang/String;)V");
318     dhcpResultsFieldIds.addLinkAddress =
319             env->GetMethodID(dhcpResultsClass, "addLinkAddress", "(Ljava/lang/String;I)Z");
320     dhcpResultsFieldIds.addGateway =
321             env->GetMethodID(dhcpResultsClass, "addGateway", "(Ljava/lang/String;)Z");
322     dhcpResultsFieldIds.addDns =
323             env->GetMethodID(dhcpResultsClass, "addDns", "(Ljava/lang/String;)Z");
324     dhcpResultsFieldIds.setDomains =
325             env->GetMethodID(dhcpResultsClass, "setDomains", "(Ljava/lang/String;)V");
326     dhcpResultsFieldIds.setServerAddress =
327             env->GetMethodID(dhcpResultsClass, "setServerAddress", "(Ljava/lang/String;)Z");
328     dhcpResultsFieldIds.setLeaseDuration =
329             env->GetMethodID(dhcpResultsClass, "setLeaseDuration", "(I)V");
330     dhcpResultsFieldIds.setVendorInfo =
331             env->GetMethodID(dhcpResultsClass, "setVendorInfo", "(Ljava/lang/String;)V");
332 
333     return AndroidRuntime::registerNativeMethods(env,
334             NETUTILS_PKG_NAME, gNetworkUtilMethods, NELEM(gNetworkUtilMethods));
335 }
336 
337 }; // namespace android
338