diff options
-rw-r--r-- | net/base/address_list.cc | 50 | ||||
-rw-r--r-- | net/base/address_list.h | 15 | ||||
-rw-r--r-- | net/base/mock_host_resolver.cc | 80 | ||||
-rw-r--r-- | net/base/mock_host_resolver.h | 24 | ||||
-rw-r--r-- | net/http/http_auth_handler_negotiate.h | 8 | ||||
-rw-r--r-- | net/http/http_auth_handler_negotiate_unittest.cc | 96 | ||||
-rw-r--r-- | net/net.gyp | 1 | ||||
-rw-r--r-- | net/socket/socks_client_socket_unittest.cc | 2 |
8 files changed, 242 insertions, 34 deletions
diff --git a/net/base/address_list.cc b/net/base/address_list.cc index cf7f0d5..1736a19 100644 --- a/net/base/address_list.cc +++ b/net/base/address_list.cc @@ -13,6 +13,14 @@ namespace net { namespace { +char* do_strdup(const char* src) { +#if defined(OS_WIN) + return _strdup(src); +#else + return strdup(src); +#endif +} + // Make a copy of |info| (the dynamically-allocated parts are copied as well). // If |recursive| is true, chained entries via ai_next are copied too. // Copy returned by this function should be deleted using @@ -27,11 +35,7 @@ struct addrinfo* CreateCopyOfAddrinfo(const struct addrinfo* info, // ai_canonname is a NULL-terminated string. if (info->ai_canonname) { -#ifdef OS_WIN - copy->ai_canonname = _strdup(info->ai_canonname); -#else - copy->ai_canonname = strdup(info->ai_canonname); -#endif + copy->ai_canonname = do_strdup(info->ai_canonname); } // ai_addr is a buffer of length ai_addrlen. @@ -162,21 +166,45 @@ void AddressList::Reset() { } // static -AddressList AddressList::CreateIPv6Address(unsigned char data[16]) { +AddressList AddressList::CreateIPv4Address(unsigned char data[4], + const std::string& canonical_name) { struct addrinfo* ai = new addrinfo; memset(ai, 0, sizeof(addrinfo)); + ai->ai_family = AF_INET; + ai->ai_socktype = SOCK_STREAM; + const size_t sockaddr_in_size = sizeof(struct sockaddr_in); + ai->ai_addrlen = sockaddr_in_size; + if (!canonical_name.empty()) + ai->ai_canonname = do_strdup(canonical_name.c_str()); + + struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>( + new char[sockaddr_in_size]); + memset(addr, 0, sockaddr_in_size); + addr->sin_family = AF_INET; + memcpy(&addr->sin_addr, data, 4); + ai->ai_addr = reinterpret_cast<struct sockaddr*>(addr); + + return AddressList(new Data(ai, false /*is_system_created*/)); +} +// static +AddressList AddressList::CreateIPv6Address(unsigned char data[16], + const std::string& canonical_name) { + struct addrinfo* ai = new addrinfo; + memset(ai, 0, sizeof(addrinfo)); ai->ai_family = AF_INET6; ai->ai_socktype = SOCK_STREAM; - ai->ai_addrlen = sizeof(struct sockaddr_in6); + const size_t sockaddr_in6_size = sizeof(struct sockaddr_in6); + ai->ai_addrlen = sockaddr_in6_size; + if (!canonical_name.empty()) + ai->ai_canonname = do_strdup(canonical_name.c_str()); struct sockaddr_in6* addr6 = reinterpret_cast<struct sockaddr_in6*>( - new char[ai->ai_addrlen]); - memset(addr6, 0, sizeof(struct sockaddr_in6)); - - ai->ai_addr = reinterpret_cast<struct sockaddr*>(addr6); + new char[sockaddr_in6_size]); + memset(addr6, 0, sockaddr_in6_size); addr6->sin6_family = AF_INET6; memcpy(&addr6->sin6_addr, data, 16); + ai->ai_addr = reinterpret_cast<struct sockaddr*>(addr6); return AddressList(new Data(ai, false /*is_system_created*/)); } diff --git a/net/base/address_list.h b/net/base/address_list.h index e8ecb63..ce17645 100644 --- a/net/base/address_list.h +++ b/net/base/address_list.h @@ -59,8 +59,19 @@ class AddressList { // empty state as when first constructed. void Reset(); - // Used by unit-tests to manually set the TCP socket address. - static AddressList CreateIPv6Address(unsigned char data[16]); + // Used by unit-tests to manually create an IPv4 AddressList. |data| should + // be an IPv4 address in network order (big endian). + // If |canonical_name| is non-empty, it will be duplicated in the + // ai_canonname field of the addrinfo struct. + static AddressList CreateIPv4Address(unsigned char data[4], + const std::string& canonical_name); + + // Used by unit-tests to manually create an IPv6 AddressList. |data| should + // be an IPv6 address in network order (big endian). + // If |canonical_name| is non-empty, it will be duplicated in the + // ai_canonname field of the addrinfo struct. + static AddressList CreateIPv6Address(unsigned char data[16], + const std::string& canonical_name); // Get access to the head of the addrinfo list. const struct addrinfo* head() const { return data_->head; } diff --git a/net/base/mock_host_resolver.cc b/net/base/mock_host_resolver.cc index d99b24e..573d57e 100644 --- a/net/base/mock_host_resolver.cc +++ b/net/base/mock_host_resolver.cc @@ -13,10 +13,14 @@ namespace net { namespace { -// Fills |addrlist| with a socket address for |host| which should be an -// IPv6 literal. Returns OK on success. -int ResolveIPV6LiteralUsingGURL(const std::string& host, - AddressList* addrlist) { + +// Fills |*addrlist| with a socket address for |host| which should be an +// IPv6 literal without enclosing brackets. If |canonical_name| is non-empty +// it is used as the DNS canonical name for the host. Returns OK on success, +// ERR_UNEXPECTED otherwise. +int CreateIPv6Address(const std::string& host, + const std::string& canonical_name, + AddressList* addrlist) { // GURL expects the hostname to be surrounded with brackets. std::string host_brackets = "[" + host + "]"; url_parse::Component host_comp(0, host_brackets.size()); @@ -31,7 +35,26 @@ int ResolveIPV6LiteralUsingGURL(const std::string& host, return ERR_UNEXPECTED; } - *addrlist = AddressList::CreateIPv6Address(ipv6_addr); + *addrlist = AddressList::CreateIPv6Address(ipv6_addr, canonical_name); + return OK; +} + +// Fills |*addrlist| with a socket address for |host| which should be an +// IPv4 literal. If |canonical_name| is non-empty it is used as the DNS +// canonical name for the host. Returns OK on success, ERR_UNEXPECTED otherwise. +int CreateIPv4Address(const std::string& host, + const std::string& canonical_name, + AddressList* addrlist) { + unsigned char ipv4_addr[4]; + url_parse::Component host_comp(0, host.size()); + int num_components; + url_canon::CanonHostInfo::Family family = url_canon::IPv4AddressToNumber( + host.data(), host_comp, ipv4_addr, &num_components); + if (family != url_canon::CanonHostInfo::IPV4) { + LOG(WARNING) << "Not an IPv4 literal: " << host; + return ERR_UNEXPECTED; + } + *addrlist = AddressList::CreateIPv4Address(ipv4_addr, canonical_name); return OK; } @@ -104,23 +127,30 @@ struct RuleBasedHostResolverProc::Rule { kResolverTypeFail, kResolverTypeSystem, kResolverTypeIPV6Literal, + kResolverTypeIPV4Literal, }; ResolverType resolver_type; std::string host_pattern; AddressFamily address_family; + HostResolverFlags host_resolver_flags; std::string replacement; + std::string canonical_name; int latency_ms; // In milliseconds. Rule(ResolverType resolver_type, const std::string& host_pattern, AddressFamily address_family, + HostResolverFlags host_resolver_flags, const std::string& replacement, + const std::string& canonical_name, int latency_ms) : resolver_type(resolver_type), host_pattern(host_pattern), address_family(address_family), + host_resolver_flags(host_resolver_flags), replacement(replacement), + canonical_name(canonical_name), latency_ms(latency_ms) {} }; @@ -143,16 +173,32 @@ void RuleBasedHostResolverProc::AddRuleForAddressFamily( const std::string& replacement) { DCHECK(!replacement.empty()); Rule rule(Rule::kResolverTypeSystem, host_pattern, - address_family, replacement, 0); + address_family, 0, replacement, "", 0); + rules_.push_back(rule); +} + +void RuleBasedHostResolverProc::AddIPv4Rule(const std::string& host_pattern, + const std::string& ipv4_literal, + const std::string& canonical_name) { + Rule rule(Rule::kResolverTypeIPV4Literal, + host_pattern, + ADDRESS_FAMILY_UNSPECIFIED, + canonical_name.empty() ? 0 : HOST_RESOLVER_CANONNAME, + ipv4_literal, + canonical_name, + 0); rules_.push_back(rule); } void RuleBasedHostResolverProc::AddIPv6Rule(const std::string& host_pattern, - const std::string& ipv6_literal) { + const std::string& ipv6_literal, + const std::string& canonical_name) { Rule rule(Rule::kResolverTypeIPV6Literal, host_pattern, ADDRESS_FAMILY_UNSPECIFIED, + canonical_name.empty() ? 0 : HOST_RESOLVER_CANONNAME, ipv6_literal, + canonical_name, 0); rules_.push_back(rule); } @@ -163,21 +209,21 @@ void RuleBasedHostResolverProc::AddRuleWithLatency( int latency_ms) { DCHECK(!replacement.empty()); Rule rule(Rule::kResolverTypeSystem, host_pattern, - ADDRESS_FAMILY_UNSPECIFIED, replacement, latency_ms); + ADDRESS_FAMILY_UNSPECIFIED, 0, replacement, "", latency_ms); rules_.push_back(rule); } void RuleBasedHostResolverProc::AllowDirectLookup( const std::string& host_pattern) { Rule rule(Rule::kResolverTypeSystem, host_pattern, - ADDRESS_FAMILY_UNSPECIFIED, "", 0); + ADDRESS_FAMILY_UNSPECIFIED, 0, "", "", 0); rules_.push_back(rule); } void RuleBasedHostResolverProc::AddSimulatedFailure( const std::string& host_pattern) { Rule rule(Rule::kResolverTypeFail, host_pattern, - ADDRESS_FAMILY_UNSPECIFIED, "", 0); + ADDRESS_FAMILY_UNSPECIFIED, 0, "", "", 0); rules_.push_back(rule); } @@ -190,8 +236,14 @@ int RuleBasedHostResolverProc::Resolve(const std::string& host, bool matches_address_family = r->address_family == ADDRESS_FAMILY_UNSPECIFIED || r->address_family == address_family; - - if (matches_address_family && MatchPatternASCII(host, r->host_pattern)) { + // Flags match if all of the bitflags in host_resolver_flags are enabled + // in the rule's host_resolver_flags. However, the rule may have additional + // flags specified, in which case the flags should still be considered a + // match. + bool matches_flags = (r->host_resolver_flags & host_resolver_flags) == + host_resolver_flags; + if (matches_flags && matches_address_family && + MatchPatternASCII(host, r->host_pattern)) { if (r->latency_ms != 0) PlatformThread::Sleep(r->latency_ms); @@ -209,7 +261,9 @@ int RuleBasedHostResolverProc::Resolve(const std::string& host, host_resolver_flags, addrlist); case Rule::kResolverTypeIPV6Literal: - return ResolveIPV6LiteralUsingGURL(effective_host, addrlist); + return CreateIPv6Address(effective_host, r->canonical_name, addrlist); + case Rule::kResolverTypeIPV4Literal: + return CreateIPv4Address(effective_host, r->canonical_name, addrlist); default: NOTREACHED(); return ERR_UNEXPECTED; diff --git a/net/base/mock_host_resolver.h b/net/base/mock_host_resolver.h index 5a1df7f..ad89a48 100644 --- a/net/base/mock_host_resolver.h +++ b/net/base/mock_host_resolver.h @@ -107,12 +107,26 @@ class RuleBasedHostResolverProc : public HostResolverProc { AddressFamily address_family, const std::string& replacement); - // Same as AddRule(), but the replacement is expected to be an IPV6 literal. - // You should use this in place of AddRule(), since the system's host resolver - // may not support IPv6 literals on all systems. Whereas this variant - // constructs the socket address directly so it will always work. + // Same as AddRule(), but the replacement is expected to be an IPV4 literal. + // This can be used in place of AddRule() to bypass the system's host + // resolver. |ipv4_literal| must be an IPv4 literal, typically taking the form + // of "[0-255].[0-255].[0-255].[0-255]". + // If |canonical-name| is non-empty, it is copied to the resulting AddressList + // but does not impact DNS resolution. + void AddIPv4Rule(const std::string& host_pattern, + const std::string& ipv4_literal, + const std::string& canonical_name); + + // Same as AddRule(), but |ipv6_literal| is expected to be an IPV6 literal, + // without enclosing brackets. You should use this in place of AddRule(), + // since the system's host resolver may not support IPv6 literals on all + // systems. This variant constructs the socket address directly so it will + // always work. + // If |canonical-name| is non-empty, it is copied to the resulting AddressList + // but does not impact DNS resolution. void AddIPv6Rule(const std::string& host_pattern, - const std::string& ipv6_literal); + const std::string& ipv6_literal, + const std::string& canonical_name); void AddRuleWithLatency(const std::string& host_pattern, const std::string& replacement, diff --git a/net/http/http_auth_handler_negotiate.h b/net/http/http_auth_handler_negotiate.h index 7bb9426..77add14 100644 --- a/net/http/http_auth_handler_negotiate.h +++ b/net/http/http_auth_handler_negotiate.h @@ -108,6 +108,12 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { CompletionCallback* callback, const BoundNetLog& net_log); +#if defined(OS_WIN) + // These are public for unit tests + std::wstring CreateSPN(const AddressList& address_list, const GURL& orign); + const std::wstring& spn() const { return spn_; } +#endif // defined(OS_WIN) + protected: virtual bool Init(HttpAuth::ChallengeTokenizer* challenge); @@ -116,8 +122,6 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { #if defined(OS_WIN) void OnResolveCanonicalName(int result); - std::wstring CreateSPN(const AddressList& address_list, const GURL& orign); - HttpAuthSSPI auth_sspi_; AddressList address_list_; scoped_ptr<SingleRequestHostResolver> single_resolve_; diff --git a/net/http/http_auth_handler_negotiate_unittest.cc b/net/http/http_auth_handler_negotiate_unittest.cc new file mode 100644 index 0000000..6ed882f --- /dev/null +++ b/net/http/http_auth_handler_negotiate_unittest.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/http/http_auth_handler_negotiate.h" + +#include "net/base/mock_host_resolver.h" +#include "net/base/net_errors.h" +#include "net/base/test_completion_callback.h" +#if defined(OS_WIN) +#include "net/http/mock_sspi_library_win.h" +#endif +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +// TODO(cbentzel): Remove the OS_WIN condition once Negotiate is supported +// on all platforms. +#if defined(OS_WIN) +namespace { + +void CreateHandler(bool disable_cname_lookup, bool include_port, + const std::string& url_string, + SSPILibrary* sspi_library, + scoped_refptr<HttpAuthHandlerNegotiate>* handler) { + *handler = new HttpAuthHandlerNegotiate(sspi_library, 50, NULL, + disable_cname_lookup, + include_port); + std::string challenge = "Negotiate"; + HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end()); + GURL gurl(url_string); + (*handler)->InitFromChallenge(&props, HttpAuth::AUTH_SERVER, gurl); +} + +} // namespace + +TEST(HttpAuthHandlerNegotiateTest, DisableCname) { + MockSSPILibrary mock_library; + scoped_refptr<HttpAuthHandlerNegotiate> auth_handler; + CreateHandler(true, false, "http://alias:500", &mock_library, &auth_handler); + EXPECT_FALSE(auth_handler->NeedsCanonicalName()); + EXPECT_EQ(L"HTTP/alias", auth_handler->spn()); +} + +TEST(HttpAuthHandlerNegotiateTest, DisableCnameStandardPort) { + MockSSPILibrary mock_library; + scoped_refptr<HttpAuthHandlerNegotiate> auth_handler; + CreateHandler(true, true, "http://alias:80", &mock_library, &auth_handler); + EXPECT_FALSE(auth_handler->NeedsCanonicalName()); + EXPECT_EQ(L"HTTP/alias", auth_handler->spn()); +} + +TEST(HttpAuthHandlerNegotiateTest, DisableCnameNonstandardPort) { + MockSSPILibrary mock_library; + scoped_refptr<HttpAuthHandlerNegotiate> auth_handler; + CreateHandler(true, true, "http://alias:500", &mock_library, &auth_handler); + EXPECT_FALSE(auth_handler->NeedsCanonicalName()); + EXPECT_EQ(L"HTTP/alias:500", auth_handler->spn()); +} + +TEST(HttpAuthHandlerNegotiateTest, CnameSync) { + MockSSPILibrary mock_library; + scoped_refptr<HttpAuthHandlerNegotiate> auth_handler; + CreateHandler(false, false, "http://alias:500", &mock_library, &auth_handler); + EXPECT_TRUE(auth_handler->NeedsCanonicalName()); + MockHostResolver* mock_resolver = new MockHostResolver(); + scoped_refptr<HostResolver> scoped_resolver(mock_resolver); + mock_resolver->set_synchronous_mode(true); + mock_resolver->rules()->AddIPv4Rule("alias", "10.0.0.2", + "canonical.example.com"); + TestCompletionCallback callback; + EXPECT_EQ(OK, auth_handler->ResolveCanonicalName(mock_resolver, &callback, + NULL)); + EXPECT_EQ(L"HTTP/canonical.example.com", auth_handler->spn()); +} + +TEST(HttpAuthHandlerNegotiateTest, CnameAsync) { + MockSSPILibrary mock_library; + scoped_refptr<HttpAuthHandlerNegotiate> auth_handler; + CreateHandler(false, false, "http://alias:500", &mock_library, &auth_handler); + EXPECT_TRUE(auth_handler->NeedsCanonicalName()); + MockHostResolver* mock_resolver = new MockHostResolver(); + scoped_refptr<HostResolver> scoped_resolver(mock_resolver); + mock_resolver->set_synchronous_mode(false); + mock_resolver->rules()->AddIPv4Rule("alias", "10.0.0.2", + "canonical.example.com"); + TestCompletionCallback callback; + EXPECT_EQ(ERR_IO_PENDING, auth_handler->ResolveCanonicalName(mock_resolver, + &callback, + NULL)); + EXPECT_EQ(OK, callback.WaitForResult()); + EXPECT_EQ(L"HTTP/canonical.example.com", auth_handler->spn()); +} +#endif // defined(OS_WIN) + +} // namespace net diff --git a/net/net.gyp b/net/net.gyp index 7ef7bf2..a7f3095 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -674,6 +674,7 @@ 'http/http_auth_handler_basic_unittest.cc', 'http/http_auth_handler_digest_unittest.cc', 'http/http_auth_handler_factory_unittest.cc', + 'http/http_auth_handler_negotiate_unittest.cc', 'http/http_auth_sspi_win_unittest.cc', 'http/http_auth_unittest.cc', 'http/http_byte_range_unittest.cc', diff --git a/net/socket/socks_client_socket_unittest.cc b/net/socket/socks_client_socket_unittest.cc index eb2ab6a..f05a7db 100644 --- a/net/socket/socks_client_socket_unittest.cc +++ b/net/socket/socks_client_socket_unittest.cc @@ -341,7 +341,7 @@ TEST_F(SOCKSClientSocketTest, SOCKS4AFailedDNS) { TEST_F(SOCKSClientSocketTest, SOCKS4AIfDomainInIPv6) { const char hostname[] = "an.ipv6.address"; - host_resolver_->rules()->AddIPv6Rule(hostname, "2001:db8:8714:3a90::12"); + host_resolver_->rules()->AddIPv6Rule(hostname, "2001:db8:8714:3a90::12", ""); std::string request(kSOCKS4aInitialRequest, arraysize(kSOCKS4aInitialRequest)); |