ares_android.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  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. #include <jni.h>
  17. #include "ares_setup.h"
  18. #include "ares.h"
  19. #include "ares_android.h"
  20. #include "ares_private.h"
  21. static JavaVM *android_jvm = NULL;
  22. static jobject android_connectivity_manager = NULL;
  23. /* ConnectivityManager.getActiveNetwork */
  24. static jmethodID android_cm_active_net_mid = NULL;
  25. /* ConnectivityManager.getLinkProperties */
  26. static jmethodID android_cm_link_props_mid = NULL;
  27. /* LinkProperties.getDnsServers */
  28. static jmethodID android_lp_dns_servers_mid = NULL;
  29. /* LinkProperties.getDomains */
  30. static jmethodID android_lp_domains_mid = NULL;
  31. /* List.size */
  32. static jmethodID android_list_size_mid = NULL;
  33. /* List.get */
  34. static jmethodID android_list_get_mid = NULL;
  35. /* InetAddress.getHostAddress */
  36. static jmethodID android_ia_host_addr_mid = NULL;
  37. static jclass jni_get_class(JNIEnv *env, const char *path)
  38. {
  39. jclass cls = NULL;
  40. if (env == NULL || path == NULL || *path == '\0')
  41. return NULL;
  42. cls = (*env)->FindClass(env, path);
  43. if ((*env)->ExceptionOccurred(env)) {
  44. (*env)->ExceptionClear(env);
  45. return NULL;
  46. }
  47. return cls;
  48. }
  49. static jmethodID jni_get_method_id(JNIEnv *env, jclass cls,
  50. const char *func_name, const char *signature)
  51. {
  52. jmethodID mid = NULL;
  53. if (env == NULL || cls == NULL || func_name == NULL || *func_name == '\0' ||
  54. signature == NULL || *signature == '\0')
  55. {
  56. return NULL;
  57. }
  58. mid = (*env)->GetMethodID(env, cls, func_name, signature);
  59. if ((*env)->ExceptionOccurred(env))
  60. {
  61. (*env)->ExceptionClear(env);
  62. return NULL;
  63. }
  64. return mid;
  65. }
  66. void ares_library_init_jvm(JavaVM *jvm)
  67. {
  68. android_jvm = jvm;
  69. }
  70. int ares_library_init_android(jobject connectivity_manager)
  71. {
  72. JNIEnv *env = NULL;
  73. int need_detatch = 0;
  74. int res;
  75. int ret = ARES_ENOTINITIALIZED;
  76. jclass obj_cls = NULL;
  77. if (android_jvm == NULL)
  78. goto cleanup;
  79. res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6);
  80. if (res == JNI_EDETACHED)
  81. {
  82. env = NULL;
  83. res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL);
  84. need_detatch = 1;
  85. }
  86. if (res != JNI_OK || env == NULL)
  87. goto cleanup;
  88. android_connectivity_manager =
  89. (*env)->NewGlobalRef(env, connectivity_manager);
  90. if (android_connectivity_manager == NULL)
  91. goto cleanup;
  92. /* Initialization has succeeded. Now attempt to cache the methods that will be
  93. * called by ares_get_android_server_list. */
  94. ret = ARES_SUCCESS;
  95. /* ConnectivityManager in API 1. */
  96. obj_cls = jni_get_class(env, "android/net/ConnectivityManager");
  97. if (obj_cls == NULL)
  98. goto cleanup;
  99. /* ConnectivityManager.getActiveNetwork in API 23. */
  100. android_cm_active_net_mid =
  101. jni_get_method_id(env, obj_cls, "getActiveNetwork",
  102. "()Landroid/net/Network;");
  103. if (android_cm_active_net_mid == NULL)
  104. goto cleanup;
  105. /* ConnectivityManager.getLinkProperties in API 21. */
  106. android_cm_link_props_mid =
  107. jni_get_method_id(env, obj_cls, "getLinkProperties",
  108. "(Landroid/net/Network;)Landroid/net/LinkProperties;");
  109. if (android_cm_link_props_mid == NULL)
  110. goto cleanup;
  111. /* LinkProperties in API 21. */
  112. (*env)->DeleteLocalRef(env, obj_cls);
  113. obj_cls = jni_get_class(env, "android/net/LinkProperties");
  114. if (obj_cls == NULL)
  115. goto cleanup;
  116. /* getDnsServers in API 21. */
  117. android_lp_dns_servers_mid = jni_get_method_id(env, obj_cls, "getDnsServers",
  118. "()Ljava/util/List;");
  119. if (android_lp_dns_servers_mid == NULL)
  120. goto cleanup;
  121. /* getDomains in API 21. */
  122. android_lp_domains_mid = jni_get_method_id(env, obj_cls, "getDomains",
  123. "()Ljava/lang/String;");
  124. if (android_lp_domains_mid == NULL)
  125. goto cleanup;
  126. (*env)->DeleteLocalRef(env, obj_cls);
  127. obj_cls = jni_get_class(env, "java/util/List");
  128. if (obj_cls == NULL)
  129. goto cleanup;
  130. android_list_size_mid = jni_get_method_id(env, obj_cls, "size", "()I");
  131. if (android_list_size_mid == NULL)
  132. goto cleanup;
  133. android_list_get_mid = jni_get_method_id(env, obj_cls, "get",
  134. "(I)Ljava/lang/Object;");
  135. if (android_list_get_mid == NULL)
  136. goto cleanup;
  137. (*env)->DeleteLocalRef(env, obj_cls);
  138. obj_cls = jni_get_class(env, "java/net/InetAddress");
  139. if (obj_cls == NULL)
  140. goto cleanup;
  141. android_ia_host_addr_mid = jni_get_method_id(env, obj_cls, "getHostAddress",
  142. "()Ljava/lang/String;");
  143. if (android_ia_host_addr_mid == NULL)
  144. goto cleanup;
  145. (*env)->DeleteLocalRef(env, obj_cls);
  146. goto done;
  147. cleanup:
  148. if (obj_cls != NULL)
  149. (*env)->DeleteLocalRef(env, obj_cls);
  150. android_cm_active_net_mid = NULL;
  151. android_cm_link_props_mid = NULL;
  152. android_lp_dns_servers_mid = NULL;
  153. android_lp_domains_mid = NULL;
  154. android_list_size_mid = NULL;
  155. android_list_get_mid = NULL;
  156. android_ia_host_addr_mid = NULL;
  157. done:
  158. if (need_detatch)
  159. (*android_jvm)->DetachCurrentThread(android_jvm);
  160. return ret;
  161. }
  162. int ares_library_android_initialized(void)
  163. {
  164. if (android_jvm == NULL || android_connectivity_manager == NULL)
  165. return ARES_ENOTINITIALIZED;
  166. return ARES_SUCCESS;
  167. }
  168. void ares_library_cleanup_android(void)
  169. {
  170. JNIEnv *env = NULL;
  171. int need_detatch = 0;
  172. int res;
  173. if (android_jvm == NULL || android_connectivity_manager == NULL)
  174. return;
  175. res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6);
  176. if (res == JNI_EDETACHED)
  177. {
  178. env = NULL;
  179. res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL);
  180. need_detatch = 1;
  181. }
  182. if (res != JNI_OK || env == NULL)
  183. return;
  184. android_cm_active_net_mid = NULL;
  185. android_cm_link_props_mid = NULL;
  186. android_lp_dns_servers_mid = NULL;
  187. android_lp_domains_mid = NULL;
  188. android_list_size_mid = NULL;
  189. android_list_get_mid = NULL;
  190. android_ia_host_addr_mid = NULL;
  191. (*env)->DeleteGlobalRef(env, android_connectivity_manager);
  192. android_connectivity_manager = NULL;
  193. if (need_detatch)
  194. (*android_jvm)->DetachCurrentThread(android_jvm);
  195. }
  196. char **ares_get_android_server_list(size_t max_servers,
  197. size_t *num_servers)
  198. {
  199. JNIEnv *env = NULL;
  200. jobject active_network = NULL;
  201. jobject link_properties = NULL;
  202. jobject server_list = NULL;
  203. jobject server = NULL;
  204. jstring str = NULL;
  205. jint nserv;
  206. const char *ch_server_address;
  207. int res;
  208. size_t i;
  209. char **dns_list = NULL;
  210. int need_detatch = 0;
  211. if (android_jvm == NULL || android_connectivity_manager == NULL ||
  212. max_servers == 0 || num_servers == NULL)
  213. {
  214. return NULL;
  215. }
  216. if (android_cm_active_net_mid == NULL || android_cm_link_props_mid == NULL ||
  217. android_lp_dns_servers_mid == NULL || android_list_size_mid == NULL ||
  218. android_list_get_mid == NULL || android_ia_host_addr_mid == NULL)
  219. {
  220. return NULL;
  221. }
  222. res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6);
  223. if (res == JNI_EDETACHED)
  224. {
  225. env = NULL;
  226. res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL);
  227. need_detatch = 1;
  228. }
  229. if (res != JNI_OK || env == NULL)
  230. goto done;
  231. /* JNI below is equivalent to this Java code.
  232. import android.content.Context;
  233. import android.net.ConnectivityManager;
  234. import android.net.LinkProperties;
  235. import android.net.Network;
  236. import java.net.InetAddress;
  237. import java.util.List;
  238. ConnectivityManager cm = (ConnectivityManager)this.getApplicationContext()
  239. .getSystemService(Context.CONNECTIVITY_SERVICE);
  240. Network an = cm.getActiveNetwork();
  241. LinkProperties lp = cm.getLinkProperties(an);
  242. List<InetAddress> dns = lp.getDnsServers();
  243. for (InetAddress ia: dns) {
  244. String ha = ia.getHostAddress();
  245. }
  246. Note: The JNI ConnectivityManager object and all method IDs were previously
  247. initialized in ares_library_init_android.
  248. */
  249. active_network = (*env)->CallObjectMethod(env, android_connectivity_manager,
  250. android_cm_active_net_mid);
  251. if (active_network == NULL)
  252. goto done;
  253. link_properties =
  254. (*env)->CallObjectMethod(env, android_connectivity_manager,
  255. android_cm_link_props_mid, active_network);
  256. if (link_properties == NULL)
  257. goto done;
  258. server_list = (*env)->CallObjectMethod(env, link_properties,
  259. android_lp_dns_servers_mid);
  260. if (server_list == NULL)
  261. goto done;
  262. nserv = (*env)->CallIntMethod(env, server_list, android_list_size_mid);
  263. if (nserv > (jint)max_servers)
  264. nserv = (jint)max_servers;
  265. if (nserv <= 0)
  266. goto done;
  267. *num_servers = (size_t)nserv;
  268. dns_list = ares_malloc(sizeof(*dns_list)*(*num_servers));
  269. for (i=0; i<*num_servers; i++)
  270. {
  271. server = (*env)->CallObjectMethod(env, server_list, android_list_get_mid,
  272. (jint)i);
  273. dns_list[i] = ares_malloc(64);
  274. dns_list[i][0] = 0;
  275. if (server == NULL)
  276. {
  277. continue;
  278. }
  279. str = (*env)->CallObjectMethod(env, server, android_ia_host_addr_mid);
  280. ch_server_address = (*env)->GetStringUTFChars(env, str, 0);
  281. strncpy(dns_list[i], ch_server_address, 64);
  282. (*env)->ReleaseStringUTFChars(env, str, ch_server_address);
  283. (*env)->DeleteLocalRef(env, str);
  284. (*env)->DeleteLocalRef(env, server);
  285. }
  286. done:
  287. if ((*env)->ExceptionOccurred(env))
  288. (*env)->ExceptionClear(env);
  289. if (server_list != NULL)
  290. (*env)->DeleteLocalRef(env, server_list);
  291. if (link_properties != NULL)
  292. (*env)->DeleteLocalRef(env, link_properties);
  293. if (active_network != NULL)
  294. (*env)->DeleteLocalRef(env, active_network);
  295. if (need_detatch)
  296. (*android_jvm)->DetachCurrentThread(android_jvm);
  297. return dns_list;
  298. }
  299. char *ares_get_android_search_domains_list(void)
  300. {
  301. JNIEnv *env = NULL;
  302. jobject active_network = NULL;
  303. jobject link_properties = NULL;
  304. jstring domains = NULL;
  305. const char *domain;
  306. int res;
  307. char *domain_list = NULL;
  308. int need_detatch = 0;
  309. if (android_jvm == NULL || android_connectivity_manager == NULL)
  310. {
  311. return NULL;
  312. }
  313. if (android_cm_active_net_mid == NULL || android_cm_link_props_mid == NULL ||
  314. android_lp_domains_mid == NULL)
  315. {
  316. return NULL;
  317. }
  318. res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6);
  319. if (res == JNI_EDETACHED)
  320. {
  321. env = NULL;
  322. res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL);
  323. need_detatch = 1;
  324. }
  325. if (res != JNI_OK || env == NULL)
  326. goto done;
  327. /* JNI below is equivalent to this Java code.
  328. import android.content.Context;
  329. import android.net.ConnectivityManager;
  330. import android.net.LinkProperties;
  331. ConnectivityManager cm = (ConnectivityManager)this.getApplicationContext()
  332. .getSystemService(Context.CONNECTIVITY_SERVICE);
  333. Network an = cm.getActiveNetwork();
  334. LinkProperties lp = cm.getLinkProperties(an);
  335. String domains = lp.getDomains();
  336. for (String domain: domains.split(",")) {
  337. String d = domain;
  338. }
  339. Note: The JNI ConnectivityManager object and all method IDs were previously
  340. initialized in ares_library_init_android.
  341. */
  342. active_network = (*env)->CallObjectMethod(env, android_connectivity_manager,
  343. android_cm_active_net_mid);
  344. if (active_network == NULL)
  345. goto done;
  346. link_properties =
  347. (*env)->CallObjectMethod(env, android_connectivity_manager,
  348. android_cm_link_props_mid, active_network);
  349. if (link_properties == NULL)
  350. goto done;
  351. /* Get the domains. It is a common separated list of domains to search. */
  352. domains = (*env)->CallObjectMethod(env, link_properties,
  353. android_lp_domains_mid);
  354. if (domains == NULL)
  355. goto done;
  356. /* Split on , */
  357. domain = (*env)->GetStringUTFChars(env, domains, 0);
  358. domain_list = ares_strdup(domain);
  359. (*env)->ReleaseStringUTFChars(env, domains, domain);
  360. (*env)->DeleteLocalRef(env, domains);
  361. done:
  362. if ((*env)->ExceptionOccurred(env))
  363. (*env)->ExceptionClear(env);
  364. if (link_properties != NULL)
  365. (*env)->DeleteLocalRef(env, link_properties);
  366. if (active_network != NULL)
  367. (*env)->DeleteLocalRef(env, active_network);
  368. if (need_detatch)
  369. (*android_jvm)->DetachCurrentThread(android_jvm);
  370. return domain_list;
  371. }
  372. #else
  373. /* warning: ISO C forbids an empty translation unit */
  374. typedef int dummy_make_iso_compilers_happy;
  375. #endif