• 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 
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