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