ares_android.c (14173B)
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 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 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 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 102 void ares_library_init_jvm(JavaVM *jvm) 103 { 104 android_jvm = jvm; 105 } 106 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 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 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 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 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