1 // Copyright 2018 The Chromium Authors. All rights reserved.
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 "services/network/proxy_auto_config_library.h"
6 
7 #include "net/base/address_list.h"
8 #include "net/base/ip_address.h"
9 #include "net/base/network_interfaces.h"
10 #include "net/dns/host_resolver_proc.h"
11 #include "net/socket/client_socket_factory.h"
12 #include "net/socket/udp_client_socket.h"
13 
14 namespace network {
15 
16 namespace {
17 
18 enum class Mode {
19   kMyIpAddress,
20   kMyIpAddressEx,
21 };
22 
23 // Helper used to accumulate and select the best candidate IP addresses.
24 //
25 // myIpAddress() is a broken API available to PAC scripts.
26 // It has the problematic definition of:
27 // "Returns the IP address of the host machine."
28 //
29 // This has ambiguity on what should happen for multi-homed hosts which may have
30 // multiple IP addresses to choose from. To be unambiguous we would need to
31 // know which hosts is going to be connected to, in order to use the outgoing
32 // IP for that request.
33 //
34 // However at this point that is not known, as the proxy still hasn't been
35 // decided.
36 //
37 // The strategy used here is to prioritize the IP address that would be used
38 // for connecting to the public internet by testing which interface is used for
39 // connecting to 8.8.8.8 and 2001:4860:4860::8888 (public IPs).
40 //
41 // If that fails, we will try resolving the machine's hostname, and also probing
42 // for routes in the private IP space.
43 //
44 // Link-local IP addresses are not generally returned, however may be if no
45 // other IP was found by the probes.
46 class MyIpAddressImpl {
47  public:
48   MyIpAddressImpl() = default;
49 
50   // Used for mocking the socket dependency.
SetSocketFactoryForTest(net::ClientSocketFactory * socket_factory)51   void SetSocketFactoryForTest(net::ClientSocketFactory* socket_factory) {
52     override_socket_factory_ = socket_factory;
53   }
54 
55   // Used for mocking the DNS dependency.
SetDNSResultForTest(const net::AddressList & addrs)56   void SetDNSResultForTest(const net::AddressList& addrs) {
57     override_dns_result_ = std::make_unique<net::AddressList>(addrs);
58   }
59 
Run(Mode mode)60   net::IPAddressList Run(Mode mode) {
61     DCHECK(candidate_ips_.empty());
62     DCHECK(link_local_ips_.empty());
63     DCHECK(!done_);
64 
65     mode_ = mode;
66 
67     // Try several different methods to obtain IP addresses.
68     TestPublicInternetRoutes();
69     TestResolvingHostname();
70     TestPrivateIPRoutes();
71 
72     return mode_ == Mode::kMyIpAddress ? GetResultForMyIpAddress()
73                                        : GetResultForMyIpAddressEx();
74   }
75 
76  private:
77   // Adds |address| to the result.
Add(const net::IPAddress & address)78   void Add(const net::IPAddress& address) {
79     if (done_)
80       return;
81 
82     // Don't consider loopback addresses (ex: 127.0.0.1). These can notably be
83     // returned when probing addresses associated with the hostname.
84     if (address.IsLoopback())
85       return;
86 
87     if (!seen_ips_.insert(address).second)
88       return;  // Duplicate IP address.
89 
90     // Link-local addresses are only used as a last-resort if there are no
91     // better addresses.
92     if (address.IsLinkLocal()) {
93       link_local_ips_.push_back(address);
94       return;
95     }
96 
97     // For legacy reasons IPv4 addresses are favored over IPv6 for myIpAddress()
98     // - https://crbug.com/905126 - so this only stops the search when a IPv4
99     // address is found.
100     if ((mode_ == Mode::kMyIpAddress) && address.IsIPv4())
101       done_ = true;
102 
103     candidate_ips_.push_back(address);
104   }
105 
GetResultForMyIpAddress() const106   net::IPAddressList GetResultForMyIpAddress() const {
107     DCHECK_EQ(Mode::kMyIpAddress, mode_);
108 
109     if (!candidate_ips_.empty())
110       return GetSingleResultFavoringIPv4(candidate_ips_);
111 
112     if (!link_local_ips_.empty())
113       return GetSingleResultFavoringIPv4(link_local_ips_);
114 
115     return {};
116   }
117 
GetResultForMyIpAddressEx() const118   net::IPAddressList GetResultForMyIpAddressEx() const {
119     DCHECK_EQ(Mode::kMyIpAddressEx, mode_);
120 
121     if (!candidate_ips_.empty())
122       return candidate_ips_;
123 
124     if (!link_local_ips_.empty()) {
125       // Note that only a single link-local address is returned here, even
126       // though multiple could be returned for this API. See
127       // http://crbug.com/905366 before expanding this.
128       return GetSingleResultFavoringIPv4(link_local_ips_);
129     }
130 
131     return {};
132   }
133 
134   // Tests what source IP address would be used for sending a UDP packet to the
135   // given destination IP. This does not hit the network and should be fast.
TestRoute(const net::IPAddress & destination_ip)136   void TestRoute(const net::IPAddress& destination_ip) {
137     if (done_)
138       return;
139 
140     net::ClientSocketFactory* socket_factory =
141         override_socket_factory_
142             ? override_socket_factory_
143             : net::ClientSocketFactory::GetDefaultFactory();
144 
145     auto socket = socket_factory->CreateDatagramClientSocket(
146         net::DatagramSocket::DEFAULT_BIND, nullptr, net::NetLogSource());
147 
148     net::IPEndPoint destination(destination_ip, /*port=*/80);
149 
150     if (socket->Connect(destination) != net::OK)
151       return;
152 
153     net::IPEndPoint source;
154     if (socket->GetLocalAddress(&source) != net::OK)
155       return;
156 
157     Add(source.address());
158   }
159 
TestPublicInternetRoutes()160   void TestPublicInternetRoutes() {
161     if (done_)
162       return;
163 
164     // 8.8.8.8 and 2001:4860:4860::8888 are Google DNS.
165     TestRoute(net::IPAddress(8, 8, 8, 8));
166     TestRoute(net::IPAddress(0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0, 0, 0, 0, 0,
167                              0, 0, 0, 0x88, 0x88));
168 
169     MarkAsDoneIfHaveCandidates();
170   }
171 
172   // Marks the current search as done if candidate IPs have been found.
173   //
174   // This is used to stop exploring for IPs if any of the high-level tests find
175   // a match (i.e. either the public internet route test, or hostname test, or
176   // private route test found something).
177   //
178   // In the case of myIpAddressEx() this means it will be conservative in which
179   // IPs it returns and not enumerate the full set. See http://crbug.com/905366
180   // before expanding that policy.
MarkAsDoneIfHaveCandidates()181   void MarkAsDoneIfHaveCandidates() {
182     if (!candidate_ips_.empty())
183       done_ = true;
184   }
185 
TestPrivateIPRoutes()186   void TestPrivateIPRoutes() {
187     if (done_)
188       return;
189 
190     // Representative IP from each range in RFC 1918.
191     TestRoute(net::IPAddress(10, 0, 0, 0));
192     TestRoute(net::IPAddress(172, 16, 0, 0));
193     TestRoute(net::IPAddress(192, 168, 0, 0));
194 
195     // Representative IP for Unique Local Address (FC00::/7).
196     TestRoute(
197         net::IPAddress(0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
198 
199     MarkAsDoneIfHaveCandidates();
200   }
201 
TestResolvingHostname()202   void TestResolvingHostname() {
203     if (done_)
204       return;
205 
206     net::AddressList addrlist;
207 
208     int resolver_error;
209 
210     if (override_dns_result_) {
211       addrlist = *override_dns_result_;
212       resolver_error = addrlist.empty() ? net::ERR_NAME_NOT_RESOLVED : net::OK;
213     } else {
214       resolver_error = SystemHostResolverCall(
215           net::GetHostName(), net::AddressFamily::ADDRESS_FAMILY_UNSPECIFIED, 0,
216           &addrlist,
217           /*os_error=*/nullptr);
218     }
219 
220     if (resolver_error != net::OK)
221       return;
222 
223     for (const auto& e : addrlist.endpoints())
224       Add(e.address());
225 
226     MarkAsDoneIfHaveCandidates();
227   }
228 
GetSingleResultFavoringIPv4(const net::IPAddressList & ips)229   static net::IPAddressList GetSingleResultFavoringIPv4(
230       const net::IPAddressList& ips) {
231     for (const auto& ip : ips) {
232       if (ip.IsIPv4())
233         return {ip};
234     }
235 
236     if (!ips.empty())
237       return {ips.front()};
238 
239     return {};
240   }
241 
242   std::set<net::IPAddress> seen_ips_;
243 
244   // The preferred ordered candidate IPs so far.
245   net::IPAddressList candidate_ips_;
246 
247   // The link-local IP addresses seen so far (not part of |candidate_ips_|).
248   net::IPAddressList link_local_ips_;
249 
250   // The operation being carried out.
251   Mode mode_;
252 
253   // Whether the search for results has completed.
254   //
255   // Once "done", calling Add() will not change the final result. This is used
256   // to short-circuit early.
257   bool done_ = false;
258 
259   net::ClientSocketFactory* override_socket_factory_ = nullptr;
260   std::unique_ptr<net::AddressList> override_dns_result_;
261 
262   DISALLOW_COPY_AND_ASSIGN(MyIpAddressImpl);
263 };
264 
265 }  // namespace
266 
PacMyIpAddress()267 net::IPAddressList PacMyIpAddress() {
268   MyIpAddressImpl impl;
269   return impl.Run(Mode::kMyIpAddress);
270 }
271 
PacMyIpAddressEx()272 net::IPAddressList PacMyIpAddressEx() {
273   MyIpAddressImpl impl;
274   return impl.Run(Mode::kMyIpAddressEx);
275 }
276 
PacMyIpAddressForTest(net::ClientSocketFactory * socket_factory,const net::AddressList & dns_result)277 net::IPAddressList PacMyIpAddressForTest(
278     net::ClientSocketFactory* socket_factory,
279     const net::AddressList& dns_result) {
280   MyIpAddressImpl impl;
281   impl.SetSocketFactoryForTest(socket_factory);
282   impl.SetDNSResultForTest(dns_result);
283   return impl.Run(Mode::kMyIpAddress);
284 }
285 
PacMyIpAddressExForTest(net::ClientSocketFactory * socket_factory,const net::AddressList & dns_result)286 net::IPAddressList PacMyIpAddressExForTest(
287     net::ClientSocketFactory* socket_factory,
288     const net::AddressList& dns_result) {
289   MyIpAddressImpl impl;
290   impl.SetSocketFactoryForTest(socket_factory);
291   impl.SetDNSResultForTest(dns_result);
292   return impl.Run(Mode::kMyIpAddressEx);
293 }
294 
295 }  // namespace network
296