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