123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- // Copyright 2018 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 "services/network/proxy_auto_config_library.h"
- #include <set>
- #include "base/memory/raw_ptr.h"
- #include "net/base/address_list.h"
- #include "net/base/ip_address.h"
- #include "net/base/network_interfaces.h"
- #include "net/dns/host_resolver_proc.h"
- #include "net/socket/client_socket_factory.h"
- #include "net/socket/udp_client_socket.h"
- namespace network {
- namespace {
- enum class Mode {
- kMyIpAddress,
- kMyIpAddressEx,
- };
- // Helper used to accumulate and select the best candidate IP addresses.
- //
- // myIpAddress() is a broken API available to PAC scripts.
- // It has the problematic definition of:
- // "Returns the IP address of the host machine."
- //
- // This has ambiguity on what should happen for multi-homed hosts which may have
- // multiple IP addresses to choose from. To be unambiguous we would need to
- // know which hosts is going to be connected to, in order to use the outgoing
- // IP for that request.
- //
- // However at this point that is not known, as the proxy still hasn't been
- // decided.
- //
- // The strategy used here is to prioritize the IP address that would be used
- // for connecting to the public internet by testing which interface is used for
- // connecting to 8.8.8.8 and 2001:4860:4860::8888 (public IPs).
- //
- // If that fails, we will try resolving the machine's hostname, and also probing
- // for routes in the private IP space.
- //
- // Link-local IP addresses are not generally returned, however may be if no
- // other IP was found by the probes.
- class MyIpAddressImpl {
- public:
- MyIpAddressImpl() = default;
- MyIpAddressImpl(const MyIpAddressImpl&) = delete;
- MyIpAddressImpl& operator=(const MyIpAddressImpl&) = delete;
- // Used for mocking the socket dependency.
- void SetSocketFactoryForTest(net::ClientSocketFactory* socket_factory) {
- override_socket_factory_ = socket_factory;
- }
- // Used for mocking the DNS dependency.
- void SetDNSResultForTest(const net::AddressList& addrs) {
- override_dns_result_ = std::make_unique<net::AddressList>(addrs);
- }
- net::IPAddressList Run(Mode mode) {
- DCHECK(candidate_ips_.empty());
- DCHECK(link_local_ips_.empty());
- DCHECK(!done_);
- mode_ = mode;
- // Try several different methods to obtain IP addresses.
- TestPublicInternetRoutes();
- TestResolvingHostname();
- TestPrivateIPRoutes();
- return mode_ == Mode::kMyIpAddress ? GetResultForMyIpAddress()
- : GetResultForMyIpAddressEx();
- }
- private:
- // Adds |address| to the result.
- void Add(const net::IPAddress& address) {
- if (done_)
- return;
- // Don't consider loopback addresses (ex: 127.0.0.1). These can notably be
- // returned when probing addresses associated with the hostname.
- if (address.IsLoopback())
- return;
- if (!seen_ips_.insert(address).second)
- return; // Duplicate IP address.
- // Link-local addresses are only used as a last-resort if there are no
- // better addresses.
- if (address.IsLinkLocal()) {
- link_local_ips_.push_back(address);
- return;
- }
- // For legacy reasons IPv4 addresses are favored over IPv6 for myIpAddress()
- // - https://crbug.com/905126 - so this only stops the search when a IPv4
- // address is found.
- if ((mode_ == Mode::kMyIpAddress) && address.IsIPv4())
- done_ = true;
- candidate_ips_.push_back(address);
- }
- net::IPAddressList GetResultForMyIpAddress() const {
- DCHECK_EQ(Mode::kMyIpAddress, mode_);
- if (!candidate_ips_.empty())
- return GetSingleResultFavoringIPv4(candidate_ips_);
- if (!link_local_ips_.empty())
- return GetSingleResultFavoringIPv4(link_local_ips_);
- return {};
- }
- net::IPAddressList GetResultForMyIpAddressEx() const {
- DCHECK_EQ(Mode::kMyIpAddressEx, mode_);
- if (!candidate_ips_.empty())
- return candidate_ips_;
- if (!link_local_ips_.empty()) {
- // Note that only a single link-local address is returned here, even
- // though multiple could be returned for this API. See
- // http://crbug.com/905366 before expanding this.
- return GetSingleResultFavoringIPv4(link_local_ips_);
- }
- return {};
- }
- // Tests what source IP address would be used for sending a UDP packet to the
- // given destination IP. This does not hit the network and should be fast.
- void TestRoute(const net::IPAddress& destination_ip) {
- if (done_)
- return;
- net::ClientSocketFactory* socket_factory =
- override_socket_factory_
- ? override_socket_factory_.get()
- : net::ClientSocketFactory::GetDefaultFactory();
- auto socket = socket_factory->CreateDatagramClientSocket(
- net::DatagramSocket::DEFAULT_BIND, nullptr, net::NetLogSource());
- net::IPEndPoint destination(destination_ip, /*port=*/80);
- if (socket->Connect(destination) != net::OK)
- return;
- net::IPEndPoint source;
- if (socket->GetLocalAddress(&source) != net::OK)
- return;
- Add(source.address());
- }
- void TestPublicInternetRoutes() {
- if (done_)
- return;
- // 8.8.8.8 and 2001:4860:4860::8888 are Google DNS.
- TestRoute(net::IPAddress(8, 8, 8, 8));
- TestRoute(net::IPAddress(0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x88, 0x88));
- MarkAsDoneIfHaveCandidates();
- }
- // Marks the current search as done if candidate IPs have been found.
- //
- // This is used to stop exploring for IPs if any of the high-level tests find
- // a match (i.e. either the public internet route test, or hostname test, or
- // private route test found something).
- //
- // In the case of myIpAddressEx() this means it will be conservative in which
- // IPs it returns and not enumerate the full set. See http://crbug.com/905366
- // before expanding that policy.
- void MarkAsDoneIfHaveCandidates() {
- if (!candidate_ips_.empty())
- done_ = true;
- }
- void TestPrivateIPRoutes() {
- if (done_)
- return;
- // Representative IP from each range in RFC 1918.
- TestRoute(net::IPAddress(10, 0, 0, 0));
- TestRoute(net::IPAddress(172, 16, 0, 0));
- TestRoute(net::IPAddress(192, 168, 0, 0));
- // Representative IP for Unique Local Address (FC00::/7).
- TestRoute(
- net::IPAddress(0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
- MarkAsDoneIfHaveCandidates();
- }
- void TestResolvingHostname() {
- if (done_)
- return;
- net::AddressList addrlist;
- int resolver_error;
- if (override_dns_result_) {
- addrlist = *override_dns_result_;
- resolver_error = addrlist.empty() ? net::ERR_NAME_NOT_RESOLVED : net::OK;
- } else {
- resolver_error = SystemHostResolverCall(
- net::GetHostName(), net::AddressFamily::ADDRESS_FAMILY_UNSPECIFIED, 0,
- &addrlist,
- /*os_error=*/nullptr);
- }
- if (resolver_error != net::OK)
- return;
- for (const auto& e : addrlist.endpoints())
- Add(e.address());
- MarkAsDoneIfHaveCandidates();
- }
- static net::IPAddressList GetSingleResultFavoringIPv4(
- const net::IPAddressList& ips) {
- for (const auto& ip : ips) {
- if (ip.IsIPv4())
- return {ip};
- }
- if (!ips.empty())
- return {ips.front()};
- return {};
- }
- std::set<net::IPAddress> seen_ips_;
- // The preferred ordered candidate IPs so far.
- net::IPAddressList candidate_ips_;
- // The link-local IP addresses seen so far (not part of |candidate_ips_|).
- net::IPAddressList link_local_ips_;
- // The operation being carried out.
- Mode mode_;
- // Whether the search for results has completed.
- //
- // Once "done", calling Add() will not change the final result. This is used
- // to short-circuit early.
- bool done_ = false;
- raw_ptr<net::ClientSocketFactory> override_socket_factory_ = nullptr;
- std::unique_ptr<net::AddressList> override_dns_result_;
- };
- } // namespace
- net::IPAddressList PacMyIpAddress() {
- MyIpAddressImpl impl;
- return impl.Run(Mode::kMyIpAddress);
- }
- net::IPAddressList PacMyIpAddressEx() {
- MyIpAddressImpl impl;
- return impl.Run(Mode::kMyIpAddressEx);
- }
- net::IPAddressList PacMyIpAddressForTest(
- net::ClientSocketFactory* socket_factory,
- const net::AddressList& dns_result) {
- MyIpAddressImpl impl;
- impl.SetSocketFactoryForTest(socket_factory);
- impl.SetDNSResultForTest(dns_result);
- return impl.Run(Mode::kMyIpAddress);
- }
- net::IPAddressList PacMyIpAddressExForTest(
- net::ClientSocketFactory* socket_factory,
- const net::AddressList& dns_result) {
- MyIpAddressImpl impl;
- impl.SetSocketFactoryForTest(socket_factory);
- impl.SetDNSResultForTest(dns_result);
- return impl.Run(Mode::kMyIpAddressEx);
- }
- } // namespace network
|