David Ghandehari | 9e5b587 | 2016-07-28 09:50:04 -0700 | [diff] [blame] | 1 | // 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 | |
| 29 | namespace net { |
| 30 | |
| 31 | namespace { |
| 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 |
| 38 | class 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 | |
| 54 | class 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 | |
| 76 | class 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 | |
| 99 | TEST(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 | |
| 126 | TEST(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() |
| 153 | TEST(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). |
| 213 | TEST(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 | |
| 232 | TEST(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. |
| 280 | TEST(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 |