This source file includes following definitions.
- CountLabels
- IsIPLiteral
- NetLogStartCallback
- server_index_
- server_index
- NetLogResponseCallback
- set_result
- is_pending
- is_completed
- query_
- Start
- GetQuery
- GetResponse
- GetSocketNetLog
- socket
- DoLoop
- DoSendQuery
- DoSendQueryComplete
- DoReadResponse
- DoReadResponseComplete
- OnIOComplete
- response_length_
- Start
- GetQuery
- GetResponse
- GetSocketNetLog
- DoLoop
- DoConnectComplete
- DoSendLength
- DoSendQuery
- DoReadLength
- DoReadResponse
- OnIOComplete
- first_server_index_
- GetHostname
- GetType
- Start
- PrepareSearch
- DoCallback
- MakeAttempt
- MakeTCPAttempt
- StartQuery
- OnUdpAttemptComplete
- OnAttemptComplete
- RecordLostPacketsIfAny
- LogResponse
- MoreAttemptsAllowed
- ProcessAttemptResult
- OnTimeout
- CreateTransaction
- CreateFactory
#include "net/dns/dns_transaction.h"
#include <deque>
#include <string>
#include <vector>
#include "base/big_endian.h"
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/rand_util.h"
#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "base/threading/non_thread_safe.h"
#include "base/timer/timer.h"
#include "base/values.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"
#include "net/dns/dns_session.h"
#include "net/socket/stream_socket.h"
#include "net/udp/datagram_client_socket.h"
namespace net {
namespace {
#define DNS_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES(name, time, \
base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1), 100)
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);
}
base::Value* NetLogStartCallback(const std::string* hostname,
uint16 qtype,
NetLog::LogLevel ) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetString("hostname", *hostname);
dict->SetInteger("query_type", qtype);
return dict;
};
class DnsAttempt {
public:
explicit DnsAttempt(unsigned server_index)
: result_(ERR_FAILED), server_index_(server_index) {}
virtual ~DnsAttempt() {}
virtual int Start(const CompletionCallback& callback) = 0;
virtual const DnsQuery* GetQuery() const = 0;
virtual const DnsResponse* GetResponse() const = 0;
virtual const BoundNetLog& GetSocketNetLog() const = 0;
unsigned server_index() const { return server_index_; }
base::Value* NetLogResponseCallback(NetLog::LogLevel log_level) const {
DCHECK(GetResponse()->IsValid());
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetInteger("rcode", GetResponse()->rcode());
dict->SetInteger("answer_count", GetResponse()->answer_count());
GetSocketNetLog().source().AddToEventParameters(dict);
return dict;
}
void set_result(int result) {
result_ = result;
}
bool is_pending() const {
return result_ == ERR_IO_PENDING;
}
bool is_completed() const {
return (result_ == OK) || (result_ == ERR_NAME_NOT_RESOLVED) ||
(result_ == ERR_DNS_SERVER_REQUIRES_TCP);
}
private:
int result_;
const unsigned server_index_;
};
class DnsUDPAttempt : public DnsAttempt {
public:
DnsUDPAttempt(unsigned server_index,
scoped_ptr<DnsSession::SocketLease> socket_lease,
scoped_ptr<DnsQuery> query)
: DnsAttempt(server_index),
next_state_(STATE_NONE),
received_malformed_response_(false),
socket_lease_(socket_lease.Pass()),
query_(query.Pass()) {}
virtual int Start(const CompletionCallback& callback) OVERRIDE {
DCHECK_EQ(STATE_NONE, next_state_);
callback_ = callback;
start_time_ = base::TimeTicks::Now();
next_state_ = STATE_SEND_QUERY;
return DoLoop(OK);
}
virtual const DnsQuery* GetQuery() const OVERRIDE {
return query_.get();
}
virtual const DnsResponse* GetResponse() const OVERRIDE {
const DnsResponse* resp = response_.get();
return (resp != NULL && resp->IsValid()) ? resp : NULL;
}
virtual const BoundNetLog& GetSocketNetLog() const OVERRIDE {
return socket_lease_->socket()->NetLog();
}
private:
enum State {
STATE_SEND_QUERY,
STATE_SEND_QUERY_COMPLETE,
STATE_READ_RESPONSE,
STATE_READ_RESPONSE_COMPLETE,
STATE_NONE,
};
DatagramClientSocket* socket() {
return socket_lease_->socket();
}
int DoLoop(int result) {
CHECK_NE(STATE_NONE, next_state_);
int rv = result;
do {
State state = next_state_;
next_state_ = STATE_NONE;
switch (state) {
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);
set_result(rv);
if (rv == ERR_IO_PENDING && received_malformed_response_)
return ERR_DNS_MALFORMED_RESPONSE;
if (rv == OK) {
DCHECK_EQ(STATE_NONE, next_state_);
DNS_HISTOGRAM("AsyncDNS.UDPAttemptSuccess",
base::TimeTicks::Now() - start_time_);
} else if (rv != ERR_IO_PENDING) {
DNS_HISTOGRAM("AsyncDNS.UDPAttemptFail",
base::TimeTicks::Now() - start_time_);
}
return rv;
}
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;
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_)) {
received_malformed_response_ = true;
next_state_ = STATE_READ_RESPONSE;
return OK;
}
if (response_->flags() & dns_protocol::kFlagTC)
return ERR_DNS_SERVER_REQUIRES_TCP;
if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
return ERR_NAME_NOT_RESOLVED;
if (response_->rcode() != dns_protocol::kRcodeNOERROR)
return ERR_DNS_SERVER_FAILED;
return OK;
}
void OnIOComplete(int rv) {
rv = DoLoop(rv);
if (rv != ERR_IO_PENDING)
callback_.Run(rv);
}
State next_state_;
bool received_malformed_response_;
base::TimeTicks start_time_;
scoped_ptr<DnsSession::SocketLease> socket_lease_;
scoped_ptr<DnsQuery> query_;
scoped_ptr<DnsResponse> response_;
CompletionCallback callback_;
DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt);
};
class DnsTCPAttempt : public DnsAttempt {
public:
DnsTCPAttempt(unsigned server_index,
scoped_ptr<StreamSocket> socket,
scoped_ptr<DnsQuery> query)
: DnsAttempt(server_index),
next_state_(STATE_NONE),
socket_(socket.Pass()),
query_(query.Pass()),
length_buffer_(new IOBufferWithSize(sizeof(uint16))),
response_length_(0) {}
virtual int Start(const CompletionCallback& callback) OVERRIDE {
DCHECK_EQ(STATE_NONE, next_state_);
callback_ = callback;
start_time_ = base::TimeTicks::Now();
next_state_ = STATE_CONNECT_COMPLETE;
int rv = socket_->Connect(base::Bind(&DnsTCPAttempt::OnIOComplete,
base::Unretained(this)));
if (rv == ERR_IO_PENDING) {
set_result(rv);
return rv;
}
return DoLoop(rv);
}
virtual const DnsQuery* GetQuery() const OVERRIDE {
return query_.get();
}
virtual const DnsResponse* GetResponse() const OVERRIDE {
const DnsResponse* resp = response_.get();
return (resp != NULL && resp->IsValid()) ? resp : NULL;
}
virtual const BoundNetLog& GetSocketNetLog() const OVERRIDE {
return socket_->NetLog();
}
private:
enum State {
STATE_CONNECT_COMPLETE,
STATE_SEND_LENGTH,
STATE_SEND_QUERY,
STATE_READ_LENGTH,
STATE_READ_RESPONSE,
STATE_NONE,
};
int DoLoop(int result) {
CHECK_NE(STATE_NONE, next_state_);
int rv = result;
do {
State state = next_state_;
next_state_ = STATE_NONE;
switch (state) {
case STATE_CONNECT_COMPLETE:
rv = DoConnectComplete(rv);
break;
case STATE_SEND_LENGTH:
rv = DoSendLength(rv);
break;
case STATE_SEND_QUERY:
rv = DoSendQuery(rv);
break;
case STATE_READ_LENGTH:
rv = DoReadLength(rv);
break;
case STATE_READ_RESPONSE:
rv = DoReadResponse(rv);
break;
default:
NOTREACHED();
break;
}
} while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
set_result(rv);
if (rv == OK) {
DCHECK_EQ(STATE_NONE, next_state_);
DNS_HISTOGRAM("AsyncDNS.TCPAttemptSuccess",
base::TimeTicks::Now() - start_time_);
} else if (rv != ERR_IO_PENDING) {
DNS_HISTOGRAM("AsyncDNS.TCPAttemptFail",
base::TimeTicks::Now() - start_time_);
}
return rv;
}
int DoConnectComplete(int rv) {
DCHECK_NE(ERR_IO_PENDING, rv);
if (rv < 0)
return rv;
base::WriteBigEndian<uint16>(length_buffer_->data(),
query_->io_buffer()->size());
buffer_ =
new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
next_state_ = STATE_SEND_LENGTH;
return OK;
}
int DoSendLength(int rv) {
DCHECK_NE(ERR_IO_PENDING, rv);
if (rv < 0)
return rv;
buffer_->DidConsume(rv);
if (buffer_->BytesRemaining() > 0) {
next_state_ = STATE_SEND_LENGTH;
return socket_->Write(
buffer_.get(),
buffer_->BytesRemaining(),
base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
}
buffer_ = new DrainableIOBuffer(query_->io_buffer(),
query_->io_buffer()->size());
next_state_ = STATE_SEND_QUERY;
return OK;
}
int DoSendQuery(int rv) {
DCHECK_NE(ERR_IO_PENDING, rv);
if (rv < 0)
return rv;
buffer_->DidConsume(rv);
if (buffer_->BytesRemaining() > 0) {
next_state_ = STATE_SEND_QUERY;
return socket_->Write(
buffer_.get(),
buffer_->BytesRemaining(),
base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
}
buffer_ =
new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
next_state_ = STATE_READ_LENGTH;
return OK;
}
int DoReadLength(int rv) {
DCHECK_NE(ERR_IO_PENDING, rv);
if (rv < 0)
return rv;
buffer_->DidConsume(rv);
if (buffer_->BytesRemaining() > 0) {
next_state_ = STATE_READ_LENGTH;
return socket_->Read(
buffer_.get(),
buffer_->BytesRemaining(),
base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
}
base::ReadBigEndian<uint16>(length_buffer_->data(), &response_length_);
if (response_length_ < query_->io_buffer()->size())
return ERR_DNS_MALFORMED_RESPONSE;
response_.reset(new DnsResponse(response_length_ + 1));
buffer_ = new DrainableIOBuffer(response_->io_buffer(), response_length_);
next_state_ = STATE_READ_RESPONSE;
return OK;
}
int DoReadResponse(int rv) {
DCHECK_NE(ERR_IO_PENDING, rv);
if (rv < 0)
return rv;
buffer_->DidConsume(rv);
if (buffer_->BytesRemaining() > 0) {
next_state_ = STATE_READ_RESPONSE;
return socket_->Read(
buffer_.get(),
buffer_->BytesRemaining(),
base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
}
if (!response_->InitParse(buffer_->BytesConsumed(), *query_))
return ERR_DNS_MALFORMED_RESPONSE;
if (response_->flags() & dns_protocol::kFlagTC)
return ERR_UNEXPECTED;
if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
return ERR_NAME_NOT_RESOLVED;
if (response_->rcode() != dns_protocol::kRcodeNOERROR)
return ERR_DNS_SERVER_FAILED;
return OK;
}
void OnIOComplete(int rv) {
rv = DoLoop(rv);
if (rv != ERR_IO_PENDING)
callback_.Run(rv);
}
State next_state_;
base::TimeTicks start_time_;
scoped_ptr<StreamSocket> socket_;
scoped_ptr<DnsQuery> query_;
scoped_refptr<IOBufferWithSize> length_buffer_;
scoped_refptr<DrainableIOBuffer> buffer_;
uint16 response_length_;
scoped_ptr<DnsResponse> response_;
CompletionCallback callback_;
DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt);
};
class DnsTransactionImpl : public DnsTransaction,
public base::NonThreadSafe,
public base::SupportsWeakPtr<DnsTransactionImpl> {
public:
DnsTransactionImpl(DnsSession* session,
const std::string& hostname,
uint16 qtype,
const DnsTransactionFactory::CallbackType& callback,
const BoundNetLog& net_log)
: session_(session),
hostname_(hostname),
qtype_(qtype),
callback_(callback),
net_log_(net_log),
qnames_initial_size_(0),
attempts_count_(0),
had_tcp_attempt_(false),
first_server_index_(0) {
DCHECK(session_.get());
DCHECK(!hostname_.empty());
DCHECK(!callback_.is_null());
DCHECK(!IsIPLiteral(hostname_));
}
virtual ~DnsTransactionImpl() {
if (!callback_.is_null()) {
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION,
ERR_ABORTED);
}
}
virtual const std::string& GetHostname() const OVERRIDE {
DCHECK(CalledOnValidThread());
return hostname_;
}
virtual uint16 GetType() const OVERRIDE {
DCHECK(CalledOnValidThread());
return qtype_;
}
virtual void Start() OVERRIDE {
DCHECK(!callback_.is_null());
DCHECK(attempts_.empty());
net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION,
base::Bind(&NetLogStartCallback, &hostname_, qtype_));
AttemptResult result(PrepareSearch(), NULL);
if (result.rv == OK) {
qnames_initial_size_ = qnames_.size();
if (qtype_ == dns_protocol::kTypeA)
UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchStart", qnames_.size());
result = ProcessAttemptResult(StartQuery());
}
if (result.rv != ERR_IO_PENDING) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result));
}
}
private:
struct AttemptResult {
AttemptResult(int rv, const DnsAttempt* attempt)
: rv(rv), attempt(attempt) {}
int rv;
const DnsAttempt* attempt;
};
int PrepareSearch() {
const DnsConfig& config = session_->config();
std::string labeled_hostname;
if (!DNSDomainFromDot(hostname_, &labeled_hostname))
return ERR_INVALID_ARGUMENT;
if (hostname_[hostname_.size() - 1] == '.') {
qnames_.push_back(labeled_hostname);
return OK;
}
int ndots = CountLabels(labeled_hostname) - 1;
if (ndots > 0 && !config.append_to_multi_label_name) {
qnames_.push_back(labeled_hostname);
return OK;
}
bool had_hostname = false;
if (ndots >= config.ndots) {
qnames_.push_back(labeled_hostname);
had_hostname = true;
}
std::string qname;
for (size_t i = 0; i < config.search.size(); ++i) {
if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname))
continue;
if (qname.size() == labeled_hostname.size()) {
if (had_hostname)
continue;
had_hostname = true;
}
qnames_.push_back(qname);
}
if (ndots > 0 && !had_hostname)
qnames_.push_back(labeled_hostname);
return qnames_.empty() ? ERR_DNS_SEARCH_EMPTY : OK;
}
void DoCallback(AttemptResult result) {
DCHECK(!callback_.is_null());
DCHECK_NE(ERR_IO_PENDING, result.rv);
const DnsResponse* response = result.attempt ?
result.attempt->GetResponse() : NULL;
CHECK(result.rv != OK || response != NULL);
timer_.Stop();
RecordLostPacketsIfAny();
if (result.rv == OK)
UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountSuccess", attempts_count_);
else
UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountFail", attempts_count_);
if (response && qtype_ == dns_protocol::kTypeA) {
UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchRemain", qnames_.size());
UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchDone",
qnames_initial_size_ - qnames_.size());
}
DnsTransactionFactory::CallbackType callback = callback_;
callback_.Reset();
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, result.rv);
callback.Run(this, result.rv, response);
}
AttemptResult MakeAttempt() {
unsigned attempt_number = attempts_.size();
uint16 id = session_->NextQueryId();
scoped_ptr<DnsQuery> query;
if (attempts_.empty()) {
query.reset(new DnsQuery(id, qnames_.front(), qtype_));
} else {
query.reset(attempts_[0]->GetQuery()->CloneWithNewId(id));
}
const DnsConfig& config = session_->config();
unsigned server_index =
(first_server_index_ + attempt_number) % config.nameservers.size();
server_index = session_->NextGoodServerIndex(server_index);
scoped_ptr<DnsSession::SocketLease> lease =
session_->AllocateSocket(server_index, net_log_.source());
bool got_socket = !!lease.get();
DnsUDPAttempt* attempt =
new DnsUDPAttempt(server_index, lease.Pass(), query.Pass());
attempts_.push_back(attempt);
++attempts_count_;
if (!got_socket)
return AttemptResult(ERR_CONNECTION_REFUSED, NULL);
net_log_.AddEvent(
NetLog::TYPE_DNS_TRANSACTION_ATTEMPT,
attempt->GetSocketNetLog().source().ToEventParametersCallback());
int rv = attempt->Start(
base::Bind(&DnsTransactionImpl::OnUdpAttemptComplete,
base::Unretained(this), attempt_number,
base::TimeTicks::Now()));
if (rv == ERR_IO_PENDING) {
base::TimeDelta timeout = session_->NextTimeout(server_index,
attempt_number);
timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
}
return AttemptResult(rv, attempt);
}
AttemptResult MakeTCPAttempt(const DnsAttempt* previous_attempt) {
DCHECK(previous_attempt);
DCHECK(!had_tcp_attempt_);
unsigned server_index = previous_attempt->server_index();
scoped_ptr<StreamSocket> socket(
session_->CreateTCPSocket(server_index, net_log_.source()));
uint16 id = session_->NextQueryId();
scoped_ptr<DnsQuery> query(
previous_attempt->GetQuery()->CloneWithNewId(id));
RecordLostPacketsIfAny();
attempts_.clear();
unsigned attempt_number = attempts_.size();
DnsTCPAttempt* attempt = new DnsTCPAttempt(server_index, socket.Pass(),
query.Pass());
attempts_.push_back(attempt);
++attempts_count_;
had_tcp_attempt_ = true;
net_log_.AddEvent(
NetLog::TYPE_DNS_TRANSACTION_TCP_ATTEMPT,
attempt->GetSocketNetLog().source().ToEventParametersCallback());
int rv = attempt->Start(base::Bind(&DnsTransactionImpl::OnAttemptComplete,
base::Unretained(this),
attempt_number));
if (rv == ERR_IO_PENDING) {
base::TimeDelta timeout = timer_.GetCurrentDelay() * 2;
timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
}
return AttemptResult(rv, attempt);
}
AttemptResult StartQuery() {
std::string dotted_qname = DNSDomainToString(qnames_.front());
net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION_QUERY,
NetLog::StringCallback("qname", &dotted_qname));
first_server_index_ = session_->NextFirstServerIndex();
RecordLostPacketsIfAny();
attempts_.clear();
had_tcp_attempt_ = false;
return MakeAttempt();
}
void OnUdpAttemptComplete(unsigned attempt_number,
base::TimeTicks start,
int rv) {
DCHECK_LT(attempt_number, attempts_.size());
const DnsAttempt* attempt = attempts_[attempt_number];
if (attempt->GetResponse()) {
session_->RecordRTT(attempt->server_index(),
base::TimeTicks::Now() - start);
}
OnAttemptComplete(attempt_number, rv);
}
void OnAttemptComplete(unsigned attempt_number, int rv) {
if (callback_.is_null())
return;
DCHECK_LT(attempt_number, attempts_.size());
const DnsAttempt* attempt = attempts_[attempt_number];
AttemptResult result = ProcessAttemptResult(AttemptResult(rv, attempt));
if (result.rv != ERR_IO_PENDING)
DoCallback(result);
}
void RecordLostPacketsIfAny() {
size_t first_completed = 0;
for (first_completed = 0; first_completed < attempts_.size();
++first_completed) {
if (attempts_[first_completed]->is_completed())
break;
}
if (first_completed == attempts_.size())
return;
size_t num_servers = session_->config().nameservers.size();
std::vector<int> server_attempts(num_servers);
for (size_t i = 0; i < first_completed; ++i) {
unsigned server_index = attempts_[i]->server_index();
int server_attempt = server_attempts[server_index]++;
if (!attempts_[i]->is_pending())
continue;
session_->RecordLostPacket(server_index, server_attempt);
}
}
void LogResponse(const DnsAttempt* attempt) {
if (attempt && attempt->GetResponse()) {
net_log_.AddEvent(
NetLog::TYPE_DNS_TRANSACTION_RESPONSE,
base::Bind(&DnsAttempt::NetLogResponseCallback,
base::Unretained(attempt)));
}
}
bool MoreAttemptsAllowed() const {
if (had_tcp_attempt_)
return false;
const DnsConfig& config = session_->config();
return attempts_.size() < config.attempts * config.nameservers.size();
}
AttemptResult ProcessAttemptResult(AttemptResult result) {
while (result.rv != ERR_IO_PENDING) {
LogResponse(result.attempt);
switch (result.rv) {
case OK:
session_->RecordServerSuccess(result.attempt->server_index());
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
result.rv);
DCHECK(result.attempt);
DCHECK(result.attempt->GetResponse());
return result;
case ERR_NAME_NOT_RESOLVED:
session_->RecordServerSuccess(result.attempt->server_index());
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
result.rv);
qnames_.pop_front();
if (qnames_.empty()) {
return AttemptResult(ERR_NAME_NOT_RESOLVED, NULL);
} else {
result = StartQuery();
}
break;
case ERR_CONNECTION_REFUSED:
case ERR_DNS_TIMED_OUT:
if (result.attempt)
session_->RecordServerFailure(result.attempt->server_index());
if (MoreAttemptsAllowed()) {
result = MakeAttempt();
} else {
return result;
}
break;
case ERR_DNS_SERVER_REQUIRES_TCP:
result = MakeTCPAttempt(result.attempt);
break;
default:
DCHECK(result.attempt);
if (result.attempt != attempts_.back()) {
session_->RecordServerFailure(result.attempt->server_index());
return AttemptResult(ERR_IO_PENDING, NULL);
}
if (MoreAttemptsAllowed()) {
result = MakeAttempt();
} else if (result.rv == ERR_DNS_MALFORMED_RESPONSE &&
!had_tcp_attempt_) {
return AttemptResult(ERR_IO_PENDING, NULL);
} else {
return AttemptResult(result.rv, NULL);
}
break;
}
}
return result;
}
void OnTimeout() {
if (callback_.is_null())
return;
DCHECK(!attempts_.empty());
AttemptResult result = ProcessAttemptResult(
AttemptResult(ERR_DNS_TIMED_OUT, attempts_.back()));
if (result.rv != ERR_IO_PENDING)
DoCallback(result);
}
scoped_refptr<DnsSession> session_;
std::string hostname_;
uint16 qtype_;
DnsTransactionFactory::CallbackType callback_;
BoundNetLog net_log_;
std::deque<std::string> qnames_;
size_t qnames_initial_size_;
ScopedVector<DnsAttempt> attempts_;
int attempts_count_;
bool had_tcp_attempt_;
int first_server_index_;
base::OneShotTimer<DnsTransactionImpl> timer_;
DISALLOW_COPY_AND_ASSIGN(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& net_log) OVERRIDE {
return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(
session_.get(), hostname, qtype, callback, net_log));
}
private:
scoped_refptr<DnsSession> session_;
};
}
scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory(
DnsSession* session) {
return scoped_ptr<DnsTransactionFactory>(
new DnsTransactionFactoryImpl(session));
}
}