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