xref: /aosp_15_r20/external/cronet/net/dns/address_sorter_posix.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 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/address_sorter_posix.h"
6 
7 #include <netinet/in.h>
8 
9 #include <memory>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/memory/raw_ptr.h"
14 #include "build/build_config.h"
15 
16 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_BSD)
17 #include <sys/socket.h>  // Must be included before ifaddrs.h.
18 #include <ifaddrs.h>
19 #include <net/if.h>
20 #include <string.h>
21 #include <sys/ioctl.h>
22 #if BUILDFLAG(IS_IOS)
23 // The code in the following header file is copied from [1]. This file has the
24 // minimum definitions needed to retrieve the IP attributes, since iOS SDK
25 // doesn't include a necessary header <netinet/in_var.h>.
26 // [1] https://chromium.googlesource.com/external/webrtc/+/master/rtc_base/mac_ifaddrs_converter.cc
27 #include "net/dns/netinet_in_var_ios.h"
28 #else
29 #include <netinet/in_var.h>
30 #endif  // BUILDFLAG(IS_IOS)
31 #endif
32 #include <vector>
33 
34 #include "base/containers/unique_ptr_adapters.h"
35 #include "base/logging.h"
36 #include "net/base/ip_endpoint.h"
37 #include "net/base/net_errors.h"
38 #include "net/log/net_log_source.h"
39 #include "net/socket/client_socket_factory.h"
40 #include "net/socket/datagram_client_socket.h"
41 
42 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
43 #include "net/base/address_tracker_linux.h"
44 #endif
45 
46 namespace net {
47 namespace {
48 // Address sorting is performed according to RFC3484 with revisions.
49 // http://tools.ietf.org/html/draft-ietf-6man-rfc3484bis-06
50 // Precedence and label are separate to support override through
51 // /etc/gai.conf.
52 
53 // Returns true if |p1| should precede |p2| in the table.
54 // Sorts table by decreasing prefix size to allow longest prefix matching.
ComparePolicy(const AddressSorterPosix::PolicyEntry & p1,const AddressSorterPosix::PolicyEntry & p2)55 bool ComparePolicy(const AddressSorterPosix::PolicyEntry& p1,
56                    const AddressSorterPosix::PolicyEntry& p2) {
57   return p1.prefix_length > p2.prefix_length;
58 }
59 
60 // Creates sorted PolicyTable from |table| with |size| entries.
LoadPolicy(const AddressSorterPosix::PolicyEntry * table,size_t size)61 AddressSorterPosix::PolicyTable LoadPolicy(
62     const AddressSorterPosix::PolicyEntry* table,
63     size_t size) {
64   AddressSorterPosix::PolicyTable result(table, table + size);
65   std::sort(result.begin(), result.end(), ComparePolicy);
66   return result;
67 }
68 
69 // Search |table| for matching prefix of |address|. |table| must be sorted by
70 // descending prefix (prefix of another prefix must be later in table).
GetPolicyValue(const AddressSorterPosix::PolicyTable & table,const IPAddress & address)71 unsigned GetPolicyValue(const AddressSorterPosix::PolicyTable& table,
72                         const IPAddress& address) {
73   if (address.IsIPv4())
74     return GetPolicyValue(table, ConvertIPv4ToIPv4MappedIPv6(address));
75   for (const auto& entry : table) {
76     IPAddress prefix(entry.prefix);
77     if (IPAddressMatchesPrefix(address, prefix, entry.prefix_length))
78       return entry.value;
79   }
80   NOTREACHED();
81   // The last entry is the least restrictive, so assume it's default.
82   return table.back().value;
83 }
84 
IsIPv6Multicast(const IPAddress & address)85 bool IsIPv6Multicast(const IPAddress& address) {
86   DCHECK(address.IsIPv6());
87   return address.bytes()[0] == 0xFF;
88 }
89 
GetIPv6MulticastScope(const IPAddress & address)90 AddressSorterPosix::AddressScope GetIPv6MulticastScope(
91     const IPAddress& address) {
92   DCHECK(address.IsIPv6());
93   return static_cast<AddressSorterPosix::AddressScope>(address.bytes()[1] &
94                                                        0x0F);
95 }
96 
IsIPv6Loopback(const IPAddress & address)97 bool IsIPv6Loopback(const IPAddress& address) {
98   DCHECK(address.IsIPv6());
99   return address == IPAddress::IPv6Localhost();
100 }
101 
IsIPv6LinkLocal(const IPAddress & address)102 bool IsIPv6LinkLocal(const IPAddress& address) {
103   DCHECK(address.IsIPv6());
104   // IN6_IS_ADDR_LINKLOCAL
105   return (address.bytes()[0] == 0xFE) && ((address.bytes()[1] & 0xC0) == 0x80);
106 }
107 
IsIPv6SiteLocal(const IPAddress & address)108 bool IsIPv6SiteLocal(const IPAddress& address) {
109   DCHECK(address.IsIPv6());
110   // IN6_IS_ADDR_SITELOCAL
111   return (address.bytes()[0] == 0xFE) && ((address.bytes()[1] & 0xC0) == 0xC0);
112 }
113 
GetScope(const AddressSorterPosix::PolicyTable & ipv4_scope_table,const IPAddress & address)114 AddressSorterPosix::AddressScope GetScope(
115     const AddressSorterPosix::PolicyTable& ipv4_scope_table,
116     const IPAddress& address) {
117   if (address.IsIPv6()) {
118     if (IsIPv6Multicast(address)) {
119       return GetIPv6MulticastScope(address);
120     } else if (IsIPv6Loopback(address) || IsIPv6LinkLocal(address)) {
121       return AddressSorterPosix::SCOPE_LINKLOCAL;
122     } else if (IsIPv6SiteLocal(address)) {
123       return AddressSorterPosix::SCOPE_SITELOCAL;
124     } else {
125       return AddressSorterPosix::SCOPE_GLOBAL;
126     }
127   } else if (address.IsIPv4()) {
128     return static_cast<AddressSorterPosix::AddressScope>(
129         GetPolicyValue(ipv4_scope_table, address));
130   } else {
131     NOTREACHED();
132     return AddressSorterPosix::SCOPE_NODELOCAL;
133   }
134 }
135 
136 // Default policy table. RFC 3484, Section 2.1.
137 const AddressSorterPosix::PolicyEntry kDefaultPrecedenceTable[] = {
138     // ::1/128 -- loopback
139     {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 128, 50},
140     // ::/0 -- any
141     {{}, 0, 40},
142     // ::ffff:0:0/96 -- IPv4 mapped
143     {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF}, 96, 35},
144     // 2002::/16 -- 6to4
145     {{
146          0x20,
147          0x02,
148      },
149      16,
150      30},
151     // 2001::/32 -- Teredo
152     {{0x20, 0x01, 0, 0}, 32, 5},
153     // fc00::/7 -- unique local address
154     {{0xFC}, 7, 3},
155     // ::/96 -- IPv4 compatible
156     {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 96, 1},
157     // fec0::/10 -- site-local expanded scope
158     {{0xFE, 0xC0}, 10, 1},
159     // 3ffe::/16 -- 6bone
160     {{0x3F, 0xFE}, 16, 1},
161 };
162 
163 const AddressSorterPosix::PolicyEntry kDefaultLabelTable[] = {
164     // ::1/128 -- loopback
165     {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 128, 0},
166     // ::/0 -- any
167     {{}, 0, 1},
168     // ::ffff:0:0/96 -- IPv4 mapped
169     {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF}, 96, 4},
170     // 2002::/16 -- 6to4
171     {{
172          0x20,
173          0x02,
174      },
175      16,
176      2},
177     // 2001::/32 -- Teredo
178     {{0x20, 0x01, 0, 0}, 32, 5},
179     // fc00::/7 -- unique local address
180     {{0xFC}, 7, 13},
181     // ::/96 -- IPv4 compatible
182     {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 96, 3},
183     // fec0::/10 -- site-local expanded scope
184     {{0xFE, 0xC0}, 10, 11},
185     // 3ffe::/16 -- 6bone
186     {{0x3F, 0xFE}, 16, 12},
187 };
188 
189 // Default mapping of IPv4 addresses to scope.
190 const AddressSorterPosix::PolicyEntry kDefaultIPv4ScopeTable[] = {
191     {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x7F},
192      104,
193      AddressSorterPosix::SCOPE_LINKLOCAL},
194     {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xA9, 0xFE},
195      112,
196      AddressSorterPosix::SCOPE_LINKLOCAL},
197     {{}, 0, AddressSorterPosix::SCOPE_GLOBAL},
198 };
199 
200 struct DestinationInfo {
201   IPEndPoint endpoint;
202   AddressSorterPosix::AddressScope scope;
203   unsigned precedence;
204   unsigned label;
205   AddressSorterPosix::SourceAddressInfo src;
206   std::unique_ptr<DatagramClientSocket> socket;
207   size_t common_prefix_length;
208   bool failed = false;
209 };
210 
211 // Returns true iff |dst_a| should precede |dst_b| in the address list.
212 // RFC 3484, section 6.
CompareDestinations(const DestinationInfo & dst_a,const DestinationInfo & dst_b)213 bool CompareDestinations(const DestinationInfo& dst_a,
214                          const DestinationInfo& dst_b) {
215   // Rule 1: Avoid unusable destinations.
216   // Nothing to do here because unusable destinations are already filtered out.
217 
218   // Rule 2: Prefer matching scope.
219   bool scope_match1 = (dst_a.src.scope == dst_a.scope);
220   bool scope_match2 = (dst_b.src.scope == dst_b.scope);
221   if (scope_match1 != scope_match2)
222     return scope_match1;
223 
224   // Rule 3: Avoid deprecated addresses.
225   if (dst_a.src.deprecated != dst_b.src.deprecated) {
226     return !dst_a.src.deprecated;
227   }
228 
229   // Rule 4: Prefer home addresses.
230   if (dst_a.src.home != dst_b.src.home) {
231     return dst_a.src.home;
232   }
233 
234   // Rule 5: Prefer matching label.
235   bool label_match1 = (dst_a.src.label == dst_a.label);
236   bool label_match2 = (dst_b.src.label == dst_b.label);
237   if (label_match1 != label_match2)
238     return label_match1;
239 
240   // Rule 6: Prefer higher precedence.
241   if (dst_a.precedence != dst_b.precedence)
242     return dst_a.precedence > dst_b.precedence;
243 
244   // Rule 7: Prefer native transport.
245   if (dst_a.src.native != dst_b.src.native) {
246     return dst_a.src.native;
247   }
248 
249   // Rule 8: Prefer smaller scope.
250   if (dst_a.scope != dst_b.scope)
251     return dst_a.scope < dst_b.scope;
252 
253   // Rule 9: Use longest matching prefix. Only for matching address families.
254   if (dst_a.endpoint.address().size() == dst_b.endpoint.address().size()) {
255     if (dst_a.common_prefix_length != dst_b.common_prefix_length)
256       return dst_a.common_prefix_length > dst_b.common_prefix_length;
257   }
258 
259   // Rule 10: Leave the order unchanged.
260   // stable_sort takes care of that.
261   return false;
262 }
263 
264 }  // namespace
265 
266 class AddressSorterPosix::SortContext {
267  public:
SortContext(size_t in_num_endpoints,AddressSorter::CallbackType callback,const AddressSorterPosix * sorter)268   SortContext(size_t in_num_endpoints,
269               AddressSorter::CallbackType callback,
270               const AddressSorterPosix* sorter)
271       : num_endpoints_(in_num_endpoints),
272         callback_(std::move(callback)),
273         sorter_(sorter) {}
274   ~SortContext() = default;
DidCompleteConnect(IPEndPoint dest,size_t info_index,int rv)275   void DidCompleteConnect(IPEndPoint dest, size_t info_index, int rv) {
276     ++num_completed_;
277     if (rv != OK) {
278       VLOG(1) << "Could not connect to " << dest.ToStringWithoutPort()
279               << " reason " << rv;
280       sort_list_[info_index].failed = true;
281     }
282 
283     MaybeFinishSort();
284   }
285 
sort_list()286   std::vector<DestinationInfo>& sort_list() { return sort_list_; }
287 
288  private:
MaybeFinishSort()289   void MaybeFinishSort() {
290     // Sort the list of endpoints only after each Connect call has been made.
291     if (num_completed_ != num_endpoints_) {
292       return;
293     }
294     for (auto& info : sort_list_) {
295       if (info.failed) {
296         continue;
297       }
298 
299       IPEndPoint src;
300       // Filter out unusable destinations.
301       int rv = info.socket->GetLocalAddress(&src);
302       if (rv != OK) {
303         LOG(WARNING) << "Could not get local address for "
304                      << info.endpoint.ToStringWithoutPort() << " reason " << rv;
305         info.failed = true;
306         continue;
307       }
308 
309       auto iter = sorter_->source_map_.find(src.address());
310       if (iter == sorter_->source_map_.end()) {
311         //  |src.address| may not be in the map if |source_info_| has not been
312         //  updated from the OS yet. It will be updated and HostCache cleared
313         //  soon, but we still want to sort, so fill in an empty
314         info.src = AddressSorterPosix::SourceAddressInfo();
315       } else {
316         info.src = iter->second;
317       }
318 
319       if (info.src.scope == AddressSorterPosix::SCOPE_UNDEFINED) {
320         sorter_->FillPolicy(src.address(), &info.src);
321       }
322 
323       if (info.endpoint.address().size() == src.address().size()) {
324         info.common_prefix_length =
325             std::min(CommonPrefixLength(info.endpoint.address(), src.address()),
326                      info.src.prefix_length);
327       }
328     }
329     std::erase_if(sort_list_, [](auto& element) { return element.failed; });
330     std::stable_sort(sort_list_.begin(), sort_list_.end(), CompareDestinations);
331 
332     std::vector<IPEndPoint> sorted_result;
333     for (const auto& info : sort_list_)
334       sorted_result.push_back(info.endpoint);
335 
336     CallbackType callback = std::move(callback_);
337     sorter_->FinishedSort(this);  // deletes this
338     std::move(callback).Run(true, std::move(sorted_result));
339   }
340 
341   const size_t num_endpoints_;
342   size_t num_completed_ = 0;
343   std::vector<DestinationInfo> sort_list_;
344   AddressSorter::CallbackType callback_;
345 
346   raw_ptr<const AddressSorterPosix> sorter_;
347 };
348 
AddressSorterPosix(ClientSocketFactory * socket_factory)349 AddressSorterPosix::AddressSorterPosix(ClientSocketFactory* socket_factory)
350     : socket_factory_(socket_factory),
351       precedence_table_(LoadPolicy(kDefaultPrecedenceTable,
352                                    std::size(kDefaultPrecedenceTable))),
353       label_table_(
354           LoadPolicy(kDefaultLabelTable, std::size(kDefaultLabelTable))),
355       ipv4_scope_table_(LoadPolicy(kDefaultIPv4ScopeTable,
356                                    std::size(kDefaultIPv4ScopeTable))) {
357   NetworkChangeNotifier::AddIPAddressObserver(this);
358   OnIPAddressChanged();
359 }
360 
~AddressSorterPosix()361 AddressSorterPosix::~AddressSorterPosix() {
362   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
363   NetworkChangeNotifier::RemoveIPAddressObserver(this);
364 }
365 
Sort(const std::vector<IPEndPoint> & endpoints,CallbackType callback) const366 void AddressSorterPosix::Sort(const std::vector<IPEndPoint>& endpoints,
367                               CallbackType callback) const {
368   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
369   sort_contexts_.insert(std::make_unique<SortContext>(
370       endpoints.size(), std::move(callback), this));
371   auto* sort_context = sort_contexts_.rbegin()->get();
372   for (const IPEndPoint& endpoint : endpoints) {
373     DestinationInfo info;
374     info.endpoint = endpoint;
375     info.scope = GetScope(ipv4_scope_table_, info.endpoint.address());
376     info.precedence =
377         GetPolicyValue(precedence_table_, info.endpoint.address());
378     info.label = GetPolicyValue(label_table_, info.endpoint.address());
379 
380     // Each socket can only be bound once.
381     info.socket = socket_factory_->CreateDatagramClientSocket(
382         DatagramSocket::DEFAULT_BIND, nullptr /* NetLog */, NetLogSource());
383     IPEndPoint dest = info.endpoint;
384     // Even though no packets are sent, cannot use port 0 in Connect.
385     if (dest.port() == 0) {
386       dest = IPEndPoint(dest.address(), /*port=*/80);
387     }
388     sort_context->sort_list().push_back(std::move(info));
389     size_t info_index = sort_context->sort_list().size() - 1;
390     // Destroying a SortContext destroys the underlying socket.
391     int rv = sort_context->sort_list().back().socket->ConnectAsync(
392         dest,
393         base::BindOnce(&AddressSorterPosix::SortContext::DidCompleteConnect,
394                        base::Unretained(sort_context), dest, info_index));
395     if (rv != ERR_IO_PENDING) {
396       sort_context->DidCompleteConnect(dest, info_index, rv);
397     }
398   }
399 }
400 
OnIPAddressChanged()401 void AddressSorterPosix::OnIPAddressChanged() {
402   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
403   source_map_.clear();
404 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
405   // TODO(crbug.com/1431364): This always returns nullptr on ChromeOS.
406   const AddressMapOwnerLinux* address_map_owner =
407       NetworkChangeNotifier::GetAddressMapOwner();
408   if (!address_map_owner) {
409     return;
410   }
411   AddressMapOwnerLinux::AddressMap map = address_map_owner->GetAddressMap();
412   for (const auto& [address, msg] : map) {
413     SourceAddressInfo& info = source_map_[address];
414     info.native = false;  // TODO(szym): obtain this via netlink.
415     info.deprecated = msg.ifa_flags & IFA_F_DEPRECATED;
416     info.home = msg.ifa_flags & IFA_F_HOMEADDRESS;
417     info.prefix_length = msg.ifa_prefixlen;
418     FillPolicy(address, &info);
419   }
420 #elif BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_BSD)
421   // It's not clear we will receive notification when deprecated flag changes.
422   // Socket for ioctl.
423   int ioctl_socket = socket(AF_INET6, SOCK_DGRAM, 0);
424   if (ioctl_socket < 0)
425     return;
426 
427   struct ifaddrs* addrs;
428   int rv = getifaddrs(&addrs);
429   if (rv < 0) {
430     LOG(WARNING) << "getifaddrs failed " << rv;
431     close(ioctl_socket);
432     return;
433   }
434 
435   for (struct ifaddrs* ifa = addrs; ifa != nullptr; ifa = ifa->ifa_next) {
436     IPEndPoint src;
437     if (!src.FromSockAddr(ifa->ifa_addr, ifa->ifa_addr->sa_len))
438       continue;
439     SourceAddressInfo& info = source_map_[src.address()];
440     // Note: no known way to fill in |native| and |home|.
441     info.native = info.home = info.deprecated = false;
442     if (ifa->ifa_addr->sa_family == AF_INET6) {
443       struct in6_ifreq ifr = {};
444       strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name) - 1);
445       DCHECK_LE(ifa->ifa_addr->sa_len, sizeof(ifr.ifr_ifru.ifru_addr));
446       memcpy(&ifr.ifr_ifru.ifru_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len);
447       rv = ioctl(ioctl_socket, SIOCGIFAFLAG_IN6, &ifr);
448       if (rv >= 0) {
449         info.deprecated = ifr.ifr_ifru.ifru_flags & IN6_IFF_DEPRECATED;
450       } else {
451         LOG(WARNING) << "SIOCGIFAFLAG_IN6 failed " << rv;
452       }
453     }
454     if (ifa->ifa_netmask) {
455       IPEndPoint netmask;
456       if (netmask.FromSockAddr(ifa->ifa_netmask, ifa->ifa_addr->sa_len)) {
457         info.prefix_length = MaskPrefixLength(netmask.address());
458       } else {
459         LOG(WARNING) << "FromSockAddr failed on netmask";
460       }
461     }
462     FillPolicy(src.address(), &info);
463   }
464   freeifaddrs(addrs);
465   close(ioctl_socket);
466 #endif
467 }
468 
FillPolicy(const IPAddress & address,SourceAddressInfo * info) const469 void AddressSorterPosix::FillPolicy(const IPAddress& address,
470                                     SourceAddressInfo* info) const {
471   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
472   info->scope = GetScope(ipv4_scope_table_, address);
473   info->label = GetPolicyValue(label_table_, address);
474 }
475 
FinishedSort(SortContext * sort_context) const476 void AddressSorterPosix::FinishedSort(SortContext* sort_context) const {
477   auto it = sort_contexts_.find(sort_context);
478   sort_contexts_.erase(it);
479 }
480 
481 // static
CreateAddressSorter()482 std::unique_ptr<AddressSorter> AddressSorter::CreateAddressSorter() {
483   return std::make_unique<AddressSorterPosix>(
484       ClientSocketFactory::GetDefaultFactory());
485 }
486 
487 }  // namespace net
488