Skip to content
Snippets Groups Projects
Commit e607ee67 authored by cbentzel@chromium.org's avatar cbentzel@chromium.org
Browse files

Adds unit tests for how HttpAuthHandlerNegotiate creates SPNs.

BUG=None
TEST=net_unittests --gtest_filter="*HttpAuthHandlerNegotiate*"

Review URL: http://codereview.chromium.org/1705001

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45821 0039d316-1c4b-4281-b951-d872f2087c98
parent 0825bc6f
No related merge requests found
......@@ -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*/));
}
......
......@@ -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; }
......
......@@ -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;
......
......@@ -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,
......
......@@ -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_;
......
// 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
......@@ -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',
......
......@@ -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));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment