1 /* MIT License
2 *
3 * Copyright (c) John Schember
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * SPDX-License-Identifier: MIT
25 */
26 #if defined(ANDROID) || defined(__ANDROID__)
27
28 # include <jni.h>
29
30 # include "ares_setup.h"
31 # include "ares.h"
32 # include "ares_android.h"
33 # include "ares_private.h"
34
35 static JavaVM *android_jvm = NULL;
36 static jobject android_connectivity_manager = NULL;
37
38 /* ConnectivityManager.getActiveNetwork */
39 static jmethodID android_cm_active_net_mid = NULL;
40 /* ConnectivityManager.getLinkProperties */
41 static jmethodID android_cm_link_props_mid = NULL;
42 /* LinkProperties.getDnsServers */
43 static jmethodID android_lp_dns_servers_mid = NULL;
44 /* LinkProperties.getDomains */
45 static jmethodID android_lp_domains_mid = NULL;
46 /* List.size */
47 static jmethodID android_list_size_mid = NULL;
48 /* List.get */
49 static jmethodID android_list_get_mid = NULL;
50 /* InetAddress.getHostAddress */
51 static jmethodID android_ia_host_addr_mid = NULL;
52
jni_get_class(JNIEnv * env,const char * path)53 static jclass jni_get_class(JNIEnv *env, const char *path)
54 {
55 jclass cls = NULL;
56
57 if (env == NULL || path == NULL || *path == '\0') {
58 return NULL;
59 }
60
61 cls = (*env)->FindClass(env, path);
62 if ((*env)->ExceptionOccurred(env)) {
63 (*env)->ExceptionClear(env);
64 return NULL;
65 }
66 return cls;
67 }
68
jni_get_method_id(JNIEnv * env,jclass cls,const char * func_name,const char * signature)69 static jmethodID jni_get_method_id(JNIEnv *env, jclass cls,
70 const char *func_name, const char *signature)
71 {
72 jmethodID mid = NULL;
73
74 if (env == NULL || cls == NULL || func_name == NULL || *func_name == '\0' ||
75 signature == NULL || *signature == '\0') {
76 return NULL;
77 }
78
79 mid = (*env)->GetMethodID(env, cls, func_name, signature);
80 if ((*env)->ExceptionOccurred(env)) {
81 (*env)->ExceptionClear(env);
82 return NULL;
83 }
84
85 return mid;
86 }
87
ares_library_init_jvm(JavaVM * jvm)88 void ares_library_init_jvm(JavaVM *jvm)
89 {
90 android_jvm = jvm;
91 }
92
ares_library_init_android(jobject connectivity_manager)93 int ares_library_init_android(jobject connectivity_manager)
94 {
95 JNIEnv *env = NULL;
96 int need_detatch = 0;
97 int res;
98 ares_status_t ret = ARES_ENOTINITIALIZED;
99 jclass obj_cls = NULL;
100
101 if (android_jvm == NULL) {
102 goto cleanup;
103 }
104
105 res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6);
106 if (res == JNI_EDETACHED) {
107 env = NULL;
108 res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL);
109 need_detatch = 1;
110 }
111 if (res != JNI_OK || env == NULL) {
112 goto cleanup;
113 }
114
115 android_connectivity_manager =
116 (*env)->NewGlobalRef(env, connectivity_manager);
117 if (android_connectivity_manager == NULL) {
118 goto cleanup;
119 }
120
121 /* Initialization has succeeded. Now attempt to cache the methods that will be
122 * called by ares_get_android_server_list. */
123 ret = ARES_SUCCESS;
124
125 /* ConnectivityManager in API 1. */
126 obj_cls = jni_get_class(env, "android/net/ConnectivityManager");
127 if (obj_cls == NULL) {
128 goto cleanup;
129 }
130
131 /* ConnectivityManager.getActiveNetwork in API 23. */
132 android_cm_active_net_mid = jni_get_method_id(
133 env, obj_cls, "getActiveNetwork", "()Landroid/net/Network;");
134 if (android_cm_active_net_mid == NULL) {
135 goto cleanup;
136 }
137
138 /* ConnectivityManager.getLinkProperties in API 21. */
139 android_cm_link_props_mid =
140 jni_get_method_id(env, obj_cls, "getLinkProperties",
141 "(Landroid/net/Network;)Landroid/net/LinkProperties;");
142 if (android_cm_link_props_mid == NULL) {
143 goto cleanup;
144 }
145
146 /* LinkProperties in API 21. */
147 (*env)->DeleteLocalRef(env, obj_cls);
148 obj_cls = jni_get_class(env, "android/net/LinkProperties");
149 if (obj_cls == NULL) {
150 goto cleanup;
151 }
152
153 /* getDnsServers in API 21. */
154 android_lp_dns_servers_mid =
155 jni_get_method_id(env, obj_cls, "getDnsServers", "()Ljava/util/List;");
156 if (android_lp_dns_servers_mid == NULL) {
157 goto cleanup;
158 }
159
160 /* getDomains in API 21. */
161 android_lp_domains_mid =
162 jni_get_method_id(env, obj_cls, "getDomains", "()Ljava/lang/String;");
163 if (android_lp_domains_mid == NULL) {
164 goto cleanup;
165 }
166
167 (*env)->DeleteLocalRef(env, obj_cls);
168 obj_cls = jni_get_class(env, "java/util/List");
169 if (obj_cls == NULL) {
170 goto cleanup;
171 }
172
173 android_list_size_mid = jni_get_method_id(env, obj_cls, "size", "()I");
174 if (android_list_size_mid == NULL) {
175 goto cleanup;
176 }
177
178 android_list_get_mid =
179 jni_get_method_id(env, obj_cls, "get", "(I)Ljava/lang/Object;");
180 if (android_list_get_mid == NULL) {
181 goto cleanup;
182 }
183
184 (*env)->DeleteLocalRef(env, obj_cls);
185 obj_cls = jni_get_class(env, "java/net/InetAddress");
186 if (obj_cls == NULL) {
187 goto cleanup;
188 }
189
190 android_ia_host_addr_mid =
191 jni_get_method_id(env, obj_cls, "getHostAddress", "()Ljava/lang/String;");
192 if (android_ia_host_addr_mid == NULL) {
193 goto cleanup;
194 }
195
196 (*env)->DeleteLocalRef(env, obj_cls);
197 goto done;
198
199 cleanup:
200 if (obj_cls != NULL) {
201 (*env)->DeleteLocalRef(env, obj_cls);
202 }
203
204 android_cm_active_net_mid = NULL;
205 android_cm_link_props_mid = NULL;
206 android_lp_dns_servers_mid = NULL;
207 android_lp_domains_mid = NULL;
208 android_list_size_mid = NULL;
209 android_list_get_mid = NULL;
210 android_ia_host_addr_mid = NULL;
211
212 done:
213 if (need_detatch) {
214 (*android_jvm)->DetachCurrentThread(android_jvm);
215 }
216
217 return ret;
218 }
219
ares_library_android_initialized(void)220 int ares_library_android_initialized(void)
221 {
222 if (android_jvm == NULL || android_connectivity_manager == NULL) {
223 return ARES_ENOTINITIALIZED;
224 }
225 return ARES_SUCCESS;
226 }
227
ares_library_cleanup_android(void)228 void ares_library_cleanup_android(void)
229 {
230 JNIEnv *env = NULL;
231 int need_detatch = 0;
232 int res;
233
234 if (android_jvm == NULL || android_connectivity_manager == NULL) {
235 return;
236 }
237
238 res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6);
239 if (res == JNI_EDETACHED) {
240 env = NULL;
241 res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL);
242 need_detatch = 1;
243 }
244 if (res != JNI_OK || env == NULL) {
245 return;
246 }
247
248 android_cm_active_net_mid = NULL;
249 android_cm_link_props_mid = NULL;
250 android_lp_dns_servers_mid = NULL;
251 android_lp_domains_mid = NULL;
252 android_list_size_mid = NULL;
253 android_list_get_mid = NULL;
254 android_ia_host_addr_mid = NULL;
255
256 (*env)->DeleteGlobalRef(env, android_connectivity_manager);
257 android_connectivity_manager = NULL;
258
259 if (need_detatch) {
260 (*android_jvm)->DetachCurrentThread(android_jvm);
261 }
262 }
263
ares_get_android_server_list(size_t max_servers,size_t * num_servers)264 char **ares_get_android_server_list(size_t max_servers, size_t *num_servers)
265 {
266 JNIEnv *env = NULL;
267 jobject active_network = NULL;
268 jobject link_properties = NULL;
269 jobject server_list = NULL;
270 jobject server = NULL;
271 jstring str = NULL;
272 jint nserv;
273 const char *ch_server_address;
274 int res;
275 size_t i;
276 char **dns_list = NULL;
277 int need_detatch = 0;
278
279 if (android_jvm == NULL || android_connectivity_manager == NULL ||
280 max_servers == 0 || num_servers == NULL) {
281 return NULL;
282 }
283
284 if (android_cm_active_net_mid == NULL || android_cm_link_props_mid == NULL ||
285 android_lp_dns_servers_mid == NULL || android_list_size_mid == NULL ||
286 android_list_get_mid == NULL || android_ia_host_addr_mid == NULL) {
287 return NULL;
288 }
289
290 res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6);
291 if (res == JNI_EDETACHED) {
292 env = NULL;
293 res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL);
294 need_detatch = 1;
295 }
296 if (res != JNI_OK || env == NULL) {
297 goto done;
298 }
299
300 /* JNI below is equivalent to this Java code.
301 import android.content.Context;
302 import android.net.ConnectivityManager;
303 import android.net.LinkProperties;
304 import android.net.Network;
305 import java.net.InetAddress;
306 import java.util.List;
307
308 ConnectivityManager cm = (ConnectivityManager)this.getApplicationContext()
309 .getSystemService(Context.CONNECTIVITY_SERVICE);
310 Network an = cm.getActiveNetwork();
311 LinkProperties lp = cm.getLinkProperties(an);
312 List<InetAddress> dns = lp.getDnsServers();
313 for (InetAddress ia: dns) {
314 String ha = ia.getHostAddress();
315 }
316
317 Note: The JNI ConnectivityManager object and all method IDs were previously
318 initialized in ares_library_init_android.
319 */
320
321 active_network = (*env)->CallObjectMethod(env, android_connectivity_manager,
322 android_cm_active_net_mid);
323 if (active_network == NULL) {
324 goto done;
325 }
326
327 link_properties =
328 (*env)->CallObjectMethod(env, android_connectivity_manager,
329 android_cm_link_props_mid, active_network);
330 if (link_properties == NULL) {
331 goto done;
332 }
333
334 server_list =
335 (*env)->CallObjectMethod(env, link_properties, android_lp_dns_servers_mid);
336 if (server_list == NULL) {
337 goto done;
338 }
339
340 nserv = (*env)->CallIntMethod(env, server_list, android_list_size_mid);
341 if (nserv > (jint)max_servers) {
342 nserv = (jint)max_servers;
343 }
344 if (nserv <= 0) {
345 goto done;
346 }
347 *num_servers = (size_t)nserv;
348
349 dns_list = ares_malloc(sizeof(*dns_list) * (*num_servers));
350 for (i = 0; i < *num_servers; i++) {
351 size_t len = 64;
352 server =
353 (*env)->CallObjectMethod(env, server_list, android_list_get_mid, (jint)i);
354 dns_list[i] = ares_malloc(len);
355 dns_list[i][0] = 0;
356 if (server == NULL) {
357 continue;
358 }
359 str = (*env)->CallObjectMethod(env, server, android_ia_host_addr_mid);
360 ch_server_address = (*env)->GetStringUTFChars(env, str, 0);
361 ares_strcpy(dns_list[i], ch_server_address, len);
362 (*env)->ReleaseStringUTFChars(env, str, ch_server_address);
363 (*env)->DeleteLocalRef(env, str);
364 (*env)->DeleteLocalRef(env, server);
365 }
366
367 done:
368 if ((*env)->ExceptionOccurred(env)) {
369 (*env)->ExceptionClear(env);
370 }
371
372 if (server_list != NULL) {
373 (*env)->DeleteLocalRef(env, server_list);
374 }
375 if (link_properties != NULL) {
376 (*env)->DeleteLocalRef(env, link_properties);
377 }
378 if (active_network != NULL) {
379 (*env)->DeleteLocalRef(env, active_network);
380 }
381
382 if (need_detatch) {
383 (*android_jvm)->DetachCurrentThread(android_jvm);
384 }
385 return dns_list;
386 }
387
ares_get_android_search_domains_list(void)388 char *ares_get_android_search_domains_list(void)
389 {
390 JNIEnv *env = NULL;
391 jobject active_network = NULL;
392 jobject link_properties = NULL;
393 jstring domains = NULL;
394 const char *domain;
395 int res;
396 char *domain_list = NULL;
397 int need_detatch = 0;
398
399 if (android_jvm == NULL || android_connectivity_manager == NULL) {
400 return NULL;
401 }
402
403 if (android_cm_active_net_mid == NULL || android_cm_link_props_mid == NULL ||
404 android_lp_domains_mid == NULL) {
405 return NULL;
406 }
407
408 res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6);
409 if (res == JNI_EDETACHED) {
410 env = NULL;
411 res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL);
412 need_detatch = 1;
413 }
414 if (res != JNI_OK || env == NULL) {
415 goto done;
416 }
417
418 /* JNI below is equivalent to this Java code.
419 import android.content.Context;
420 import android.net.ConnectivityManager;
421 import android.net.LinkProperties;
422
423 ConnectivityManager cm = (ConnectivityManager)this.getApplicationContext()
424 .getSystemService(Context.CONNECTIVITY_SERVICE);
425 Network an = cm.getActiveNetwork();
426 LinkProperties lp = cm.getLinkProperties(an);
427 String domains = lp.getDomains();
428 for (String domain: domains.split(",")) {
429 String d = domain;
430 }
431
432 Note: The JNI ConnectivityManager object and all method IDs were previously
433 initialized in ares_library_init_android.
434 */
435
436 active_network = (*env)->CallObjectMethod(env, android_connectivity_manager,
437 android_cm_active_net_mid);
438 if (active_network == NULL) {
439 goto done;
440 }
441
442 link_properties =
443 (*env)->CallObjectMethod(env, android_connectivity_manager,
444 android_cm_link_props_mid, active_network);
445 if (link_properties == NULL) {
446 goto done;
447 }
448
449 /* Get the domains. It is a common separated list of domains to search. */
450 domains =
451 (*env)->CallObjectMethod(env, link_properties, android_lp_domains_mid);
452 if (domains == NULL) {
453 goto done;
454 }
455
456 /* Split on , */
457 domain = (*env)->GetStringUTFChars(env, domains, 0);
458 domain_list = ares_strdup(domain);
459 (*env)->ReleaseStringUTFChars(env, domains, domain);
460 (*env)->DeleteLocalRef(env, domains);
461
462 done:
463 if ((*env)->ExceptionOccurred(env)) {
464 (*env)->ExceptionClear(env);
465 }
466
467 if (link_properties != NULL) {
468 (*env)->DeleteLocalRef(env, link_properties);
469 }
470 if (active_network != NULL) {
471 (*env)->DeleteLocalRef(env, active_network);
472 }
473
474 if (need_detatch) {
475 (*android_jvm)->DetachCurrentThread(android_jvm);
476 }
477 return domain_list;
478 }
479 #else
480 /* warning: ISO C forbids an empty translation unit */
481 typedef int dummy_make_iso_compilers_happy;
482 #endif
483