Index: net/dns/dns_transaction.cc |
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc |
index 6d711b4a5a97cbcd3fe4d3b88cf1192a962a3177..8222863690aa38006bd6f7be416ea80d699f1b20 100644 |
--- a/net/dns/dns_transaction.cc |
+++ b/net/dns/dns_transaction.cc |
@@ -1,14 +1,29 @@ |
-// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Copyright (c) 2012 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/dns/dns_transaction.h" |
+#include <deque> |
+#include <string> |
+#include <vector> |
+ |
#include "base/bind.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/memory/weak_ptr.h" |
+#include "base/message_loop.h" |
#include "base/rand_util.h" |
-#include "base/values.h" |
+#include "base/stl_util.h" |
+#include "base/string_piece.h" |
+#include "base/threading/non_thread_safe.h" |
+#include "base/timer.h" |
+#include "net/base/completion_callback.h" |
+#include "net/base/dns_util.h" |
#include "net/base/io_buffer.h" |
+#include "net/base/ip_endpoint.h" |
#include "net/base/net_errors.h" |
+#include "net/base/net_log.h" |
#include "net/dns/dns_protocol.h" |
#include "net/dns/dns_query.h" |
#include "net/dns/dns_response.h" |
@@ -20,259 +35,497 @@ namespace net { |
namespace { |
-class DnsTransactionStartParameters : public NetLog::EventParameters { |
+// Count labels in the fully-qualified name in DNS format. |
+int CountLabels(const std::string& name) { |
+ size_t count = 0; |
+ for (size_t i = 0; i < name.size() && name[i]; i += name[i] + 1) |
+ ++count; |
+ return count; |
+} |
+ |
+bool IsIPLiteral(const std::string& hostname) { |
+ IPAddressNumber ip; |
+ return ParseIPLiteralToNumber(hostname, &ip); |
+} |
+ |
+class StartParameters : public NetLog::EventParameters { |
public: |
- DnsTransactionStartParameters(const IPEndPoint& dns_server, |
- const base::StringPiece& qname, |
- uint16 qtype, |
- const NetLog::Source& source) |
- : dns_server_(dns_server), |
- qname_(qname.data(), qname.length()), |
- qtype_(qtype), |
- source_(source) {} |
- |
- virtual Value* ToValue() const { |
+ StartParameters(const std::string& hostname, |
+ uint16 qtype, |
+ const NetLog::Source& source) |
+ : hostname_(hostname), qtype_(qtype), source_(source) {} |
+ |
+ virtual Value* ToValue() const OVERRIDE { |
DictionaryValue* dict = new DictionaryValue(); |
- dict->SetString("dns_server", dns_server_.ToString()); |
- dict->SetString("hostname", qname_); |
+ dict->SetString("hostname", hostname_); |
dict->SetInteger("query_type", qtype_); |
- if (source_.is_valid()) |
- dict->Set("source_dependency", source_.ToValue()); |
+ dict->Set("source_dependency", source_.ToValue()); |
return dict; |
} |
private: |
- IPEndPoint dns_server_; |
- std::string qname_; |
- uint16 qtype_; |
+ const std::string hostname_; |
+ const uint16 qtype_; |
const NetLog::Source source_; |
}; |
-class DnsTransactionFinishParameters : public NetLog::EventParameters { |
+class ResponseParameters : public NetLog::EventParameters { |
public: |
- // TODO(szym): add rcode ? |
- DnsTransactionFinishParameters(int net_error, int answer_count) |
- : net_error_(net_error), answer_count_(answer_count) {} |
+ ResponseParameters(int rcode, int answer_count, const NetLog::Source& source) |
+ : rcode_(rcode), answer_count_(answer_count), source_(source) {} |
- virtual Value* ToValue() const { |
+ virtual Value* ToValue() const OVERRIDE { |
DictionaryValue* dict = new DictionaryValue(); |
- if (net_error_) |
- dict->SetInteger("net_error", net_error_); |
+ dict->SetInteger("rcode", rcode_); |
dict->SetInteger("answer_count", answer_count_); |
+ dict->Set("socket_source", source_.ToValue()); |
return dict; |
} |
private: |
- const int net_error_; |
+ const int rcode_; |
const int answer_count_; |
+ const NetLog::Source source_; |
}; |
-class DnsTransactionRetryParameters : public NetLog::EventParameters { |
+// ---------------------------------------------------------------------------- |
+ |
+// A single asynchronous DNS exchange over UDP, which consists of sending out a |
+// DNS query, waiting for a response, and returning the response that it |
+// matches. Logging is done in the socket and in the outer DnsTransaction. |
+class DnsUDPAttempt { |
public: |
- DnsTransactionRetryParameters(int attempt_number, |
- const NetLog::Source& source) |
- : attempt_number_(attempt_number), source_(source) {} |
+ DnsUDPAttempt(scoped_ptr<DatagramClientSocket> socket, |
+ const IPEndPoint& server, |
+ scoped_ptr<DnsQuery> query, |
+ const CompletionCallback& callback) |
+ : next_state_(STATE_NONE), |
+ socket_(socket.Pass()), |
+ server_(server), |
+ query_(query.Pass()), |
+ callback_(callback) { |
+ } |
- virtual Value* ToValue() const { |
- DictionaryValue* dict = new DictionaryValue(); |
- dict->SetInteger("attempt_number", attempt_number_); |
- dict->Set("source_dependency", source_.ToValue()); |
- return dict; |
+ // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously |
+ // and calls |callback| upon completion. |
+ int Start() { |
+ DCHECK_EQ(STATE_NONE, next_state_); |
+ next_state_ = STATE_CONNECT; |
+ return DoLoop(OK); |
+ } |
+ |
+ const DnsQuery* query() const { |
+ return query_.get(); |
+ } |
+ |
+ const DatagramClientSocket* socket() const { |
+ return socket_.get(); |
+ } |
+ |
+ // Returns the response or NULL if has not received a matching response from |
+ // the server. |
+ const DnsResponse* response() const { |
+ const DnsResponse* resp = response_.get(); |
+ return (resp != NULL && resp->IsValid()) ? resp : NULL; |
} |
private: |
- const int attempt_number_; |
- const NetLog::Source source_; |
-}; |
+ enum State { |
+ STATE_CONNECT, |
+ STATE_SEND_QUERY, |
+ STATE_SEND_QUERY_COMPLETE, |
+ STATE_READ_RESPONSE, |
+ STATE_READ_RESPONSE_COMPLETE, |
+ STATE_NONE, |
+ }; |
+ |
+ int DoLoop(int result) { |
+ DCHECK_NE(STATE_NONE, next_state_); |
+ int rv = result; |
+ do { |
+ State state = next_state_; |
+ next_state_ = STATE_NONE; |
+ switch (state) { |
+ case STATE_CONNECT: |
+ rv = DoConnect(); |
+ break; |
+ case STATE_SEND_QUERY: |
+ rv = DoSendQuery(); |
+ break; |
+ case STATE_SEND_QUERY_COMPLETE: |
+ rv = DoSendQueryComplete(rv); |
+ break; |
+ case STATE_READ_RESPONSE: |
+ rv = DoReadResponse(); |
+ break; |
+ case STATE_READ_RESPONSE_COMPLETE: |
+ rv = DoReadResponseComplete(rv); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+ } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); |
-} // namespace |
+ return rv; |
+ } |
+ |
+ int DoConnect() { |
+ next_state_ = STATE_SEND_QUERY; |
+ return socket_->Connect(server_); |
+ } |
+ |
+ int DoSendQuery() { |
+ next_state_ = STATE_SEND_QUERY_COMPLETE; |
+ return socket_->Write(query_->io_buffer(), |
+ query_->io_buffer()->size(), |
+ base::Bind(&DnsUDPAttempt::OnIOComplete, |
+ base::Unretained(this))); |
+ } |
+ |
+ int DoSendQueryComplete(int rv) { |
+ DCHECK_NE(ERR_IO_PENDING, rv); |
+ if (rv < 0) |
+ return rv; |
+ |
+ // Writing to UDP should not result in a partial datagram. |
+ if (rv != query_->io_buffer()->size()) |
+ return ERR_MSG_TOO_BIG; |
+ next_state_ = STATE_READ_RESPONSE; |
+ return OK; |
+ } |
+ |
+ int DoReadResponse() { |
+ next_state_ = STATE_READ_RESPONSE_COMPLETE; |
+ response_.reset(new DnsResponse()); |
+ return socket_->Read(response_->io_buffer(), |
+ response_->io_buffer()->size(), |
+ base::Bind(&DnsUDPAttempt::OnIOComplete, |
+ base::Unretained(this))); |
+ } |
+ |
+ int DoReadResponseComplete(int rv) { |
+ DCHECK_NE(ERR_IO_PENDING, rv); |
+ if (rv < 0) |
+ return rv; |
+ |
+ DCHECK(rv); |
+ if (!response_->InitParse(rv, *query_)) |
+ return ERR_DNS_MALFORMED_RESPONSE; |
+ if (response_->flags() & dns_protocol::kFlagTC) |
+ return ERR_DNS_SERVER_REQUIRES_TCP; |
+ if (response_->rcode() != dns_protocol::kRcodeNOERROR && |
+ response_->rcode() != dns_protocol::kRcodeNXDOMAIN) { |
+ return ERR_DNS_SERVER_FAILED; |
+ } |
+ if (response_->answer_count() == 0) |
+ return ERR_NAME_NOT_RESOLVED; |
+ |
+ return OK; |
+ } |
+ |
+ void OnIOComplete(int rv) { |
+ rv = DoLoop(rv); |
+ if (rv != ERR_IO_PENDING) |
+ callback_.Run(rv); |
+ } |
+ |
+ State next_state_; |
+ |
+ scoped_ptr<DatagramClientSocket> socket_; |
+ IPEndPoint server_; |
+ scoped_ptr<DnsQuery> query_; |
+ |
+ scoped_ptr<DnsResponse> response_; |
+ |
+ CompletionCallback callback_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt); |
+}; |
+ |
+// ---------------------------------------------------------------------------- |
-DnsTransaction::DnsTransaction(DnsSession* session, |
- const base::StringPiece& qname, |
- uint16 qtype, |
- const ResultCallback& callback, |
- const BoundNetLog& source_net_log) |
+// Implements DnsTransaction. Configuration is supplied by DnsSession. |
+// The suffix list is built according to the DnsConfig from the session. |
+// The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout. |
+// The first server to attempt on each query is given by |
+// DnsSession::NextFirstServerIndex, and the order is round-robin afterwards. |
+// Each server is attempted DnsConfig::attempts times. |
+class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe { |
+ public: |
+ DnsTransactionImpl(DnsSession* session, |
+ const std::string& hostname, |
+ uint16 qtype, |
+ const DnsTransactionFactory::CallbackType& callback, |
+ const BoundNetLog& source_net_log) |
: session_(session), |
- dns_server_(session->NextServer()), |
- query_(new DnsQuery(session->NextId(), qname, qtype)), |
+ hostname_(hostname), |
+ qtype_(qtype), |
callback_(callback), |
- attempts_(0), |
- next_state_(STATE_NONE), |
net_log_(BoundNetLog::Make(session->net_log(), |
- NetLog::SOURCE_DNS_TRANSACTION)) { |
- net_log_.BeginEvent( |
- NetLog::TYPE_DNS_TRANSACTION, |
- make_scoped_refptr( |
- new DnsTransactionStartParameters(dns_server_, |
- qname, |
- qtype, |
- source_net_log.source()))); |
-} |
+ NetLog::SOURCE_DNS_TRANSACTION)), |
+ first_server_index_(0) { |
+ DCHECK(session_); |
+ DCHECK(!hostname_.empty()); |
+ DCHECK(!callback_.is_null()); |
-DnsTransaction::~DnsTransaction() {} |
+ DCHECK(!IsIPLiteral(hostname_)); |
-int DnsTransaction::Start() { |
- DCHECK_EQ(STATE_NONE, next_state_); |
- next_state_ = STATE_CONNECT; |
- return DoLoop(OK); |
-} |
+ net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION, make_scoped_refptr( |
+ new StartParameters(hostname_, qtype_, source_net_log.source()))); |
+ } |
-int DnsTransaction::DoLoop(int result) { |
- DCHECK_NE(STATE_NONE, next_state_); |
- int rv = result; |
- do { |
- State state = next_state_; |
- next_state_ = STATE_NONE; |
- switch (state) { |
- case STATE_CONNECT: |
- rv = DoConnect(); |
- break; |
- case STATE_CONNECT_COMPLETE: |
- rv = DoConnectComplete(rv); |
- break; |
- case STATE_SEND_QUERY: |
- rv = DoSendQuery(); |
- break; |
- case STATE_SEND_QUERY_COMPLETE: |
- rv = DoSendQueryComplete(rv); |
- break; |
- case STATE_READ_RESPONSE: |
- rv = DoReadResponse(); |
- break; |
- case STATE_READ_RESPONSE_COMPLETE: |
- rv = DoReadResponseComplete(rv); |
- break; |
- default: |
- NOTREACHED(); |
- break; |
+ virtual ~DnsTransactionImpl() { |
+ STLDeleteElements(&attempts_); |
+ if (!callback_.is_null()) { |
+ net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL); |
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, |
+ ERR_ABORTED); |
} |
- } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); |
+ } |
- return rv; |
-} |
+ virtual const std::string& GetHostname() const OVERRIDE { |
+ DCHECK(CalledOnValidThread()); |
+ return hostname_; |
+ } |
-void DnsTransaction::DoCallback(int result) { |
- DCHECK_NE(result, ERR_IO_PENDING); |
- int answer_count = (result == OK) ? response()->answer_count() : 0; |
- net_log_.EndEvent( |
- NetLog::TYPE_DNS_TRANSACTION, |
- make_scoped_refptr( |
- new DnsTransactionFinishParameters(result, answer_count))); |
- callback_.Run(this, result); |
-} |
+ virtual uint16 GetType() const OVERRIDE { |
+ DCHECK(CalledOnValidThread()); |
+ return qtype_; |
+ } |
-void DnsTransaction::OnIOComplete(int result) { |
- int rv = DoLoop(result); |
- if (rv != ERR_IO_PENDING) |
- DoCallback(rv); |
-} |
+ virtual int Start() OVERRIDE { |
+ int rv = PrepareSearch(); |
+ if (rv == OK) |
+ rv = StartQuery(); |
+ DCHECK_NE(OK, rv); |
+ return rv; |
+ } |
-int DnsTransaction::DoConnect() { |
- next_state_ = STATE_CONNECT_COMPLETE; |
- |
- StartTimer(session_->NextTimeout(attempts_)); |
- ++attempts_; |
- |
- // TODO(szym): keep all sockets around in case the server responds |
- // after its timeout; state machine will need to change to handle that. |
- // The current plan is to move socket management out to DnsSession. |
- // Hence also move retransmissions to DnsClient::Request. |
- socket_.reset(session_->socket_factory()->CreateDatagramClientSocket( |
- DatagramSocket::RANDOM_BIND, |
- base::Bind(&base::RandInt), |
- net_log_.net_log(), |
- net_log_.source())); |
- |
- net_log_.AddEvent( |
- NetLog::TYPE_DNS_TRANSACTION_ATTEMPT_STARTED, |
- make_scoped_refptr( |
- new DnsTransactionRetryParameters(attempts_, |
- socket_->NetLog().source()))); |
- |
- return socket_->Connect(dns_server_); |
-} |
+ private: |
+ // Prepares |qnames_| according to the DnsConfig. |
+ int PrepareSearch() { |
+ const DnsConfig& config = session_->config(); |
+ |
+ std::string labelled_hostname; |
+ if (!DNSDomainFromDot(hostname_, &labelled_hostname)) |
+ return ERR_INVALID_ARGUMENT; |
+ |
+ if (hostname_[hostname_.size() - 1] == '.') { |
+ // It's a fully-qualified name, no suffix search. |
+ qnames_.push_back(labelled_hostname); |
+ return OK; |
+ } |
-int DnsTransaction::DoConnectComplete(int rv) { |
- if (rv < 0) |
- return rv; |
- next_state_ = STATE_SEND_QUERY; |
- return OK; |
-} |
+ // Set true when |labelled_hostname| is put on the list. |
+ bool had_hostname = false; |
-int DnsTransaction::DoSendQuery() { |
- next_state_ = STATE_SEND_QUERY_COMPLETE; |
- return socket_->Write(query_->io_buffer(), |
- query_->io_buffer()->size(), |
- base::Bind(&DnsTransaction::OnIOComplete, |
- base::Unretained(this))); |
-} |
+ int ndots = CountLabels(labelled_hostname) - 1; |
+ if (ndots >= config.ndots) { |
+ qnames_.push_back(labelled_hostname); |
+ had_hostname = true; |
+ } |
-int DnsTransaction::DoSendQueryComplete(int rv) { |
- if (rv < 0) |
- return rv; |
+ std::string qname; |
+ for (size_t i = 0; i < config.search.size(); ++i) { |
+ // Ignore invalid (too long) combinations. |
+ if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname)) |
+ continue; |
+ if (qname.size() == labelled_hostname.size()) { |
+ if (had_hostname) |
+ continue; |
+ had_hostname = true; |
+ } |
+ qnames_.push_back(qname); |
+ } |
- // Writing to UDP should not result in a partial datagram. |
- if (rv != query_->io_buffer()->size()) |
- return ERR_MSG_TOO_BIG; |
+ if (!had_hostname) |
+ qnames_.push_back(labelled_hostname); |
- next_state_ = STATE_READ_RESPONSE; |
- return OK; |
-} |
+ return OK; |
+ } |
-int DnsTransaction::DoReadResponse() { |
- next_state_ = STATE_READ_RESPONSE_COMPLETE; |
- response_.reset(new DnsResponse()); |
- return socket_->Read(response_->io_buffer(), |
- response_->io_buffer()->size(), |
- base::Bind(&DnsTransaction::OnIOComplete, |
- base::Unretained(this))); |
-} |
+ void DoCallback(int rv, const DnsUDPAttempt* successful_attempt) { |
+ if (callback_.is_null()) |
+ return; |
+ DCHECK_NE(ERR_IO_PENDING, rv); |
+ DCHECK(rv != OK || successful_attempt != NULL); |
+ |
+ DnsTransactionFactory::CallbackType callback = callback_; |
+ callback_.Reset(); |
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, rv); |
+ callback.Run(this, |
+ rv, |
+ successful_attempt ? successful_attempt->response() : NULL); |
+ } |
-int DnsTransaction::DoReadResponseComplete(int rv) { |
- DCHECK_NE(ERR_IO_PENDING, rv); |
- RevokeTimer(); |
- if (rv < 0) |
- return rv; |
+ // Makes another attempt at the current name, |qnames_.front()|, using the |
+ // next nameserver. |
+ int MakeAttempt() { |
+ unsigned attempt_number = attempts_.size(); |
+ |
+ scoped_ptr<DatagramClientSocket> socket( |
+ session_->socket_factory()->CreateDatagramClientSocket( |
+ DatagramSocket::RANDOM_BIND, |
+ base::Bind(&base::RandInt), |
+ net_log_.net_log(), |
+ net_log_.source())); |
+ |
+ uint16 id = session_->NextQueryId(); |
+ scoped_ptr<DnsQuery> query; |
+ if (attempts_.empty()) { |
+ query.reset(new DnsQuery(id, qnames_.front(), qtype_)); |
+ } else { |
+ query.reset(attempts_[0]->query()->CloneWithNewId(id)); |
+ } |
+ |
+ net_log_.AddEvent(NetLog::TYPE_DNS_TRANSACTION_ATTEMPT, make_scoped_refptr( |
+ new NetLogSourceParameter("socket_source", socket->NetLog().source()))); |
+ |
+ const DnsConfig& config = session_->config(); |
+ |
+ unsigned server_index = first_server_index_ + |
+ (attempt_number % config.nameservers.size()); |
+ |
+ DnsUDPAttempt* attempt = new DnsUDPAttempt( |
+ socket.Pass(), |
+ config.nameservers[server_index], |
+ query.Pass(), |
+ base::Bind(&DnsTransactionImpl::OnAttemptComplete, |
+ base::Unretained(this), |
+ attempt_number)); |
- DCHECK(rv); |
- if (!response_->InitParse(rv, *query_)) |
- return ERR_DNS_MALFORMED_RESPONSE; |
- // TODO(szym): define this flag value in dns_protocol |
- if (response_->flags1() & 2) |
- return ERR_DNS_SERVER_REQUIRES_TCP; |
- // TODO(szym): move this handling out of DnsTransaction? |
- if (response_->rcode() != dns_protocol::kRcodeNOERROR && |
- response_->rcode() != dns_protocol::kRcodeNXDOMAIN) { |
- return ERR_DNS_SERVER_FAILED; |
+ base::TimeDelta timeout = session_->NextTimeout(attempt_number); |
+ timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout); |
+ attempts_.push_back(attempt); |
+ return attempt->Start(); |
} |
- // TODO(szym): add ERR_DNS_RR_NOT_FOUND? |
- if (response_->answer_count() == 0) |
- return ERR_NAME_NOT_RESOLVED; |
- return OK; |
-} |
+ // Begins query for the current name. Makes the first attempt. |
+ int StartQuery() { |
+ std::string dotted_qname = DNSDomainToString(qnames_.front()); |
+ net_log_.BeginEvent( |
+ NetLog::TYPE_DNS_TRANSACTION_QUERY, |
+ make_scoped_refptr(new NetLogStringParameter("qname", dotted_qname))); |
-void DnsTransaction::StartTimer(base::TimeDelta delay) { |
- timer_.Start(FROM_HERE, delay, this, &DnsTransaction::OnTimeout); |
-} |
+ first_server_index_ = session_->NextFirstServerIndex(); |
-void DnsTransaction::RevokeTimer() { |
- timer_.Stop(); |
-} |
+ STLDeleteElements(&attempts_); |
+ return MakeAttempt(); |
+ } |
+ |
+ void OnAttemptComplete(unsigned attempt_number, int rv) { |
+ DCHECK_LT(attempt_number, attempts_.size()); |
+ timer_.Stop(); |
+ |
+ const DnsUDPAttempt* attempt = attempts_[attempt_number]; |
+ |
+ if (attempt->response()) { |
+ net_log_.AddEvent( |
+ NetLog::TYPE_DNS_TRANSACTION_RESPONSE, |
+ make_scoped_refptr( |
+ new ResponseParameters(attempt->response()->rcode(), |
+ attempt->response()->answer_count(), |
+ attempt->socket()->NetLog().source()))); |
+ } |
+ |
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY, rv); |
+ |
+ switch (rv) { |
+ case ERR_NAME_NOT_RESOLVED: |
+ // Try next suffix. |
+ qnames_.pop_front(); |
+ if (qnames_.empty()) |
+ rv = ERR_NAME_NOT_RESOLVED; |
+ else |
+ rv = StartQuery(); |
+ break; |
+ case OK: |
+ DoCallback(rv, attempt); |
+ return; |
+ default: |
+ // TODO(szym): Some nameservers could fail so try the next one. |
+ const DnsConfig& config = session_->config(); |
+ if (attempts_.size() < config.attempts * config.nameservers.size()) { |
+ rv = MakeAttempt(); |
+ } else { |
+ // TODO(szym): Should this be different than the timeout case? |
+ rv = ERR_DNS_SERVER_FAILED; |
+ } |
+ break; |
+ } |
+ if (rv != ERR_IO_PENDING) |
+ DoCallback(rv, NULL); |
+ } |
-void DnsTransaction::OnTimeout() { |
- DCHECK(next_state_ == STATE_SEND_QUERY_COMPLETE || |
- next_state_ == STATE_READ_RESPONSE_COMPLETE); |
- if (attempts_ == session_->config().attempts) { |
- DoCallback(ERR_DNS_TIMED_OUT); |
- return; |
+ void OnTimeout() { |
+ const DnsConfig& config = session_->config(); |
+ if (attempts_.size() == config.attempts * config.nameservers.size()) { |
+ DoCallback(ERR_DNS_TIMED_OUT, NULL); |
+ return; |
+ } |
+ int rv = MakeAttempt(); |
+ if (rv != ERR_IO_PENDING) |
+ DoCallback(rv, NULL); |
} |
- next_state_ = STATE_CONNECT; |
- query_.reset(query_->CloneWithNewId(session_->NextId())); |
- int rv = DoLoop(OK); |
- if (rv != ERR_IO_PENDING) |
- DoCallback(rv); |
+ |
+ scoped_refptr<DnsSession> session_; |
+ std::string hostname_; |
+ uint16 qtype_; |
+ // Set to null once the transaction completes. |
+ DnsTransactionFactory::CallbackType callback_; |
+ |
+ BoundNetLog net_log_; |
+ |
+ // Search list of fully-qualified DNS names to query next (in DNS format). |
+ std::deque<std::string> qnames_; |
+ |
+ // List of attempts for the current name. |
+ std::vector<DnsUDPAttempt*> attempts_; |
+ |
+ // Index of the first server to try on each search query. |
+ int first_server_index_; |
+ |
+ base::OneShotTimer<DnsTransactionImpl> timer_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl); |
+}; |
+ |
+// ---------------------------------------------------------------------------- |
+ |
+// Implementation of DnsTransactionFactory that returns instances of |
+// DnsTransactionImpl. |
+class DnsTransactionFactoryImpl : public DnsTransactionFactory { |
+ public: |
+ explicit DnsTransactionFactoryImpl(DnsSession* session) { |
+ session_ = session; |
+ } |
+ |
+ virtual scoped_ptr<DnsTransaction> CreateTransaction( |
+ const std::string& hostname, |
+ uint16 qtype, |
+ const CallbackType& callback, |
+ const BoundNetLog& source_net_log) OVERRIDE { |
+ return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(session_, |
+ hostname, |
+ qtype, |
+ callback, |
+ source_net_log)); |
+ } |
+ |
+ private: |
+ scoped_refptr<DnsSession> session_; |
+}; |
+ |
+} // namespace |
+ |
+// static |
+scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory( |
+ DnsSession* session) { |
+ return scoped_ptr<DnsTransactionFactory>( |
+ new DnsTransactionFactoryImpl(session)); |
} |
} // namespace net |
+ |