summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/base/address_list.cc2
-rw-r--r--net/http/http_auth_handler.cc9
-rw-r--r--net/http/http_auth_handler.h20
-rw-r--r--net/http/http_auth_handler_factory.h10
-rw-r--r--net/http/http_auth_handler_negotiate.h40
-rw-r--r--net/http/http_auth_handler_negotiate_posix.cc15
-rw-r--r--net/http/http_auth_handler_negotiate_win.cc118
-rw-r--r--net/http/http_auth_handler_ntlm.cc12
-rw-r--r--net/http/http_auth_handler_ntlm.h3
-rw-r--r--net/http/http_auth_handler_ntlm_win.cc2
-rw-r--r--net/http/http_auth_sspi_win.cc15
-rw-r--r--net/http/http_auth_sspi_win.h8
-rw-r--r--net/http/http_network_transaction.cc30
-rw-r--r--net/http/http_network_transaction.h4
-rw-r--r--net/http/http_network_transaction_unittest.cc198
15 files changed, 454 insertions, 32 deletions
diff --git a/net/base/address_list.cc b/net/base/address_list.cc
index 55a5992..89c9496 100644
--- a/net/base/address_list.cc
+++ b/net/base/address_list.cc
@@ -135,7 +135,7 @@ int AddressList::GetPort() const {
bool AddressList::GetCanonicalName(std::string* canonical_name) const {
DCHECK(canonical_name);
- if (!data_->head || !data_->head->ai_canonname)
+ if (!data_ || !data_->head->ai_canonname)
return false;
canonical_name->assign(data_->head->ai_canonname);
return true;
diff --git a/net/http/http_auth_handler.cc b/net/http/http_auth_handler.cc
index e61de2f..7aa114e 100644
--- a/net/http/http_auth_handler.cc
+++ b/net/http/http_auth_handler.cc
@@ -5,6 +5,7 @@
#include "net/http/http_auth_handler.h"
#include "base/logging.h"
+#include "net/base/net_errors.h"
namespace net {
@@ -28,4 +29,12 @@ bool HttpAuthHandler::InitFromChallenge(
return ok;
}
+int HttpAuthHandler::ResolveCanonicalName(net::HostResolver* host_resolver,
+ CompletionCallback* callback,
+ const BoundNetLog& net_log) {
+ NOTREACHED();
+ LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED);
+ return ERR_NOT_IMPLEMENTED;
+}
+
} // namespace net
diff --git a/net/http/http_auth_handler.h b/net/http/http_auth_handler.h
index d2eae13..663e04a 100644
--- a/net/http/http_auth_handler.h
+++ b/net/http/http_auth_handler.h
@@ -8,10 +8,13 @@
#include <string>
#include "base/ref_counted.h"
+#include "net/base/completion_callback.h"
#include "net/http/http_auth.h"
namespace net {
+class BoundNetLog;
+class HostResolver;
class HttpRequestInfo;
class ProxyInfo;
@@ -81,6 +84,11 @@ class HttpAuthHandler : public base::RefCounted<HttpAuthHandler> {
// TODO(cbentzel): Add a pointer to Firefox documentation about risk.
virtual bool SupportsDefaultCredentials() { return false; }
+ // Returns whether the canonical DNS name for the origin host needs to be
+ // resolved. The Negotiate auth scheme typically uses the canonical DNS
+ // name when constructing the Kerberos SPN.
+ virtual bool NeedsCanonicalName() { return false; }
+
// TODO(cbentzel): Separate providing credentials from generating the
// authentication token in the API.
@@ -103,6 +111,14 @@ class HttpAuthHandler : public base::RefCounted<HttpAuthHandler> {
const ProxyInfo* proxy,
std::string* auth_token) = 0;
+ // Resolves the canonical name for the |origin_| host. The canonical
+ // name is used by the Negotiate scheme to generate a valid Kerberos
+ // SPN.
+ // The return value is a net error code.
+ virtual int ResolveCanonicalName(HostResolver* host_resolver,
+ CompletionCallback* callback,
+ const BoundNetLog& net_log);
+
protected:
enum Property {
ENCRYPTS_IDENTITY = 1 << 0,
@@ -121,14 +137,14 @@ class HttpAuthHandler : public base::RefCounted<HttpAuthHandler> {
// scheme_, realm_, score_, properties_
virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) = 0;
- // The lowercase auth-scheme {"basic", "digest", "ntlm", ...}
+ // The lowercase auth-scheme {"basic", "digest", "ntlm", "negotiate"}
std::string scheme_;
// The realm. Used by "basic" and "digest".
std::string realm_;
// The {scheme, host, port} for the authentication target. Used by "ntlm"
- // to construct the service principal name.
+ // and "negotiate" to construct the service principal name.
GURL origin_;
// The score for this challenge. Higher numbers are better.
diff --git a/net/http/http_auth_handler_factory.h b/net/http/http_auth_handler_factory.h
index f677455..587bb30 100644
--- a/net/http/http_auth_handler_factory.h
+++ b/net/http/http_auth_handler_factory.h
@@ -105,6 +105,13 @@ class HttpAuthHandlerRegistryFactory : public HttpAuthHandlerFactory {
void RegisterSchemeFactory(const std::string& scheme,
HttpAuthHandlerFactory* factory);
+ // Retrieve the factory for the specified |scheme|. If no factory exists
+ // for the |scheme|, NULL is returned. The returned factory must not be
+ // deleted by the caller, and it is guaranteed to be valid until either
+ // a new factory is registered for the same scheme, or until this
+ // registry factory is destroyed.
+ HttpAuthHandlerFactory* GetSchemeFactory(const std::string& scheme) const;
+
// Creates an auth handler by dispatching out to the registered factories
// based on the first token in |challenge|.
virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
@@ -113,9 +120,6 @@ class HttpAuthHandlerRegistryFactory : public HttpAuthHandlerFactory {
scoped_refptr<HttpAuthHandler>* handler);
private:
- // Retrieve the factory for the specified |scheme|
- HttpAuthHandlerFactory* GetSchemeFactory(const std::string& scheme) const;
-
typedef std::map<std::string, HttpAuthHandlerFactory*> FactoryMap;
FactoryMap factory_map_;
diff --git a/net/http/http_auth_handler_negotiate.h b/net/http/http_auth_handler_negotiate.h
index f34664c..3450fc4 100644
--- a/net/http/http_auth_handler_negotiate.h
+++ b/net/http/http_auth_handler_negotiate.h
@@ -9,6 +9,7 @@
#include <string>
+#include "net/base/address_list.h"
#include "net/http/http_auth_handler.h"
#include "net/http/http_auth_handler_factory.h"
@@ -18,6 +19,8 @@
namespace net {
+class SingleRequestHostResolver;
+
// Handler for WWW-Authenticate: Negotiate protocol.
//
// See http://tools.ietf.org/html/rfc4178 and http://tools.ietf.org/html/rfc4559
@@ -30,6 +33,22 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler {
Factory();
virtual ~Factory();
+ // |disable_cname_lookup()| and |set_disable_cname_lookup()| get/set whether
+ // the auth handlers generated by this factory should skip looking up the
+ // canonical DNS name of the the host that they are authenticating to when
+ // generating the SPN. The default value is false.
+ bool disable_cname_lookup() const { return disable_cname_lookup_; }
+ void set_disable_cname_lookup(bool disable_cname_lookup) {
+ disable_cname_lookup_ = disable_cname_lookup;
+ }
+
+ // |use_port()| and |set_use_port()| get/set whether the auth handlers
+ // generated by this factory should include the port number of the server
+ // they are authenticating to when constructing a Kerberos SPN. The default
+ // value is false.
+ bool use_port() const { return use_port_; }
+ void set_use_port(bool use_port) { use_port_ = use_port; }
+
virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
@@ -47,6 +66,8 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler {
}
#endif // defined(OS_WIN)
private:
+ bool disable_cname_lookup_;
+ bool use_port_;
#if defined(OS_WIN)
ULONG max_token_length_;
bool first_creation_;
@@ -56,7 +77,8 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler {
};
#if defined(OS_WIN)
- HttpAuthHandlerNegotiate(SSPILibrary* sspi_library, ULONG max_token_length);
+ HttpAuthHandlerNegotiate(SSPILibrary* sspi_library, ULONG max_token_length,
+ bool disable_cname_lookup, bool use_port);
#else
HttpAuthHandlerNegotiate();
#endif
@@ -67,6 +89,8 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler {
virtual bool SupportsDefaultCredentials();
+ virtual bool NeedsCanonicalName();
+
virtual int GenerateAuthToken(const std::wstring& username,
const std::wstring& password,
const HttpRequestInfo* request,
@@ -77,6 +101,10 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler {
const ProxyInfo* proxy,
std::string* auth_token);
+ virtual int ResolveCanonicalName(HostResolver* host_resolver,
+ CompletionCallback* callback,
+ const BoundNetLog& net_log);
+
protected:
virtual bool Init(HttpAuth::ChallengeTokenizer* challenge);
@@ -84,7 +112,17 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler {
~HttpAuthHandlerNegotiate();
#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_;
+ CompletionCallback* user_callback_;
+ CompletionCallbackImpl<HttpAuthHandlerNegotiate> resolve_cname_callback_;
+ bool disable_cname_lookup_;
+ bool use_port_;
+ std::wstring spn_;
#endif
};
diff --git a/net/http/http_auth_handler_negotiate_posix.cc b/net/http/http_auth_handler_negotiate_posix.cc
index 2b96597..7b57853 100644
--- a/net/http/http_auth_handler_negotiate_posix.cc
+++ b/net/http/http_auth_handler_negotiate_posix.cc
@@ -61,7 +61,20 @@ int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken(
return ERR_NOT_IMPLEMENTED;
}
-HttpAuthHandlerNegotiate::Factory::Factory() {
+bool HttpAuthHandlerNegotiate::NeedsCanonicalName() {
+ return false;
+}
+
+int HttpAuthHandlerNegotiate::ResolveCanonicalName(HostResolver* host_resolver,
+ CompletionCallback* callback,
+ const BoundNetLog& net_log) {
+ NOTREACHED();
+ LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED);
+ return ERR_NOT_IMPLEMENTED;
+}
+
+HttpAuthHandlerNegotiate::Factory::Factory()
+ : disable_cname_lookup_(false), use_port_(false) {
}
HttpAuthHandlerNegotiate::Factory::~Factory() {
diff --git a/net/http/http_auth_handler_negotiate_win.cc b/net/http/http_auth_handler_negotiate_win.cc
index fb849d4..6c6e767 100644
--- a/net/http/http_auth_handler_negotiate_win.cc
+++ b/net/http/http_auth_handler_negotiate_win.cc
@@ -5,14 +5,23 @@
#include "net/http/http_auth_handler_negotiate.h"
#include "base/logging.h"
+#include "net/base/address_family.h"
+#include "net/base/host_resolver.h"
#include "net/base/net_errors.h"
#include "net/http/http_auth_filter.h"
namespace net {
HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate(SSPILibrary* library,
- ULONG max_token_length)
- : auth_sspi_(library, "Negotiate", NEGOSSP_NAME, max_token_length) {
+ ULONG max_token_length,
+ bool disable_cname_lookup,
+ bool use_port)
+ : auth_sspi_(library, "Negotiate", NEGOSSP_NAME, max_token_length),
+ user_callback_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(resolve_cname_callback_(
+ this, &HttpAuthHandlerNegotiate::OnResolveCanonicalName)),
+ disable_cname_lookup_(disable_cname_lookup),
+ use_port_(use_port) {
}
HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() {
@@ -27,7 +36,7 @@ int HttpAuthHandlerNegotiate::GenerateAuthToken(
return auth_sspi_.GenerateAuthToken(
&username,
&password,
- origin_,
+ spn_,
request,
proxy,
auth_token);
@@ -55,6 +64,100 @@ bool HttpAuthHandlerNegotiate::SupportsDefaultCredentials() {
return true;
}
+bool HttpAuthHandlerNegotiate::NeedsCanonicalName() {
+ if (!spn_.empty())
+ return false;
+ if (disable_cname_lookup_) {
+ spn_ = CreateSPN(address_list_, origin_);
+ address_list_.Reset();
+ return false;
+ }
+ return true;
+}
+
+int HttpAuthHandlerNegotiate::ResolveCanonicalName(HostResolver* resolver,
+ CompletionCallback* callback,
+ const BoundNetLog& net_log) {
+ // TODO(cbentzel): Add reverse DNS lookup for numeric addresses.
+ DCHECK(!single_resolve_.get());
+ DCHECK(!disable_cname_lookup_);
+ DCHECK(callback);
+
+ HostResolver::RequestInfo info(origin_.host(), 0);
+ info.set_host_resolver_flags(HOST_RESOLVER_CANONNAME);
+ single_resolve_.reset(new SingleRequestHostResolver(resolver));
+ int rv = single_resolve_->Resolve(info, &address_list_,
+ &resolve_cname_callback_,
+ net_log);
+ if (rv == ERR_IO_PENDING) {
+ user_callback_ = callback;
+ return rv;
+ }
+ OnResolveCanonicalName(rv);
+ // Always return OK. OnResolveCanonicalName logs the error code if not
+ // OK and attempts to use the original origin_ hostname rather than failing
+ // the auth attempt completely.
+ return OK;
+}
+
+void HttpAuthHandlerNegotiate::OnResolveCanonicalName(int result) {
+ if (result != OK) {
+ // Even in the error case, try to use origin_.host instead of
+ // passing the failure on to the caller.
+ LOG(INFO) << "Problem finding canonical name for SPN for host "
+ << origin_.host() << ": " << ErrorToString(result);
+ result = OK;
+ }
+ spn_ = CreateSPN(address_list_, origin_);
+ address_list_.Reset();
+ if (user_callback_) {
+ CompletionCallback* callback = user_callback_;
+ user_callback_ = NULL;
+ callback->Run(result);
+ }
+}
+
+std::wstring HttpAuthHandlerNegotiate::CreateSPN(
+ const AddressList& address_list, const GURL& origin) {
+ // Kerberos SPNs are in the form HTTP/<host>:<port>
+ // http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx
+ //
+ // However, reality differs from the specification. A good description of
+ // the problems can be found here:
+ // http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-kb911149-and-kb908209-are-not-the-soluton.aspx
+ //
+ // Typically the <host> portion should be the canonical FQDN for the service.
+ // If this could not be resolved, the original hostname in the URL will be
+ // attempted instead. However, some intranets register SPNs using aliases
+ // for the same canonical DNS name to allow multiple web services to reside
+ // on the same host machine without requiring different ports. IE6 and IE7
+ // have hotpatches that allow the default behavior to be overridden.
+ // http://support.microsoft.com/kb/911149
+ // http://support.microsoft.com/kb/938305
+ //
+ // According to the spec, the <port> option should be included if it is a
+ // non-standard port (i.e. not 80 or 443 in the HTTP case). However,
+ // historically browsers have not included the port, even on non-standard
+ // ports. IE6 required a hotpatch and a registry setting to enable
+ // including non-standard ports, and IE7 and IE8 also require the same
+ // registry setting, but no hotpatch. Firefox does not appear to have an
+ // option to include non-standard ports as of 3.6.
+ // http://support.microsoft.com/kb/908209
+ //
+ // Without any command-line flags, Chrome matches the behavior of Firefox
+ // and IE. Users can override the behavior so aliases are allowed and
+ // non-standard ports are included.
+ int port = origin.EffectiveIntPort();
+ std::string server;
+ if (!address_list.GetCanonicalName(&server))
+ server = origin.host();
+ if (port != 80 && port != 443 && use_port_) {
+ return ASCIIToWide(StringPrintf("HTTP/%s:%d", server.c_str(), port));
+ } else {
+ return ASCIIToWide(StringPrintf("HTTP/%s", server.c_str()));
+ }
+}
+
int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken(
const HttpRequestInfo* request,
const ProxyInfo* proxy,
@@ -62,14 +165,16 @@ int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken(
return auth_sspi_.GenerateAuthToken(
NULL, // username
NULL, // password
- origin_,
+ spn_,
request,
proxy,
auth_token);
}
HttpAuthHandlerNegotiate::Factory::Factory()
- : max_token_length_(0),
+ : disable_cname_lookup_(false),
+ use_port_(false),
+ max_token_length_(0),
first_creation_(true),
is_unsupported_(false),
sspi_library_(SSPILibrary::GetDefault()) {
@@ -96,7 +201,8 @@ int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler(
// TODO(cbentzel): Move towards model of parsing in the factory
// method and only constructing when valid.
scoped_refptr<HttpAuthHandler> tmp_handler(
- new HttpAuthHandlerNegotiate(sspi_library_, max_token_length_));
+ new HttpAuthHandlerNegotiate(sspi_library_, max_token_length_,
+ disable_cname_lookup_, use_port_));
if (!tmp_handler->InitFromChallenge(challenge, target, origin))
return ERR_INVALID_RESPONSE;
handler->swap(tmp_handler);
diff --git a/net/http/http_auth_handler_ntlm.cc b/net/http/http_auth_handler_ntlm.cc
index dab239d..ed3eb3a 100644
--- a/net/http/http_auth_handler_ntlm.cc
+++ b/net/http/http_auth_handler_ntlm.cc
@@ -9,6 +9,7 @@
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
namespace net {
@@ -22,7 +23,7 @@ int HttpAuthHandlerNTLM::GenerateAuthToken(
return auth_sspi_.GenerateAuthToken(
&username,
&password,
- origin_,
+ CreateSPN(origin_),
request,
proxy,
auth_token);
@@ -109,4 +110,13 @@ bool HttpAuthHandlerNTLM::ParseChallenge(
#endif // defined(NTLM_SSPI)
}
+// static
+std::wstring HttpAuthHandlerNTLM::CreateSPN(const GURL& origin) {
+ // The service principal name of the destination server. See
+ // http://msdn.microsoft.com/en-us/library/ms677949%28VS.85%29.aspx
+ std::wstring target(L"HTTP/");
+ target.append(ASCIIToWide(GetHostAndPort(origin)));
+ return target;
+}
+
} // namespace net
diff --git a/net/http/http_auth_handler_ntlm.h b/net/http/http_auth_handler_ntlm.h
index 542bf92..f027471 100644
--- a/net/http/http_auth_handler_ntlm.h
+++ b/net/http/http_auth_handler_ntlm.h
@@ -145,6 +145,9 @@ class HttpAuthHandlerNTLM : public HttpAuthHandler {
void** out_token,
uint32* out_token_len);
+ // Create an NTLM SPN to identify the |origin| server.
+ static std::wstring CreateSPN(const GURL& origin);
+
#if defined(NTLM_SSPI)
HttpAuthSSPI auth_sspi_;
#endif
diff --git a/net/http/http_auth_handler_ntlm_win.cc b/net/http/http_auth_handler_ntlm_win.cc
index baa7f86..04c53f3 100644
--- a/net/http/http_auth_handler_ntlm_win.cc
+++ b/net/http/http_auth_handler_ntlm_win.cc
@@ -47,7 +47,7 @@ int HttpAuthHandlerNTLM::GenerateDefaultAuthToken(
return auth_sspi_.GenerateAuthToken(
NULL, // username
NULL, // password
- origin_,
+ CreateSPN(origin_),
request,
proxy,
auth_token);
diff --git a/net/http/http_auth_sspi_win.cc b/net/http/http_auth_sspi_win.cc
index 5349e76..1f3165d 100644
--- a/net/http/http_auth_sspi_win.cc
+++ b/net/http/http_auth_sspi_win.cc
@@ -12,7 +12,6 @@
#include "base/singleton.h"
#include "base/string_util.h"
#include "net/base/net_errors.h"
-#include "net/base/net_util.h"
#include "net/http/http_auth.h"
namespace net {
@@ -162,7 +161,7 @@ bool HttpAuthSSPI::ParseChallenge(HttpAuth::ChallengeTokenizer* tok) {
int HttpAuthSSPI::GenerateAuthToken(const std::wstring* username,
const std::wstring* password,
- const GURL& origin,
+ const std::wstring& spn,
const HttpRequestInfo* request,
const ProxyInfo* proxy,
std::string* auth_token) {
@@ -178,7 +177,7 @@ int HttpAuthSSPI::GenerateAuthToken(const std::wstring* username,
void* out_buf;
int out_buf_len;
int rv = GetNextSecurityToken(
- origin,
+ spn,
static_cast<void *>(const_cast<char *>(
decoded_server_auth_token_.c_str())),
decoded_server_auth_token_.length(),
@@ -223,7 +222,7 @@ int HttpAuthSSPI::OnFirstRound(const std::wstring* username,
}
int HttpAuthSSPI::GetNextSecurityToken(
- const GURL& origin,
+ const std::wstring& spn,
const void * in_token,
int in_token_len,
void** out_token,
@@ -269,17 +268,11 @@ int HttpAuthSSPI::GetNextSecurityToken(
if (!out_buffer.pvBuffer)
return ERR_OUT_OF_MEMORY;
- // The service principal name of the destination server. See
- // http://msdn.microsoft.com/en-us/library/ms677949%28VS.85%29.aspx
- std::wstring target(L"HTTP/");
- target.append(ASCIIToWide(GetHostAndPort(origin)));
- wchar_t* target_name = const_cast<wchar_t*>(target.c_str());
-
// This returns a token that is passed to the remote server.
status = library_->InitializeSecurityContext(
&cred_, // phCredential
ctxt_ptr, // phContext
- target_name, // pszTargetName
+ const_cast<wchar_t *>(spn.c_str()), // pszTargetName
0, // fContextReq
0, // Reserved1 (must be 0)
SECURITY_NATIVE_DREP, // TargetDataRep
diff --git a/net/http/http_auth_sspi_win.h b/net/http/http_auth_sspi_win.h
index c925920..a6fb49e 100644
--- a/net/http/http_auth_sspi_win.h
+++ b/net/http/http_auth_sspi_win.h
@@ -18,8 +18,6 @@
#include "net/http/http_auth.h"
-class GURL;
-
namespace net {
class HttpRequestInfo;
@@ -89,12 +87,14 @@ class HttpAuthSSPI {
// Generates an authentication token.
// The return value is an error code. If it's not |OK|, the value of
// |*auth_token| is unspecified.
+ // |spn| is the Service Principal Name of the server that the token is
+ // being generated for.
// If this is the first round of a multiple round scheme, credentials are
// obtained using |*username| and |*password|. If |username| and |password|
// are NULL, the default credentials are used instead.
int GenerateAuthToken(const std::wstring* username,
const std::wstring* password,
- const GURL& origin,
+ const std::wstring& spn,
const HttpRequestInfo* request,
const ProxyInfo* proxy,
std::string* auth_token);
@@ -104,7 +104,7 @@ class HttpAuthSSPI {
const std::wstring* password);
int GetNextSecurityToken(
- const GURL& origin,
+ const std::wstring& spn,
const void* in_token,
int in_token_len,
void** out_token,
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 467f63c..9078f35 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -574,6 +574,12 @@ int HttpNetworkTransaction::DoLoop(int result) {
TRACE_EVENT_END("http.read_headers", request_, request_->url.spec());
net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_READ_HEADERS);
break;
+ case STATE_RESOLVE_CANONICAL_NAME:
+ rv = DoResolveCanonicalName();
+ break;
+ case STATE_RESOLVE_CANONICAL_NAME_COMPLETE:
+ rv = DoResolveCanonicalNameComplete(rv);
+ break;
case STATE_READ_BODY:
DCHECK_EQ(OK, rv);
TRACE_EVENT_BEGIN("http.read_body", request_, request_->url.spec());
@@ -1116,6 +1122,23 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
return OK;
}
+int HttpNetworkTransaction::DoResolveCanonicalName() {
+ HttpAuthHandler* auth_handler = auth_handler_[pending_auth_target_];
+ DCHECK(auth_handler);
+ next_state_ = STATE_RESOLVE_CANONICAL_NAME_COMPLETE;
+ return auth_handler->ResolveCanonicalName(session_->host_resolver(),
+ &io_callback_, net_log_);
+}
+
+int HttpNetworkTransaction::DoResolveCanonicalNameComplete(int result) {
+ // The STATE_RESOLVE_CANONICAL_NAME state ends the Start sequence when the
+ // canonical name of the server needs to be determined. Normally
+ // DoReadHeadersComplete completes the sequence. The next state is
+ // intentionally not set as it should be STATE_NONE;
+ DCHECK_EQ(STATE_NONE, next_state_);
+ return result;
+}
+
int HttpNetworkTransaction::DoReadBody() {
DCHECK(read_buf_);
DCHECK_GT(read_buf_len_, 0);
@@ -1917,6 +1940,13 @@ int HttpNetworkTransaction::HandleAuthChallenge() {
// pass the challenge information back to the client.
PopulateAuthChallenge(target, auth_origin);
}
+
+ // SPN determination (for Negotiate) requires a DNS lookup to find the
+ // canonical name. This needs to be done asynchronously to prevent blocking
+ // the IO thread.
+ if (auth_handler_[target]->NeedsCanonicalName())
+ next_state_ = STATE_RESOLVE_CANONICAL_NAME;
+
return OK;
}
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index 113c0b5..220e789 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -84,6 +84,8 @@ class HttpNetworkTransaction : public HttpTransaction {
STATE_SEND_REQUEST_COMPLETE,
STATE_READ_HEADERS,
STATE_READ_HEADERS_COMPLETE,
+ STATE_RESOLVE_CANONICAL_NAME,
+ STATE_RESOLVE_CANONICAL_NAME_COMPLETE,
STATE_READ_BODY,
STATE_READ_BODY_COMPLETE,
STATE_DRAIN_BODY_FOR_AUTH_RESTART,
@@ -130,6 +132,8 @@ class HttpNetworkTransaction : public HttpTransaction {
int DoSendRequestComplete(int result);
int DoReadHeaders();
int DoReadHeadersComplete(int result);
+ int DoResolveCanonicalName();
+ int DoResolveCanonicalNameComplete(int result);
int DoReadBody();
int DoReadBodyComplete(int result);
int DoDrainBodyForAuthRestart();
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index aa18df5..b9458d5 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -65,7 +65,6 @@ ProxyService* CreateFixedProxyService(const std::string& proxy) {
return ProxyService::CreateFixed(proxy_config);
}
-
HttpNetworkSession* CreateSession(SessionDependencies* session_deps) {
return new HttpNetworkSession(NULL,
session_deps->host_resolver,
@@ -4679,4 +4678,201 @@ TEST_F(HttpNetworkTransactionTest, FailNpnSpdyAndFallback) {
EXPECT_EQ("hello world", response_data);
}
+// MockAuthHandlerCanonical is used by the ResolveCanonicalName
+// HttpNetworkTransaction unit test below. Callers set up expectations for
+// whether the canonical name needs to be resolved.
+class MockAuthHandlerCanonical : public HttpAuthHandler {
+ public:
+ enum Resolve {
+ RESOLVE_INIT,
+ RESOLVE_SKIP,
+ RESOLVE_SYNC,
+ RESOLVE_ASYNC,
+ RESOLVE_TESTED,
+ };
+
+ MockAuthHandlerCanonical() : resolve_(RESOLVE_INIT), user_callback_(NULL) {}
+ virtual ~MockAuthHandlerCanonical() {}
+
+ void SetResolveExpectation(Resolve resolve) {
+ EXPECT_EQ(RESOLVE_INIT, resolve_);
+ resolve_ = resolve;
+ }
+
+ void ResetResolveExpectation() {
+ EXPECT_EQ(RESOLVE_TESTED, resolve_);
+ resolve_ = RESOLVE_INIT;
+ }
+
+ virtual bool NeedsCanonicalName() {
+ switch (resolve_) {
+ case RESOLVE_SYNC:
+ case RESOLVE_ASYNC:
+ return true;
+ case RESOLVE_SKIP:
+ resolve_ = RESOLVE_TESTED;
+ return false;
+ default:
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ virtual int ResolveCanonicalName(HostResolver* host_resolver,
+ CompletionCallback* callback,
+ const BoundNetLog& net_log) {
+ EXPECT_NE(RESOLVE_TESTED, resolve_);
+ int rv = OK;
+ switch (resolve_) {
+ case RESOLVE_SYNC:
+ resolve_ = RESOLVE_TESTED;
+ break;
+ case RESOLVE_ASYNC:
+ EXPECT_TRUE(user_callback_ == NULL);
+ rv = ERR_IO_PENDING;
+ user_callback_ = callback;
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ this, &MockAuthHandlerCanonical::OnResolveCanonicalName));
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ return rv;
+ }
+
+ void OnResolveCanonicalName() {
+ EXPECT_EQ(RESOLVE_ASYNC, resolve_);
+ EXPECT_TRUE(user_callback_ != NULL);
+ resolve_ = RESOLVE_TESTED;
+ CompletionCallback* callback = user_callback_;
+ user_callback_ = NULL;
+ callback->Run(OK);
+ }
+
+ virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) {
+ scheme_ = "mock";
+ score_ = 1;
+ properties_ = 0;
+ return true;
+ }
+
+ virtual int GenerateAuthToken(const std::wstring& username,
+ const std::wstring& password,
+ const HttpRequestInfo* request,
+ const ProxyInfo* proxy,
+ std::string* auth_token) {
+ auth_token->assign("Mock AUTH myserver.example.com");
+ return OK;
+ }
+
+ virtual int GenerateDefaultAuthToken(const HttpRequestInfo* request,
+ const ProxyInfo* proxy,
+ std::string* auth_token) {
+ auth_token->assign("Mock DEFAULT_AUTH myserver.example.com");
+ return OK;
+ }
+
+ // The Factory class simply returns the same handler each time
+ // CreateAuthHandler is called.
+ class Factory : public HttpAuthHandlerFactory {
+ public:
+ Factory() {}
+ virtual ~Factory() {}
+
+ void set_mock_handler(MockAuthHandlerCanonical* mock_handler) {
+ mock_handler_ = mock_handler;
+ }
+ MockAuthHandlerCanonical* mock_handler() const {
+ return mock_handler_.get();
+ }
+
+ virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuth::Target target,
+ const GURL& origin,
+ scoped_refptr<HttpAuthHandler>* handler) {
+ *handler = mock_handler_;
+ return OK;
+ }
+
+ private:
+ scoped_refptr<MockAuthHandlerCanonical> mock_handler_;
+ };
+
+ private:
+ Resolve resolve_;
+ CompletionCallback* user_callback_;
+};
+
+// Tests that ResolveCanonicalName is handled correctly by the
+// HttpNetworkTransaction.
+TEST_F(HttpNetworkTransactionTest, ResolveCanonicalName) {
+ SessionDependencies session_deps;
+ scoped_refptr<MockAuthHandlerCanonical> auth_handler(
+ new MockAuthHandlerCanonical());
+ auth_handler->Init(NULL);
+ MockAuthHandlerCanonical::Factory* auth_factory(
+ new MockAuthHandlerCanonical::Factory());
+ auth_factory->set_mock_handler(auth_handler);
+ session_deps.http_auth_handler_factory.reset(auth_factory);
+
+ for (int i = 0; i < 2; ++i) {
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+ // Set up expectations for this pass of the test. Many of the EXPECT calls
+ // are contained inside the MockAuthHandlerCanonical codebase in response to
+ // the expectations.
+ MockAuthHandlerCanonical::Resolve resolve = (i == 0) ?
+ MockAuthHandlerCanonical::RESOLVE_SYNC :
+ MockAuthHandlerCanonical::RESOLVE_ASYNC;
+ auth_handler->SetResolveExpectation(resolve);
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://myserver/");
+ request.load_flags = 0;
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: myserver\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.1 401 Unauthorized\r\n"),
+ MockRead("WWW-Authenticate: Mock myserver.example.com\r\n"),
+ MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
+ MockRead("Content-Length: 14\r\n\r\n"),
+ MockRead("Unauthorized\r\n"),
+ };
+
+ StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+ data_writes1, arraysize(data_writes1));
+ session_deps.socket_factory.AddSocketDataProvider(&data1);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1, NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_FALSE(response == NULL);
+
+ // The password prompt is set after the canonical name is resolved.
+ // If it isn't present or is incorrect, it indicates that the scheme
+ // did not complete correctly.
+ EXPECT_FALSE(response->auth_challenge.get() == NULL);
+
+ EXPECT_EQ(L"myserver:80", response->auth_challenge->host_and_port);
+ EXPECT_EQ(L"", response->auth_challenge->realm);
+ EXPECT_EQ(L"mock", response->auth_challenge->scheme);
+ auth_handler->ResetResolveExpectation();
+ }
+}
+
} // namespace net