Home | History | Annotate | Download | only in proxy
      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