1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/dns/loopback_only.h"
6
7 #include "base/functional/bind.h"
8 #include "base/functional/callback.h"
9 #include "base/logging.h"
10 #include "base/task/sequenced_task_runner.h"
11 #include "base/task/task_traits.h"
12 #include "base/task/thread_pool.h"
13 #include "base/threading/scoped_blocking_call.h"
14 #include "build/build_config.h"
15 #include "net/base/network_change_notifier.h"
16 #include "net/base/network_interfaces.h"
17 #include "net/base/sys_addrinfo.h"
18
19 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
20 #include <net/if.h>
21 #if BUILDFLAG(IS_ANDROID)
22 #include "net/android/network_library.h"
23 #else // BUILDFLAG(IS_ANDROID)
24 #include <ifaddrs.h>
25 #endif // BUILDFLAG(IS_ANDROID)
26 #endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
27
28 #if BUILDFLAG(IS_LINUX)
29 #include <linux/rtnetlink.h>
30 #include "net/base/address_map_linux.h"
31 #include "net/base/address_tracker_linux.h"
32 #include "net/base/network_interfaces_linux.h"
33 #endif
34
35 namespace net {
36
37 namespace {
38
39 #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)) || BUILDFLAG(IS_FUCHSIA)
HaveOnlyLoopbackAddressesUsingGetifaddrs()40 bool HaveOnlyLoopbackAddressesUsingGetifaddrs() {
41 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
42 base::BlockingType::MAY_BLOCK);
43 struct ifaddrs* interface_addr = nullptr;
44 int rv = getifaddrs(&interface_addr);
45 if (rv != 0) {
46 DVPLOG(1) << "getifaddrs() failed";
47 return false;
48 }
49
50 bool result = true;
51 for (struct ifaddrs* interface = interface_addr; interface != nullptr;
52 interface = interface->ifa_next) {
53 if (!(IFF_UP & interface->ifa_flags)) {
54 continue;
55 }
56 if (IFF_LOOPBACK & interface->ifa_flags) {
57 continue;
58 }
59 const struct sockaddr* addr = interface->ifa_addr;
60 if (!addr) {
61 continue;
62 }
63 if (addr->sa_family == AF_INET6) {
64 // Safe cast since this is AF_INET6.
65 const struct sockaddr_in6* addr_in6 =
66 reinterpret_cast<const struct sockaddr_in6*>(addr);
67 const struct in6_addr* sin6_addr = &addr_in6->sin6_addr;
68 if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_LINKLOCAL(sin6_addr)) {
69 continue;
70 }
71 }
72 if (addr->sa_family != AF_INET6 && addr->sa_family != AF_INET) {
73 continue;
74 }
75
76 result = false;
77 break;
78 }
79 freeifaddrs(interface_addr);
80 return result;
81 }
82 #endif // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)) ||
83 // BUILDFLAG(IS_FUCHSIA)
84
85 // This implementation will always be posted to a thread pool.
HaveOnlyLoopbackAddressesSlow()86 bool HaveOnlyLoopbackAddressesSlow() {
87 #if BUILDFLAG(IS_WIN)
88 // TODO(wtc): implement with the GetAdaptersAddresses function.
89 NOTIMPLEMENTED();
90 return false;
91 #elif BUILDFLAG(IS_ANDROID)
92 return android::HaveOnlyLoopbackAddresses();
93 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
94 return HaveOnlyLoopbackAddressesUsingGetifaddrs();
95 #endif // defined(various platforms)
96 }
97
98 #if BUILDFLAG(IS_LINUX)
99 // This implementation can run on the main thread as it will not block.
HaveOnlyLoopbackAddressesFast(AddressMapOwnerLinux * address_map_owner)100 bool HaveOnlyLoopbackAddressesFast(AddressMapOwnerLinux* address_map_owner) {
101 // The AddressMapOwnerLinux has already cached all the information necessary
102 // to determine if only loopback addresses exist.
103 AddressMapOwnerLinux::AddressMap address_map =
104 address_map_owner->GetAddressMap();
105 std::unordered_set<int> online_links = address_map_owner->GetOnlineLinks();
106 for (const auto& [address, ifaddrmsg] : address_map) {
107 // If there is an online link that isn't loopback or IPv6 link-local, return
108 // false.
109 // `online_links` shouldn't ever contain a loopback address, but keep the
110 // check as it is clearer and harmless.
111 //
112 // NOTE(2023-05-26): `online_links` only contains links with *both*
113 // IFF_LOWER_UP and IFF_UP, which is stricter than the
114 // HaveOnlyLoopbackAddressesUsingGetifaddrs() check above. LOWER_UP means
115 // the physical link layer is up and IFF_UP means the interface is
116 // administratively up. This new behavior might even be desirable, but if
117 // this causes issues it will need to be reverted.
118 if (online_links.contains(ifaddrmsg.ifa_index) && !address.IsLoopback() &&
119 !(address.IsIPv6() && address.IsLinkLocal())) {
120 return false;
121 }
122 }
123
124 return true;
125 }
126 #endif // BUILDFLAG(IS_LINUX)
127
128 } // namespace
129
RunHaveOnlyLoopbackAddressesJob(base::OnceCallback<void (bool)> finished_cb)130 void RunHaveOnlyLoopbackAddressesJob(
131 base::OnceCallback<void(bool)> finished_cb) {
132 #if BUILDFLAG(IS_LINUX)
133 // On Linux, this check can be fast if it accesses only network information
134 // that's cached by NetworkChangeNotifier, so there's no need to post this
135 // task to a thread pool. If HaveOnlyLoopbackAddressesFast() *is* posted to a
136 // different thread, it can cause a TSAN error when also setting a mock
137 // NetworkChangeNotifier in tests. So it's important to not run off the main
138 // thread if using cached, global information.
139 AddressMapOwnerLinux* address_map_owner =
140 NetworkChangeNotifier::GetAddressMapOwner();
141 if (address_map_owner) {
142 // Post `finished_cb` to avoid the bug-prone sometimes-synchronous behavior,
143 // which is only useful in latency-sensitive situations.
144 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
145 FROM_HERE,
146 base::BindOnce(std::move(finished_cb),
147 HaveOnlyLoopbackAddressesFast(address_map_owner)));
148 return;
149 }
150 #endif // BUILDFLAG(IS_LINUX)
151
152 base::ThreadPool::PostTaskAndReplyWithResult(
153 FROM_HERE,
154 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
155 base::BindOnce(&HaveOnlyLoopbackAddressesSlow), std::move(finished_cb));
156 }
157
158 } // namespace net
159