// Copyright 2023 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/dns/loopback_only.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/logging.h" #include "base/task/sequenced_task_runner.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "base/threading/scoped_blocking_call.h" #include "build/build_config.h" #include "net/base/network_change_notifier.h" #include "net/base/network_interfaces.h" #include "net/base/sys_addrinfo.h" #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include #if BUILDFLAG(IS_ANDROID) #include "net/android/network_library.h" #else // BUILDFLAG(IS_ANDROID) #include #endif // BUILDFLAG(IS_ANDROID) #endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #if BUILDFLAG(IS_LINUX) #include #include "net/base/address_map_linux.h" #include "net/base/address_tracker_linux.h" #include "net/base/network_interfaces_linux.h" #endif namespace net { namespace { #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)) || BUILDFLAG(IS_FUCHSIA) bool HaveOnlyLoopbackAddressesUsingGetifaddrs() { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); struct ifaddrs* interface_addr = nullptr; int rv = getifaddrs(&interface_addr); if (rv != 0) { DVPLOG(1) << "getifaddrs() failed"; return false; } bool result = true; for (struct ifaddrs* interface = interface_addr; interface != nullptr; interface = interface->ifa_next) { if (!(IFF_UP & interface->ifa_flags)) { continue; } if (IFF_LOOPBACK & interface->ifa_flags) { continue; } const struct sockaddr* addr = interface->ifa_addr; if (!addr) { continue; } if (addr->sa_family == AF_INET6) { // Safe cast since this is AF_INET6. const struct sockaddr_in6* addr_in6 = reinterpret_cast(addr); const struct in6_addr* sin6_addr = &addr_in6->sin6_addr; if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_LINKLOCAL(sin6_addr)) { continue; } } if (addr->sa_family != AF_INET6 && addr->sa_family != AF_INET) { continue; } result = false; break; } freeifaddrs(interface_addr); return result; } #endif // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)) || // BUILDFLAG(IS_FUCHSIA) // This implementation will always be posted to a thread pool. bool HaveOnlyLoopbackAddressesSlow() { #if BUILDFLAG(IS_WIN) // TODO(wtc): implement with the GetAdaptersAddresses function. NOTIMPLEMENTED(); return false; #elif BUILDFLAG(IS_ANDROID) return android::HaveOnlyLoopbackAddresses(); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) return HaveOnlyLoopbackAddressesUsingGetifaddrs(); #endif // defined(various platforms) } #if BUILDFLAG(IS_LINUX) // This implementation can run on the main thread as it will not block. bool HaveOnlyLoopbackAddressesFast(AddressMapOwnerLinux* address_map_owner) { // The AddressMapOwnerLinux has already cached all the information necessary // to determine if only loopback addresses exist. AddressMapOwnerLinux::AddressMap address_map = address_map_owner->GetAddressMap(); std::unordered_set online_links = address_map_owner->GetOnlineLinks(); for (const auto& [address, ifaddrmsg] : address_map) { // If there is an online link that isn't loopback or IPv6 link-local, return // false. // `online_links` shouldn't ever contain a loopback address, but keep the // check as it is clearer and harmless. // // NOTE(2023-05-26): `online_links` only contains links with *both* // IFF_LOWER_UP and IFF_UP, which is stricter than the // HaveOnlyLoopbackAddressesUsingGetifaddrs() check above. LOWER_UP means // the physical link layer is up and IFF_UP means the interface is // administratively up. This new behavior might even be desirable, but if // this causes issues it will need to be reverted. if (online_links.contains(ifaddrmsg.ifa_index) && !address.IsLoopback() && !(address.IsIPv6() && address.IsLinkLocal())) { return false; } } return true; } #endif // BUILDFLAG(IS_LINUX) } // namespace void RunHaveOnlyLoopbackAddressesJob( base::OnceCallback finished_cb) { #if BUILDFLAG(IS_LINUX) // On Linux, this check can be fast if it accesses only network information // that's cached by NetworkChangeNotifier, so there's no need to post this // task to a thread pool. If HaveOnlyLoopbackAddressesFast() *is* posted to a // different thread, it can cause a TSAN error when also setting a mock // NetworkChangeNotifier in tests. So it's important to not run off the main // thread if using cached, global information. AddressMapOwnerLinux* address_map_owner = NetworkChangeNotifier::GetAddressMapOwner(); if (address_map_owner) { // Post `finished_cb` to avoid the bug-prone sometimes-synchronous behavior, // which is only useful in latency-sensitive situations. base::SequencedTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(std::move(finished_cb), HaveOnlyLoopbackAddressesFast(address_map_owner))); return; } #endif // BUILDFLAG(IS_LINUX) base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, base::BindOnce(&HaveOnlyLoopbackAddressesSlow), std::move(finished_cb)); } } // namespace net