• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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