• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   char *domain_list = NULL;
364   int need_detatch = 0;
365 
366   if (android_jvm == NULL || android_connectivity_manager == NULL)
367   {
368     return NULL;
369   }
370 
371   if (android_cm_active_net_mid == NULL || android_cm_link_props_mid == NULL ||
372       android_lp_domains_mid == NULL)
373   {
374     return NULL;
375   }
376 
377   res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6);
378   if (res == JNI_EDETACHED)
379   {
380     env = NULL;
381     res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL);
382     need_detatch = 1;
383   }
384   if (res != JNI_OK || env == NULL)
385     goto done;
386 
387   /* JNI below is equivalent to this Java code.
388      import android.content.Context;
389      import android.net.ConnectivityManager;
390      import android.net.LinkProperties;
391 
392      ConnectivityManager cm = (ConnectivityManager)this.getApplicationContext()
393        .getSystemService(Context.CONNECTIVITY_SERVICE);
394      Network an = cm.getActiveNetwork();
395      LinkProperties lp = cm.getLinkProperties(an);
396 	 String domains = lp.getDomains();
397      for (String domain: domains.split(",")) {
398        String d = domain;
399      }
400 
401      Note: The JNI ConnectivityManager object and all method IDs were previously
402            initialized in ares_library_init_android.
403    */
404 
405   active_network = (*env)->CallObjectMethod(env, android_connectivity_manager,
406                                             android_cm_active_net_mid);
407   if (active_network == NULL)
408     goto done;
409 
410   link_properties =
411       (*env)->CallObjectMethod(env, android_connectivity_manager,
412                                android_cm_link_props_mid, active_network);
413   if (link_properties == NULL)
414     goto done;
415 
416   /* Get the domains. It is a common separated list of domains to search. */
417   domains = (*env)->CallObjectMethod(env, link_properties,
418                                          android_lp_domains_mid);
419   if (domains == NULL)
420     goto done;
421 
422   /* Split on , */
423   domain = (*env)->GetStringUTFChars(env, domains, 0);
424   domain_list = ares_strdup(domain);
425   (*env)->ReleaseStringUTFChars(env, domains, domain);
426   (*env)->DeleteLocalRef(env, domains);
427 
428 done:
429   if ((*env)->ExceptionOccurred(env))
430     (*env)->ExceptionClear(env);
431 
432   if (link_properties != NULL)
433     (*env)->DeleteLocalRef(env, link_properties);
434   if (active_network != NULL)
435     (*env)->DeleteLocalRef(env, active_network);
436 
437   if (need_detatch)
438     (*android_jvm)->DetachCurrentThread(android_jvm);
439   return domain_list;
440 }
441 #else
442 /* warning: ISO C forbids an empty translation unit */
443 typedef int dummy_make_iso_compilers_happy;
444 #endif
445