// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/android/network_library.h" #include #include #include #include "base/android/build_info.h" #include "base/android/jni_android.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "base/check_op.h" #include "base/native_library.h" #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "net/base/net_errors.h" #include "net/dns/public/dns_protocol.h" #include "net/net_jni_headers/AndroidNetworkLibrary_jni.h" #include "net/net_jni_headers/DnsStatus_jni.h" using base::android::AttachCurrentThread; using base::android::ConvertJavaStringToUTF8; using base::android::ConvertUTF8ToJavaString; using base::android::ScopedJavaLocalRef; using base::android::ToJavaArrayOfByteArray; using base::android::ToJavaByteArray; namespace net::android { void VerifyX509CertChain(const std::vector& cert_chain, base::StringPiece auth_type, base::StringPiece host, CertVerifyStatusAndroid* status, bool* is_issued_by_known_root, std::vector* verified_chain) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef chain_byte_array = ToJavaArrayOfByteArray(env, cert_chain); DCHECK(!chain_byte_array.is_null()); ScopedJavaLocalRef auth_string = ConvertUTF8ToJavaString(env, auth_type); DCHECK(!auth_string.is_null()); ScopedJavaLocalRef host_string = ConvertUTF8ToJavaString(env, host); DCHECK(!host_string.is_null()); ScopedJavaLocalRef result = Java_AndroidNetworkLibrary_verifyServerCertificates( env, chain_byte_array, auth_string, host_string); ExtractCertVerifyResult(result, status, is_issued_by_known_root, verified_chain); } void AddTestRootCertificate(const uint8_t* cert, size_t len) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef cert_array = ToJavaByteArray(env, cert, len); DCHECK(!cert_array.is_null()); Java_AndroidNetworkLibrary_addTestRootCertificate(env, cert_array); } void ClearTestRootCertificates() { JNIEnv* env = AttachCurrentThread(); Java_AndroidNetworkLibrary_clearTestRootCertificates(env); } bool IsCleartextPermitted(const std::string& host) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef host_string = ConvertUTF8ToJavaString(env, host); return Java_AndroidNetworkLibrary_isCleartextPermitted(env, host_string); } bool HaveOnlyLoopbackAddresses() { JNIEnv* env = AttachCurrentThread(); return Java_AndroidNetworkLibrary_haveOnlyLoopbackAddresses(env); } bool GetMimeTypeFromExtension(const std::string& extension, std::string* result) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef extension_string = ConvertUTF8ToJavaString(env, extension); ScopedJavaLocalRef ret = Java_AndroidNetworkLibrary_getMimeTypeFromExtension(env, extension_string); if (!ret.obj()) return false; *result = ConvertJavaStringToUTF8(ret); return true; } std::string GetTelephonyNetworkOperator() { return base::android::ConvertJavaStringToUTF8( Java_AndroidNetworkLibrary_getNetworkOperator( base::android::AttachCurrentThread())); } bool GetIsRoaming() { return Java_AndroidNetworkLibrary_getIsRoaming( base::android::AttachCurrentThread()); } bool GetIsCaptivePortal() { return Java_AndroidNetworkLibrary_getIsCaptivePortal( base::android::AttachCurrentThread()); } std::string GetWifiSSID() { return base::android::ConvertJavaStringToUTF8( Java_AndroidNetworkLibrary_getWifiSSID( base::android::AttachCurrentThread())); } void SetWifiEnabledForTesting(bool enabled) { Java_AndroidNetworkLibrary_setWifiEnabled( base::android::AttachCurrentThread(), enabled); } absl::optional GetWifiSignalLevel() { const int count_buckets = 5; int signal_strength = Java_AndroidNetworkLibrary_getWifiSignalLevel( base::android::AttachCurrentThread(), count_buckets); if (signal_strength < 0) return absl::nullopt; DCHECK_LE(0, signal_strength); DCHECK_GE(count_buckets - 1, signal_strength); return signal_strength; } namespace { bool GetDnsServersInternal(JNIEnv* env, const base::android::JavaRef& dns_status, std::vector* dns_servers, bool* dns_over_tls_active, std::string* dns_over_tls_hostname, std::vector* search_suffixes) { // Parse the DNS servers. std::vector> dns_servers_data; base::android::JavaArrayOfByteArrayToBytesVector( env, Java_DnsStatus_getDnsServers(env, dns_status), &dns_servers_data); for (const std::vector& dns_address_data : dns_servers_data) { IPAddress dns_address(dns_address_data.data(), dns_address_data.size()); IPEndPoint dns_server(dns_address, dns_protocol::kDefaultPort); dns_servers->push_back(dns_server); } *dns_over_tls_active = Java_DnsStatus_getPrivateDnsActive(env, dns_status); *dns_over_tls_hostname = base::android::ConvertJavaStringToUTF8( Java_DnsStatus_getPrivateDnsServerName(env, dns_status)); std::string search_suffixes_str = base::android::ConvertJavaStringToUTF8( Java_DnsStatus_getSearchDomains(env, dns_status)); *search_suffixes = base::SplitString(search_suffixes_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); return !dns_servers->empty(); } } // namespace bool GetCurrentDnsServers(std::vector* dns_servers, bool* dns_over_tls_active, std::string* dns_over_tls_hostname, std::vector* search_suffixes) { DCHECK_GE(base::android::BuildInfo::GetInstance()->sdk_int(), base::android::SDK_VERSION_MARSHMALLOW); JNIEnv* env = AttachCurrentThread(); // Get the DNS status for the current default network. ScopedJavaLocalRef result = Java_AndroidNetworkLibrary_getCurrentDnsStatus(env); if (result.is_null()) return false; return GetDnsServersInternal(env, result, dns_servers, dns_over_tls_active, dns_over_tls_hostname, search_suffixes); } bool GetDnsServersForNetwork(std::vector* dns_servers, bool* dns_over_tls_active, std::string* dns_over_tls_hostname, std::vector* search_suffixes, handles::NetworkHandle network) { DCHECK_GE(base::android::BuildInfo::GetInstance()->sdk_int(), base::android::SDK_VERSION_P); JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef result = Java_AndroidNetworkLibrary_getDnsStatusForNetwork(env, network); if (result.is_null()) return false; return GetDnsServersInternal(env, result, dns_servers, dns_over_tls_active, dns_over_tls_hostname, search_suffixes); } bool ReportBadDefaultNetwork() { return Java_AndroidNetworkLibrary_reportBadDefaultNetwork( AttachCurrentThread()); } void TagSocket(SocketDescriptor socket, uid_t uid, int32_t tag) { Java_AndroidNetworkLibrary_tagSocket(AttachCurrentThread(), socket, uid, tag); } namespace { using LollipopSetNetworkForSocket = int (*)(unsigned net_id, int socket_fd); using MarshmallowSetNetworkForSocket = int (*)(int64_t net_id, int socket_fd); MarshmallowSetNetworkForSocket GetMarshmallowSetNetworkForSocket() { // On Android M and newer releases use supported NDK API. base::FilePath file(base::GetNativeLibraryName("android")); // See declaration of android_setsocknetwork() here: // http://androidxref.com/6.0.0_r1/xref/development/ndk/platforms/android-M/include/android/multinetwork.h#65 // Function cannot be called directly as it will cause app to fail to load on // pre-marshmallow devices. void* dl = dlopen(file.value().c_str(), RTLD_NOW); return reinterpret_cast( dlsym(dl, "android_setsocknetwork")); } LollipopSetNetworkForSocket GetLollipopSetNetworkForSocket() { // On Android L use setNetworkForSocket from libnetd_client.so. Android's netd // client library should always be loaded in our address space as it shims // socket(). base::FilePath file(base::GetNativeLibraryName("netd_client")); // Use RTLD_NOW to match Android's prior loading of the library: // http://androidxref.com/6.0.0_r5/xref/bionic/libc/bionic/NetdClient.cpp#37 // Use RTLD_NOLOAD to assert that the library is already loaded and avoid // doing any disk IO. void* dl = dlopen(file.value().c_str(), RTLD_NOW | RTLD_NOLOAD); return reinterpret_cast( dlsym(dl, "setNetworkForSocket")); } } // namespace int BindToNetwork(SocketDescriptor socket, handles::NetworkHandle network) { DCHECK_NE(socket, kInvalidSocket); if (network == handles::kInvalidNetworkHandle) return ERR_INVALID_ARGUMENT; // Android prior to Lollipop didn't have support for binding sockets to // networks. if (base::android::BuildInfo::GetInstance()->sdk_int() < base::android::SDK_VERSION_LOLLIPOP) return ERR_NOT_IMPLEMENTED; int rv; if (base::android::BuildInfo::GetInstance()->sdk_int() >= base::android::SDK_VERSION_MARSHMALLOW) { static MarshmallowSetNetworkForSocket marshmallow_set_network_for_socket = GetMarshmallowSetNetworkForSocket(); if (!marshmallow_set_network_for_socket) return ERR_NOT_IMPLEMENTED; rv = marshmallow_set_network_for_socket(network, socket); if (rv) rv = errno; } else { static LollipopSetNetworkForSocket lollipop_set_network_for_socket = GetLollipopSetNetworkForSocket(); if (!lollipop_set_network_for_socket) return ERR_NOT_IMPLEMENTED; rv = -lollipop_set_network_for_socket(network, socket); } // If |network| has since disconnected, |rv| will be ENONET. Surface this as // ERR_NETWORK_CHANGED, rather than MapSystemError(ENONET) which gives back // the less descriptive ERR_FAILED. if (rv == ENONET) return ERR_NETWORK_CHANGED; return MapSystemError(rv); } namespace { using MarshmallowGetAddrInfoForNetwork = int (*)(int64_t network, const char* node, const char* service, const struct addrinfo* hints, struct addrinfo** res); MarshmallowGetAddrInfoForNetwork GetMarshmallowGetAddrInfoForNetwork() { // On Android M and newer releases use supported NDK API. base::FilePath file(base::GetNativeLibraryName("android")); // See declaration of android_getaddrinfofornetwork() here: // https://developer.android.com/ndk/reference/group/networking#android_getaddrinfofornetwork // Function cannot be called directly as it will cause app to fail to load on // pre-marshmallow devices. void* dl = dlopen(file.value().c_str(), RTLD_NOW); return reinterpret_cast( dlsym(dl, "android_getaddrinfofornetwork")); } } // namespace NET_EXPORT_PRIVATE int GetAddrInfoForNetwork(handles::NetworkHandle network, const char* node, const char* service, const struct addrinfo* hints, struct addrinfo** res) { if (network == handles::kInvalidNetworkHandle) { errno = EINVAL; return EAI_SYSTEM; } if (base::android::BuildInfo::GetInstance()->sdk_int() < base::android::SDK_VERSION_MARSHMALLOW) { errno = ENOSYS; return EAI_SYSTEM; } static MarshmallowGetAddrInfoForNetwork get_addrinfo_for_network = GetMarshmallowGetAddrInfoForNetwork(); if (!get_addrinfo_for_network) { errno = ENOSYS; return EAI_SYSTEM; } return get_addrinfo_for_network(network, node, service, hints, res); } } // namespace net::android