1// Copyright (c) 2011 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 "net/proxy/proxy_resolver_js_bindings.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "base/string_util.h"
9#include "net/base/address_list.h"
10#include "net/base/mock_host_resolver.h"
11#include "net/base/net_errors.h"
12#include "net/base/net_log.h"
13#include "net/base/net_log_unittest.h"
14#include "net/base/net_util.h"
15#include "net/base/sys_addrinfo.h"
16#include "net/proxy/proxy_resolver_request_context.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19namespace net {
20
21namespace {
22
23// This is a HostResolver that synchronously resolves all hosts to the
24// following address list of length 3:
25//     192.168.1.1
26//     172.22.34.1
27//     200.100.1.2
28class MockHostResolverWithMultipleResults : public HostResolver {
29 public:
30  // HostResolver methods:
31  virtual int Resolve(const RequestInfo& info,
32                      AddressList* addresses,
33                      CompletionCallback* callback,
34                      RequestHandle* out_req,
35                      const BoundNetLog& net_log) {
36    // Build up the result list (in reverse).
37    AddressList temp_list = ResolveIPLiteral("200.100.1.2");
38    temp_list = PrependAddressToList("172.22.34.1", temp_list);
39    temp_list = PrependAddressToList("192.168.1.1", temp_list);
40    *addresses = temp_list;
41    return OK;
42  }
43  virtual void CancelRequest(RequestHandle req) {}
44  virtual void AddObserver(Observer* observer) {}
45  virtual void RemoveObserver(Observer* observer) {}
46  virtual void Shutdown() {}
47
48 private:
49  ~MockHostResolverWithMultipleResults() {}
50
51  // Resolves an IP literal to an address list.
52  AddressList ResolveIPLiteral(const char* ip_literal) {
53    AddressList result;
54    int rv = SystemHostResolverProc(ip_literal,
55                                    ADDRESS_FAMILY_UNSPECIFIED,
56                                    0,
57                                    &result, NULL);
58    EXPECT_EQ(OK, rv);
59    EXPECT_EQ(NULL, result.head()->ai_next);
60    return result;
61  }
62
63  // Builds an AddressList that is |ip_literal| + |address_list|.
64  // |orig_list| must not be empty.
65  AddressList PrependAddressToList(const char* ip_literal,
66                                   const AddressList& orig_list) {
67    // Build an addrinfo for |ip_literal|.
68    AddressList result = ResolveIPLiteral(ip_literal);
69
70    struct addrinfo* result_head = const_cast<struct addrinfo*>(result.head());
71
72    // Temporarily append |orig_list| to |result|.
73    result_head->ai_next = const_cast<struct addrinfo*>(orig_list.head());
74
75    // Make a copy of the concatenated list.
76    AddressList concatenated;
77    concatenated.Copy(result.head(), true);
78
79    // Restore |result| (so it is freed properly).
80    result_head->ai_next = NULL;
81
82    return concatenated;
83  }
84};
85
86class MockFailingHostResolver : public HostResolver {
87 public:
88  MockFailingHostResolver() : count_(0) {}
89
90  // HostResolver methods:
91  virtual int Resolve(const RequestInfo& info,
92                      AddressList* addresses,
93                      CompletionCallback* callback,
94                      RequestHandle* out_req,
95                      const BoundNetLog& net_log) {
96    count_++;
97    return ERR_NAME_NOT_RESOLVED;
98  }
99
100  virtual void CancelRequest(RequestHandle req) {}
101  virtual void AddObserver(Observer* observer) {}
102  virtual void RemoveObserver(Observer* observer) {}
103  virtual void Shutdown() {}
104
105  // Returns the number of times Resolve() has been called.
106  int count() const { return count_; }
107  void ResetCount() { count_ = 0; }
108
109 private:
110  int count_;
111};
112
113TEST(ProxyResolverJSBindingsTest, DnsResolve) {
114  scoped_ptr<MockHostResolver> host_resolver(new MockHostResolver);
115
116  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
117  scoped_ptr<ProxyResolverJSBindings> bindings(
118      ProxyResolverJSBindings::CreateDefault(host_resolver.get(), NULL));
119
120  std::string ip_address;
121
122  // Empty string is not considered a valid host (even though on some systems
123  // requesting this will resolve to localhost).
124  host_resolver->rules()->AddSimulatedFailure("");
125  EXPECT_FALSE(bindings->DnsResolve("", &ip_address));
126
127  // Should call through to the HostResolver.
128  host_resolver->rules()->AddRule("google.com", "192.168.1.1");
129  EXPECT_TRUE(bindings->DnsResolve("google.com", &ip_address));
130  EXPECT_EQ("192.168.1.1", ip_address);
131
132  // Resolve failures should give empty string.
133  host_resolver->rules()->AddSimulatedFailure("fail");
134  EXPECT_FALSE(bindings->DnsResolve("fail", &ip_address));
135
136  // TODO(eroman): would be nice to have an IPV6 test here too, but that
137  // won't work on all systems.
138}
139
140TEST(ProxyResolverJSBindingsTest, MyIpAddress) {
141  scoped_ptr<MockHostResolver> host_resolver(new MockHostResolver);
142
143  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
144  scoped_ptr<ProxyResolverJSBindings> bindings(
145      ProxyResolverJSBindings::CreateDefault(host_resolver.get(), NULL));
146
147  // Our IP address is always going to be 127.0.0.1, since we are using a
148  // mock host resolver.
149  std::string my_ip_address;
150  EXPECT_TRUE(bindings->MyIpAddress(&my_ip_address));
151
152  EXPECT_EQ("127.0.0.1", my_ip_address);
153}
154
155// Tests that the regular PAC functions restrict results to IPv4,
156// but that the Microsoft extensions to PAC do not. We test this
157// by seeing whether ADDRESS_FAMILY_IPV4 or ADDRESS_FAMILY_UNSPECIFIED
158// was passed into to the host resolver.
159//
160//   Restricted to IPv4 address family:
161//     myIpAddress()
162//     dnsResolve()
163//
164//   Unrestricted address family:
165//     myIpAddressEx()
166//     dnsResolveEx()
167TEST(ProxyResolverJSBindingsTest, RestrictAddressFamily) {
168  scoped_ptr<MockHostResolver> host_resolver(new MockHostResolver);
169
170  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
171  scoped_ptr<ProxyResolverJSBindings> bindings(
172      ProxyResolverJSBindings::CreateDefault(host_resolver.get(), NULL));
173
174  // Make it so requests resolve to particular address patterns based on family:
175  //  IPV4_ONLY --> 192.168.1.*
176  //  UNSPECIFIED --> 192.168.2.1
177  host_resolver->rules()->AddRuleForAddressFamily(
178      "foo", ADDRESS_FAMILY_IPV4, "192.168.1.1");
179  host_resolver->rules()->AddRuleForAddressFamily(
180      "*", ADDRESS_FAMILY_IPV4, "192.168.1.2");
181  host_resolver->rules()->AddRuleForAddressFamily(
182      "foo", ADDRESS_FAMILY_UNSPECIFIED, "192.168.2.1");
183  host_resolver->rules()->AddRuleForAddressFamily(
184      "*", ADDRESS_FAMILY_UNSPECIFIED, "192.168.2.2");
185
186  // Verify that our mock setups works as expected, and we get different results
187  // depending if the address family was IPV4_ONLY or not.
188  HostResolver::RequestInfo info(HostPortPair("foo", 80));
189  AddressList address_list;
190  EXPECT_EQ(OK, host_resolver->Resolve(info, &address_list, NULL, NULL,
191                                       BoundNetLog()));
192  EXPECT_EQ("192.168.2.1", NetAddressToString(address_list.head()));
193
194  info.set_address_family(ADDRESS_FAMILY_IPV4);
195  EXPECT_EQ(OK, host_resolver->Resolve(info, &address_list, NULL, NULL,
196                                       BoundNetLog()));
197  EXPECT_EQ("192.168.1.1", NetAddressToString(address_list.head()));
198
199  std::string ip_address;
200  // Now the actual test.
201  EXPECT_TRUE(bindings->MyIpAddress(&ip_address));
202  EXPECT_EQ("192.168.1.2", ip_address);  // IPv4 restricted.
203
204  EXPECT_TRUE(bindings->DnsResolve("foo", &ip_address));
205  EXPECT_EQ("192.168.1.1", ip_address);  // IPv4 restricted.
206
207  EXPECT_TRUE(bindings->DnsResolve("foo2", &ip_address));
208  EXPECT_EQ("192.168.1.2", ip_address);  // IPv4 restricted.
209
210  EXPECT_TRUE(bindings->MyIpAddressEx(&ip_address));
211  EXPECT_EQ("192.168.2.2", ip_address);  // Unrestricted.
212
213  EXPECT_TRUE(bindings->DnsResolveEx("foo", &ip_address));
214  EXPECT_EQ("192.168.2.1", ip_address);  // Unrestricted.
215
216  EXPECT_TRUE(bindings->DnsResolveEx("foo2", &ip_address));
217  EXPECT_EQ("192.168.2.2", ip_address);  // Unrestricted.
218}
219
220// Test that myIpAddressEx() and dnsResolveEx() both return a semi-colon
221// separated list of addresses (as opposed to the non-Ex versions which
222// just return the first result).
223TEST(ProxyResolverJSBindingsTest, ExFunctionsReturnList) {
224  scoped_ptr<HostResolver> host_resolver(
225      new MockHostResolverWithMultipleResults);
226
227  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
228  scoped_ptr<ProxyResolverJSBindings> bindings(
229      ProxyResolverJSBindings::CreateDefault(host_resolver.get(), NULL));
230
231  std::string ip_addresses;
232
233  EXPECT_TRUE(bindings->MyIpAddressEx(&ip_addresses));
234  EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2", ip_addresses);
235
236  EXPECT_TRUE(bindings->DnsResolveEx("FOO", &ip_addresses));
237  EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2", ip_addresses);
238}
239
240TEST(ProxyResolverJSBindingsTest, PerRequestDNSCache) {
241  scoped_ptr<MockFailingHostResolver> host_resolver(
242      new MockFailingHostResolver);
243
244  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
245  scoped_ptr<ProxyResolverJSBindings> bindings(
246      ProxyResolverJSBindings::CreateDefault(host_resolver.get(), NULL));
247
248  std::string ip_address;
249
250  // Call DnsResolve() 4 times for the same hostname -- this should issue
251  // 4 separate calls to the underlying host resolver, since there is no
252  // current request context.
253  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
254  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
255  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
256  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
257  EXPECT_EQ(4, host_resolver->count());
258
259  host_resolver->ResetCount();
260
261  // Now setup a per-request context, and try the same experiment -- we
262  // expect the underlying host resolver to receive only 1 request this time,
263  // since it will service the others from the per-request DNS cache.
264  HostCache cache(50,
265                  base::TimeDelta::FromMinutes(10),
266                  base::TimeDelta::FromMinutes(10));
267  ProxyResolverRequestContext context(NULL, &cache);
268  bindings->set_current_request_context(&context);
269
270  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
271  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
272  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
273  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
274  EXPECT_EQ(1, host_resolver->count());
275
276  host_resolver->ResetCount();
277
278  // The "Ex" version shares this same cache, however since the flags
279  // are different it won't reuse this particular entry.
280  EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address));
281  EXPECT_EQ(1, host_resolver->count());
282  EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address));
283  EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address));
284  EXPECT_EQ(1, host_resolver->count());
285
286  bindings->set_current_request_context(NULL);
287}
288
289// Test that when a binding is called, it logs to the per-request NetLog.
290TEST(ProxyResolverJSBindingsTest, NetLog) {
291  scoped_ptr<MockFailingHostResolver> host_resolver(
292      new MockFailingHostResolver);
293
294  CapturingNetLog global_log(CapturingNetLog::kUnbounded);
295
296  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
297  scoped_ptr<ProxyResolverJSBindings> bindings(
298      ProxyResolverJSBindings::CreateDefault(host_resolver.get(), &global_log));
299
300  // Attach a capturing NetLog as the current request's log stream.
301  CapturingNetLog log(CapturingNetLog::kUnbounded);
302  BoundNetLog bound_log(NetLog::Source(NetLog::SOURCE_NONE, 0), &log);
303  ProxyResolverRequestContext context(&bound_log, NULL);
304  bindings->set_current_request_context(&context);
305
306  std::string ip_address;
307  net::CapturingNetLog::EntryList entries;
308  log.GetEntries(&entries);
309  ASSERT_EQ(0u, entries.size());
310
311  // Call all the bindings. Each call should be logging something to
312  // our NetLog.
313
314  bindings->MyIpAddress(&ip_address);
315
316  log.GetEntries(&entries);
317  EXPECT_EQ(2u, entries.size());
318  EXPECT_TRUE(LogContainsBeginEvent(
319      entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS));
320  EXPECT_TRUE(LogContainsEndEvent(
321      entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS));
322
323  bindings->MyIpAddressEx(&ip_address);
324
325  log.GetEntries(&entries);
326  EXPECT_EQ(4u, entries.size());
327  EXPECT_TRUE(LogContainsBeginEvent(
328      entries, 2, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX));
329  EXPECT_TRUE(LogContainsEndEvent(
330      entries, 3, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX));
331
332  bindings->DnsResolve("foo", &ip_address);
333
334  log.GetEntries(&entries);
335  EXPECT_EQ(6u, entries.size());
336  EXPECT_TRUE(LogContainsBeginEvent(
337      entries, 4, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE));
338  EXPECT_TRUE(LogContainsEndEvent(
339      entries, 5, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE));
340
341  bindings->DnsResolveEx("foo", &ip_address);
342
343  log.GetEntries(&entries);
344  EXPECT_EQ(8u, entries.size());
345  EXPECT_TRUE(LogContainsBeginEvent(
346      entries, 6, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX));
347  EXPECT_TRUE(LogContainsEndEvent(
348      entries, 7, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX));
349
350  // Nothing has been emitted globally yet.
351  net::CapturingNetLog::EntryList global_log_entries;
352  global_log.GetEntries(&global_log_entries);
353  EXPECT_EQ(0u, global_log_entries.size());
354
355  bindings->OnError(30, string16());
356
357  log.GetEntries(&entries);
358  EXPECT_EQ(9u, entries.size());
359  EXPECT_TRUE(LogContainsEvent(
360      entries, 8, NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
361      NetLog::PHASE_NONE));
362
363  // We also emit errors to the top-level log stream.
364  global_log.GetEntries(&global_log_entries);
365  EXPECT_EQ(1u, global_log_entries.size());
366  EXPECT_TRUE(LogContainsEvent(
367      global_log_entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
368      NetLog::PHASE_NONE));
369
370  bindings->Alert(string16());
371
372  log.GetEntries(&entries);
373  EXPECT_EQ(10u, entries.size());
374  EXPECT_TRUE(LogContainsEvent(
375      entries, 9, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
376      NetLog::PHASE_NONE));
377
378  // We also emit javascript alerts to the top-level log stream.
379  global_log.GetEntries(&global_log_entries);
380  EXPECT_EQ(2u, global_log_entries.size());
381  EXPECT_TRUE(LogContainsEvent(
382      global_log_entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
383      NetLog::PHASE_NONE));
384}
385
386}  // namespace
387
388}  // namespace net
389