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 19 namespace net { 20 21 namespace { 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 28 class 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 86 class 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 113 TEST(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 140 TEST(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() 167 TEST(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). 223 TEST(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 240 TEST(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. 290 TEST(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