1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/dns/dns_transaction.h"
11 #include "base/big_endian.h"
12 #include "base/bind.h"
13 #include "base/location.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/scoped_vector.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/metrics/histogram.h"
19 #include "base/profiler/scoped_tracker.h"
20 #include "base/rand_util.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/stl_util.h"
23 #include "base/strings/string_piece.h"
24 #include "base/thread_task_runner_handle.h"
25 #include "base/threading/non_thread_safe.h"
26 #include "base/timer/timer.h"
27 #include "base/values.h"
28 #include "net/base/completion_callback.h"
29 #include "net/base/dns_util.h"
30 #include "net/base/io_buffer.h"
31 #include "net/base/ip_endpoint.h"
32 #include "net/base/net_errors.h"
33 #include "net/dns/dns_protocol.h"
34 #include "net/dns/dns_query.h"
35 #include "net/dns/dns_response.h"
36 #include "net/dns/dns_session.h"
37 #include "net/log/net_log.h"
38 #include "net/socket/stream_socket.h"
39 #include "net/udp/datagram_client_socket.h"
45 // Provide a common macro to simplify code and readability. We must use a
46 // macro as the underlying HISTOGRAM macro creates static variables.
47 #define DNS_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES(name, time, \
48 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1), 100)
50 // Count labels in the fully-qualified name in DNS format.
51 int CountLabels(const std::string
& name
) {
53 for (size_t i
= 0; i
< name
.size() && name
[i
]; i
+= name
[i
] + 1)
58 bool IsIPLiteral(const std::string
& hostname
) {
60 return ParseIPLiteralToNumber(hostname
, &ip
);
63 scoped_ptr
<base::Value
> NetLogStartCallback(
64 const std::string
* hostname
,
66 NetLogCaptureMode
/* capture_mode */) {
67 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
68 dict
->SetString("hostname", *hostname
);
69 dict
->SetInteger("query_type", qtype
);
73 // ----------------------------------------------------------------------------
75 // A single asynchronous DNS exchange, which consists of sending out a
76 // DNS query, waiting for a response, and returning the response that it
77 // matches. Logging is done in the socket and in the outer DnsTransaction.
80 explicit DnsAttempt(unsigned server_index
)
81 : result_(ERR_FAILED
), server_index_(server_index
) {}
83 virtual ~DnsAttempt() {}
84 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously
85 // and calls |callback| upon completion.
86 virtual int Start(const CompletionCallback
& callback
) = 0;
88 // Returns the query of this attempt.
89 virtual const DnsQuery
* GetQuery() const = 0;
91 // Returns the response or NULL if has not received a matching response from
93 virtual const DnsResponse
* GetResponse() const = 0;
95 // Returns the net log bound to the source of the socket.
96 virtual const BoundNetLog
& GetSocketNetLog() const = 0;
98 // Returns the index of the destination server within DnsConfig::nameservers.
99 unsigned server_index() const { return server_index_
; }
101 // Returns a Value representing the received response, along with a reference
102 // to the NetLog source source of the UDP socket used. The request must have
103 // completed before this is called.
104 scoped_ptr
<base::Value
> NetLogResponseCallback(
105 NetLogCaptureMode capture_mode
) const {
106 DCHECK(GetResponse()->IsValid());
108 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
109 dict
->SetInteger("rcode", GetResponse()->rcode());
110 dict
->SetInteger("answer_count", GetResponse()->answer_count());
111 GetSocketNetLog().source().AddToEventParameters(dict
.get());
115 void set_result(int result
) {
119 // True if current attempt is pending (waiting for server response).
120 bool is_pending() const {
121 return result_
== ERR_IO_PENDING
;
124 // True if attempt is completed (received server response).
125 bool is_completed() const {
126 return (result_
== OK
) || (result_
== ERR_NAME_NOT_RESOLVED
) ||
127 (result_
== ERR_DNS_SERVER_REQUIRES_TCP
);
131 // Result of last operation.
134 const unsigned server_index_
;
137 class DnsUDPAttempt
: public DnsAttempt
{
139 DnsUDPAttempt(unsigned server_index
,
140 scoped_ptr
<DnsSession::SocketLease
> socket_lease
,
141 scoped_ptr
<DnsQuery
> query
)
142 : DnsAttempt(server_index
),
143 next_state_(STATE_NONE
),
144 received_malformed_response_(false),
145 socket_lease_(socket_lease
.Pass()),
146 query_(query
.Pass()) {}
149 int Start(const CompletionCallback
& callback
) override
{
150 DCHECK_EQ(STATE_NONE
, next_state_
);
151 callback_
= callback
;
152 start_time_
= base::TimeTicks::Now();
153 next_state_
= STATE_SEND_QUERY
;
157 const DnsQuery
* GetQuery() const override
{ return query_
.get(); }
159 const DnsResponse
* GetResponse() const override
{
160 const DnsResponse
* resp
= response_
.get();
161 return (resp
!= NULL
&& resp
->IsValid()) ? resp
: NULL
;
164 const BoundNetLog
& GetSocketNetLog() const override
{
165 return socket_lease_
->socket()->NetLog();
171 STATE_SEND_QUERY_COMPLETE
,
173 STATE_READ_RESPONSE_COMPLETE
,
177 DatagramClientSocket
* socket() {
178 return socket_lease_
->socket();
181 int DoLoop(int result
) {
182 CHECK_NE(STATE_NONE
, next_state_
);
185 State state
= next_state_
;
186 next_state_
= STATE_NONE
;
188 case STATE_SEND_QUERY
:
191 case STATE_SEND_QUERY_COMPLETE
:
192 rv
= DoSendQueryComplete(rv
);
194 case STATE_READ_RESPONSE
:
195 rv
= DoReadResponse();
197 case STATE_READ_RESPONSE_COMPLETE
:
198 rv
= DoReadResponseComplete(rv
);
204 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
207 // If we received a malformed response, and are now waiting for another one,
208 // indicate to the transaction that the server might be misbehaving.
209 if (rv
== ERR_IO_PENDING
&& received_malformed_response_
)
210 return ERR_DNS_MALFORMED_RESPONSE
;
212 DCHECK_EQ(STATE_NONE
, next_state_
);
213 DNS_HISTOGRAM("AsyncDNS.UDPAttemptSuccess",
214 base::TimeTicks::Now() - start_time_
);
215 } else if (rv
!= ERR_IO_PENDING
) {
216 DNS_HISTOGRAM("AsyncDNS.UDPAttemptFail",
217 base::TimeTicks::Now() - start_time_
);
223 next_state_
= STATE_SEND_QUERY_COMPLETE
;
224 return socket()->Write(query_
->io_buffer(),
225 query_
->io_buffer()->size(),
226 base::Bind(&DnsUDPAttempt::OnIOComplete
,
227 base::Unretained(this)));
230 int DoSendQueryComplete(int rv
) {
231 DCHECK_NE(ERR_IO_PENDING
, rv
);
235 // Writing to UDP should not result in a partial datagram.
236 if (rv
!= query_
->io_buffer()->size())
237 return ERR_MSG_TOO_BIG
;
239 next_state_
= STATE_READ_RESPONSE
;
243 int DoReadResponse() {
244 next_state_
= STATE_READ_RESPONSE_COMPLETE
;
245 response_
.reset(new DnsResponse());
246 return socket()->Read(response_
->io_buffer(),
247 response_
->io_buffer()->size(),
248 base::Bind(&DnsUDPAttempt::OnIOComplete
,
249 base::Unretained(this)));
252 int DoReadResponseComplete(int rv
) {
253 DCHECK_NE(ERR_IO_PENDING
, rv
);
258 if (!response_
->InitParse(rv
, *query_
)) {
259 // Other implementations simply ignore mismatched responses. Since each
260 // DnsUDPAttempt binds to a different port, we might find that responses
261 // to previously timed out queries lead to failures in the future.
262 // Our solution is to make another attempt, in case the query truly
263 // failed, but keep this attempt alive, in case it was a false alarm.
264 received_malformed_response_
= true;
265 next_state_
= STATE_READ_RESPONSE
;
268 if (response_
->flags() & dns_protocol::kFlagTC
)
269 return ERR_DNS_SERVER_REQUIRES_TCP
;
270 // TODO(szym): Extract TTL for NXDOMAIN results. http://crbug.com/115051
271 if (response_
->rcode() == dns_protocol::kRcodeNXDOMAIN
)
272 return ERR_NAME_NOT_RESOLVED
;
273 if (response_
->rcode() != dns_protocol::kRcodeNOERROR
)
274 return ERR_DNS_SERVER_FAILED
;
279 void OnIOComplete(int rv
) {
281 if (rv
!= ERR_IO_PENDING
)
286 bool received_malformed_response_
;
287 base::TimeTicks start_time_
;
289 scoped_ptr
<DnsSession::SocketLease
> socket_lease_
;
290 scoped_ptr
<DnsQuery
> query_
;
292 scoped_ptr
<DnsResponse
> response_
;
294 CompletionCallback callback_
;
296 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt
);
299 class DnsTCPAttempt
: public DnsAttempt
{
301 DnsTCPAttempt(unsigned server_index
,
302 scoped_ptr
<StreamSocket
> socket
,
303 scoped_ptr
<DnsQuery
> query
)
304 : DnsAttempt(server_index
),
305 next_state_(STATE_NONE
),
306 socket_(socket
.Pass()),
307 query_(query
.Pass()),
308 length_buffer_(new IOBufferWithSize(sizeof(uint16
))),
309 response_length_(0) {}
312 int Start(const CompletionCallback
& callback
) override
{
313 DCHECK_EQ(STATE_NONE
, next_state_
);
314 callback_
= callback
;
315 start_time_
= base::TimeTicks::Now();
316 next_state_
= STATE_CONNECT_COMPLETE
;
317 int rv
= socket_
->Connect(base::Bind(&DnsTCPAttempt::OnIOComplete
,
318 base::Unretained(this)));
319 if (rv
== ERR_IO_PENDING
) {
326 const DnsQuery
* GetQuery() const override
{ return query_
.get(); }
328 const DnsResponse
* GetResponse() const override
{
329 const DnsResponse
* resp
= response_
.get();
330 return (resp
!= NULL
&& resp
->IsValid()) ? resp
: NULL
;
333 const BoundNetLog
& GetSocketNetLog() const override
{
334 return socket_
->NetLog();
339 STATE_CONNECT_COMPLETE
,
343 STATE_READ_LENGTH_COMPLETE
,
345 STATE_READ_RESPONSE_COMPLETE
,
349 int DoLoop(int result
) {
350 CHECK_NE(STATE_NONE
, next_state_
);
353 State state
= next_state_
;
354 next_state_
= STATE_NONE
;
356 case STATE_CONNECT_COMPLETE
:
357 rv
= DoConnectComplete(rv
);
359 case STATE_SEND_LENGTH
:
360 rv
= DoSendLength(rv
);
362 case STATE_SEND_QUERY
:
363 rv
= DoSendQuery(rv
);
365 case STATE_READ_LENGTH
:
366 rv
= DoReadLength(rv
);
368 case STATE_READ_LENGTH_COMPLETE
:
369 rv
= DoReadLengthComplete(rv
);
371 case STATE_READ_RESPONSE
:
372 rv
= DoReadResponse(rv
);
374 case STATE_READ_RESPONSE_COMPLETE
:
375 rv
= DoReadResponseComplete(rv
);
381 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
385 DCHECK_EQ(STATE_NONE
, next_state_
);
386 DNS_HISTOGRAM("AsyncDNS.TCPAttemptSuccess",
387 base::TimeTicks::Now() - start_time_
);
388 } else if (rv
!= ERR_IO_PENDING
) {
389 DNS_HISTOGRAM("AsyncDNS.TCPAttemptFail",
390 base::TimeTicks::Now() - start_time_
);
395 int DoConnectComplete(int rv
) {
396 // TODO(rvargas): Remove ScopedTracker below once crbug.com/462784 is fixed.
397 tracked_objects::ScopedTracker
tracking_profile(
398 FROM_HERE_WITH_EXPLICIT_FUNCTION(
399 "462784 DnsTCPAttempt::DoConnectComplete"));
401 DCHECK_NE(ERR_IO_PENDING
, rv
);
405 uint16 query_size
= static_cast<uint16
>(query_
->io_buffer()->size());
406 if (static_cast<int>(query_size
) != query_
->io_buffer()->size())
408 base::WriteBigEndian
<uint16
>(length_buffer_
->data(), query_size
);
410 new DrainableIOBuffer(length_buffer_
.get(), length_buffer_
->size());
411 next_state_
= STATE_SEND_LENGTH
;
415 int DoSendLength(int rv
) {
416 DCHECK_NE(ERR_IO_PENDING
, rv
);
420 buffer_
->DidConsume(rv
);
421 if (buffer_
->BytesRemaining() > 0) {
422 next_state_
= STATE_SEND_LENGTH
;
423 return socket_
->Write(
425 buffer_
->BytesRemaining(),
426 base::Bind(&DnsTCPAttempt::OnIOComplete
, base::Unretained(this)));
428 buffer_
= new DrainableIOBuffer(query_
->io_buffer(),
429 query_
->io_buffer()->size());
430 next_state_
= STATE_SEND_QUERY
;
434 int DoSendQuery(int rv
) {
435 DCHECK_NE(ERR_IO_PENDING
, rv
);
439 buffer_
->DidConsume(rv
);
440 if (buffer_
->BytesRemaining() > 0) {
441 next_state_
= STATE_SEND_QUERY
;
442 return socket_
->Write(
444 buffer_
->BytesRemaining(),
445 base::Bind(&DnsTCPAttempt::OnIOComplete
, base::Unretained(this)));
448 new DrainableIOBuffer(length_buffer_
.get(), length_buffer_
->size());
449 next_state_
= STATE_READ_LENGTH
;
453 int DoReadLength(int rv
) {
456 next_state_
= STATE_READ_LENGTH_COMPLETE
;
457 return ReadIntoBuffer();
460 int DoReadLengthComplete(int rv
) {
461 DCHECK_NE(ERR_IO_PENDING
, rv
);
465 return ERR_CONNECTION_CLOSED
;
467 buffer_
->DidConsume(rv
);
468 if (buffer_
->BytesRemaining() > 0) {
469 next_state_
= STATE_READ_LENGTH
;
473 base::ReadBigEndian
<uint16
>(length_buffer_
->data(), &response_length_
);
474 // Check if advertised response is too short. (Optimization only.)
475 if (response_length_
< query_
->io_buffer()->size())
476 return ERR_DNS_MALFORMED_RESPONSE
;
477 // Allocate more space so that DnsResponse::InitParse sanity check passes.
478 response_
.reset(new DnsResponse(response_length_
+ 1));
479 buffer_
= new DrainableIOBuffer(response_
->io_buffer(), response_length_
);
480 next_state_
= STATE_READ_RESPONSE
;
484 int DoReadResponse(int rv
) {
487 next_state_
= STATE_READ_RESPONSE_COMPLETE
;
488 return ReadIntoBuffer();
491 int DoReadResponseComplete(int rv
) {
492 DCHECK_NE(ERR_IO_PENDING
, rv
);
496 return ERR_CONNECTION_CLOSED
;
498 buffer_
->DidConsume(rv
);
499 if (buffer_
->BytesRemaining() > 0) {
500 next_state_
= STATE_READ_RESPONSE
;
504 if (!response_
->InitParse(buffer_
->BytesConsumed(), *query_
))
505 return ERR_DNS_MALFORMED_RESPONSE
;
506 if (response_
->flags() & dns_protocol::kFlagTC
)
507 return ERR_UNEXPECTED
;
508 // TODO(szym): Frankly, none of these are expected.
509 if (response_
->rcode() == dns_protocol::kRcodeNXDOMAIN
)
510 return ERR_NAME_NOT_RESOLVED
;
511 if (response_
->rcode() != dns_protocol::kRcodeNOERROR
)
512 return ERR_DNS_SERVER_FAILED
;
517 void OnIOComplete(int rv
) {
519 if (rv
!= ERR_IO_PENDING
)
523 int ReadIntoBuffer() {
524 return socket_
->Read(
526 buffer_
->BytesRemaining(),
527 base::Bind(&DnsTCPAttempt::OnIOComplete
, base::Unretained(this)));
531 base::TimeTicks start_time_
;
533 scoped_ptr
<StreamSocket
> socket_
;
534 scoped_ptr
<DnsQuery
> query_
;
535 scoped_refptr
<IOBufferWithSize
> length_buffer_
;
536 scoped_refptr
<DrainableIOBuffer
> buffer_
;
538 uint16 response_length_
;
539 scoped_ptr
<DnsResponse
> response_
;
541 CompletionCallback callback_
;
543 DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt
);
546 // ----------------------------------------------------------------------------
548 // Implements DnsTransaction. Configuration is supplied by DnsSession.
549 // The suffix list is built according to the DnsConfig from the session.
550 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout.
551 // The first server to attempt on each query is given by
552 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards.
553 // Each server is attempted DnsConfig::attempts times.
554 class DnsTransactionImpl
: public DnsTransaction
,
555 public base::NonThreadSafe
,
556 public base::SupportsWeakPtr
<DnsTransactionImpl
> {
558 DnsTransactionImpl(DnsSession
* session
,
559 const std::string
& hostname
,
561 const DnsTransactionFactory::CallbackType
& callback
,
562 const BoundNetLog
& net_log
)
568 qnames_initial_size_(0),
570 had_tcp_attempt_(false),
571 first_server_index_(0) {
572 DCHECK(session_
.get());
573 DCHECK(!hostname_
.empty());
574 DCHECK(!callback_
.is_null());
575 DCHECK(!IsIPLiteral(hostname_
));
578 ~DnsTransactionImpl() override
{
579 if (!callback_
.is_null()) {
580 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION
,
582 } // otherwise logged in DoCallback or Start
585 const std::string
& GetHostname() const override
{
586 DCHECK(CalledOnValidThread());
590 uint16
GetType() const override
{
591 DCHECK(CalledOnValidThread());
595 void Start() override
{
596 DCHECK(!callback_
.is_null());
597 DCHECK(attempts_
.empty());
598 net_log_
.BeginEvent(NetLog::TYPE_DNS_TRANSACTION
,
599 base::Bind(&NetLogStartCallback
, &hostname_
, qtype_
));
600 AttemptResult
result(PrepareSearch(), NULL
);
601 if (result
.rv
== OK
) {
602 qnames_initial_size_
= qnames_
.size();
603 if (qtype_
== dns_protocol::kTypeA
)
604 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchStart", qnames_
.size());
605 result
= ProcessAttemptResult(StartQuery());
608 // Must always return result asynchronously, to avoid reentrancy.
609 if (result
.rv
!= ERR_IO_PENDING
) {
610 base::ThreadTaskRunnerHandle::Get()->PostTask(
612 base::Bind(&DnsTransactionImpl::DoCallback
, AsWeakPtr(), result
));
617 // Wrapper for the result of a DnsUDPAttempt.
618 struct AttemptResult
{
619 AttemptResult(int rv
, const DnsAttempt
* attempt
)
620 : rv(rv
), attempt(attempt
) {}
623 const DnsAttempt
* attempt
;
626 // Prepares |qnames_| according to the DnsConfig.
627 int PrepareSearch() {
628 const DnsConfig
& config
= session_
->config();
630 std::string labeled_hostname
;
631 if (!DNSDomainFromDot(hostname_
, &labeled_hostname
))
632 return ERR_INVALID_ARGUMENT
;
634 if (hostname_
[hostname_
.size() - 1] == '.') {
635 // It's a fully-qualified name, no suffix search.
636 qnames_
.push_back(labeled_hostname
);
640 int ndots
= CountLabels(labeled_hostname
) - 1;
642 if (ndots
> 0 && !config
.append_to_multi_label_name
) {
643 qnames_
.push_back(labeled_hostname
);
647 // Set true when |labeled_hostname| is put on the list.
648 bool had_hostname
= false;
650 if (ndots
>= config
.ndots
) {
651 qnames_
.push_back(labeled_hostname
);
656 for (size_t i
= 0; i
< config
.search
.size(); ++i
) {
657 // Ignore invalid (too long) combinations.
658 if (!DNSDomainFromDot(hostname_
+ "." + config
.search
[i
], &qname
))
660 if (qname
.size() == labeled_hostname
.size()) {
665 qnames_
.push_back(qname
);
668 if (ndots
> 0 && !had_hostname
)
669 qnames_
.push_back(labeled_hostname
);
671 return qnames_
.empty() ? ERR_DNS_SEARCH_EMPTY
: OK
;
674 void DoCallback(AttemptResult result
) {
675 DCHECK(!callback_
.is_null());
676 DCHECK_NE(ERR_IO_PENDING
, result
.rv
);
677 const DnsResponse
* response
= result
.attempt
?
678 result
.attempt
->GetResponse() : NULL
;
679 CHECK(result
.rv
!= OK
|| response
!= NULL
);
682 RecordLostPacketsIfAny();
684 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountSuccess", attempts_count_
);
686 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountFail", attempts_count_
);
688 if (response
&& qtype_
== dns_protocol::kTypeA
) {
689 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchRemain", qnames_
.size());
690 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchDone",
691 qnames_initial_size_
- qnames_
.size());
694 DnsTransactionFactory::CallbackType callback
= callback_
;
697 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION
, result
.rv
);
698 callback
.Run(this, result
.rv
, response
);
701 // Makes another attempt at the current name, |qnames_.front()|, using the
703 AttemptResult
MakeAttempt() {
704 unsigned attempt_number
= attempts_
.size();
706 uint16 id
= session_
->NextQueryId();
707 scoped_ptr
<DnsQuery
> query
;
708 if (attempts_
.empty()) {
709 query
.reset(new DnsQuery(id
, qnames_
.front(), qtype_
));
711 query
.reset(attempts_
[0]->GetQuery()->CloneWithNewId(id
));
714 const DnsConfig
& config
= session_
->config();
716 unsigned server_index
=
717 (first_server_index_
+ attempt_number
) % config
.nameservers
.size();
718 // Skip over known failed servers.
719 server_index
= session_
->NextGoodServerIndex(server_index
);
721 scoped_ptr
<DnsSession::SocketLease
> lease
=
722 session_
->AllocateSocket(server_index
, net_log_
.source());
724 bool got_socket
= !!lease
.get();
726 DnsUDPAttempt
* attempt
=
727 new DnsUDPAttempt(server_index
, lease
.Pass(), query
.Pass());
729 attempts_
.push_back(attempt
);
733 return AttemptResult(ERR_CONNECTION_REFUSED
, NULL
);
736 NetLog::TYPE_DNS_TRANSACTION_ATTEMPT
,
737 attempt
->GetSocketNetLog().source().ToEventParametersCallback());
739 int rv
= attempt
->Start(
740 base::Bind(&DnsTransactionImpl::OnUdpAttemptComplete
,
741 base::Unretained(this), attempt_number
,
742 base::TimeTicks::Now()));
743 if (rv
== ERR_IO_PENDING
) {
744 base::TimeDelta timeout
= session_
->NextTimeout(server_index
,
746 timer_
.Start(FROM_HERE
, timeout
, this, &DnsTransactionImpl::OnTimeout
);
748 return AttemptResult(rv
, attempt
);
751 AttemptResult
MakeTCPAttempt(const DnsAttempt
* previous_attempt
) {
752 DCHECK(previous_attempt
);
753 DCHECK(!had_tcp_attempt_
);
755 unsigned server_index
= previous_attempt
->server_index();
757 scoped_ptr
<StreamSocket
> socket(
758 session_
->CreateTCPSocket(server_index
, net_log_
.source()));
760 // TODO(szym): Reuse the same id to help the server?
761 uint16 id
= session_
->NextQueryId();
762 scoped_ptr
<DnsQuery
> query(
763 previous_attempt
->GetQuery()->CloneWithNewId(id
));
765 RecordLostPacketsIfAny();
766 // Cancel all other attempts, no point waiting on them.
769 unsigned attempt_number
= attempts_
.size();
771 DnsTCPAttempt
* attempt
= new DnsTCPAttempt(server_index
, socket
.Pass(),
774 attempts_
.push_back(attempt
);
776 had_tcp_attempt_
= true;
779 NetLog::TYPE_DNS_TRANSACTION_TCP_ATTEMPT
,
780 attempt
->GetSocketNetLog().source().ToEventParametersCallback());
782 int rv
= attempt
->Start(base::Bind(&DnsTransactionImpl::OnAttemptComplete
,
783 base::Unretained(this),
785 if (rv
== ERR_IO_PENDING
) {
786 // Custom timeout for TCP attempt.
787 base::TimeDelta timeout
= timer_
.GetCurrentDelay() * 2;
788 timer_
.Start(FROM_HERE
, timeout
, this, &DnsTransactionImpl::OnTimeout
);
790 return AttemptResult(rv
, attempt
);
793 // Begins query for the current name. Makes the first attempt.
794 AttemptResult
StartQuery() {
795 std::string dotted_qname
= DNSDomainToString(qnames_
.front());
796 net_log_
.BeginEvent(NetLog::TYPE_DNS_TRANSACTION_QUERY
,
797 NetLog::StringCallback("qname", &dotted_qname
));
799 first_server_index_
= session_
->NextFirstServerIndex();
800 RecordLostPacketsIfAny();
802 had_tcp_attempt_
= false;
803 return MakeAttempt();
806 void OnUdpAttemptComplete(unsigned attempt_number
,
807 base::TimeTicks start
,
809 DCHECK_LT(attempt_number
, attempts_
.size());
810 const DnsAttempt
* attempt
= attempts_
[attempt_number
];
811 if (attempt
->GetResponse()) {
812 session_
->RecordRTT(attempt
->server_index(),
813 base::TimeTicks::Now() - start
);
815 OnAttemptComplete(attempt_number
, rv
);
818 void OnAttemptComplete(unsigned attempt_number
, int rv
) {
819 if (callback_
.is_null())
821 DCHECK_LT(attempt_number
, attempts_
.size());
822 const DnsAttempt
* attempt
= attempts_
[attempt_number
];
823 AttemptResult result
= ProcessAttemptResult(AttemptResult(rv
, attempt
));
824 if (result
.rv
!= ERR_IO_PENDING
)
828 // Record packet loss for any incomplete attempts.
829 void RecordLostPacketsIfAny() {
830 // Loop through attempts until we find first that is completed
831 size_t first_completed
= 0;
832 for (first_completed
= 0; first_completed
< attempts_
.size();
834 if (attempts_
[first_completed
]->is_completed())
837 // If there were no completed attempts, then we must be offline, so don't
838 // record any attempts as lost packets.
839 if (first_completed
== attempts_
.size())
842 size_t num_servers
= session_
->config().nameservers
.size();
843 std::vector
<int> server_attempts(num_servers
);
844 for (size_t i
= 0; i
< first_completed
; ++i
) {
845 unsigned server_index
= attempts_
[i
]->server_index();
846 int server_attempt
= server_attempts
[server_index
]++;
847 // Don't record lost packet unless attempt is in pending state.
848 if (!attempts_
[i
]->is_pending())
850 session_
->RecordLostPacket(server_index
, server_attempt
);
854 void LogResponse(const DnsAttempt
* attempt
) {
855 if (attempt
&& attempt
->GetResponse()) {
857 NetLog::TYPE_DNS_TRANSACTION_RESPONSE
,
858 base::Bind(&DnsAttempt::NetLogResponseCallback
,
859 base::Unretained(attempt
)));
863 bool MoreAttemptsAllowed() const {
864 if (had_tcp_attempt_
)
866 const DnsConfig
& config
= session_
->config();
867 return attempts_
.size() < config
.attempts
* config
.nameservers
.size();
870 // Resolves the result of a DnsAttempt until a terminal result is reached
871 // or it will complete asynchronously (ERR_IO_PENDING).
872 AttemptResult
ProcessAttemptResult(AttemptResult result
) {
873 while (result
.rv
!= ERR_IO_PENDING
) {
874 LogResponse(result
.attempt
);
878 session_
->RecordServerSuccess(result
.attempt
->server_index());
879 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY
,
881 DCHECK(result
.attempt
);
882 DCHECK(result
.attempt
->GetResponse());
884 case ERR_NAME_NOT_RESOLVED
:
885 session_
->RecordServerSuccess(result
.attempt
->server_index());
886 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY
,
890 if (qnames_
.empty()) {
891 return AttemptResult(ERR_NAME_NOT_RESOLVED
, NULL
);
893 result
= StartQuery();
896 case ERR_CONNECTION_REFUSED
:
897 case ERR_DNS_TIMED_OUT
:
899 session_
->RecordServerFailure(result
.attempt
->server_index());
900 if (MoreAttemptsAllowed()) {
901 result
= MakeAttempt();
906 case ERR_DNS_SERVER_REQUIRES_TCP
:
907 result
= MakeTCPAttempt(result
.attempt
);
911 DCHECK(result
.attempt
);
912 if (result
.attempt
!= attempts_
.back()) {
913 // This attempt already timed out. Ignore it.
914 session_
->RecordServerFailure(result
.attempt
->server_index());
915 return AttemptResult(ERR_IO_PENDING
, NULL
);
917 if (MoreAttemptsAllowed()) {
918 result
= MakeAttempt();
919 } else if (result
.rv
== ERR_DNS_MALFORMED_RESPONSE
&&
921 // For UDP only, ignore the response and wait until the last attempt
923 return AttemptResult(ERR_IO_PENDING
, NULL
);
925 return AttemptResult(result
.rv
, NULL
);
934 if (callback_
.is_null())
936 DCHECK(!attempts_
.empty());
937 AttemptResult result
= ProcessAttemptResult(
938 AttemptResult(ERR_DNS_TIMED_OUT
, attempts_
.back()));
939 if (result
.rv
!= ERR_IO_PENDING
)
943 scoped_refptr
<DnsSession
> session_
;
944 std::string hostname_
;
946 // Cleared in DoCallback.
947 DnsTransactionFactory::CallbackType callback_
;
949 BoundNetLog net_log_
;
951 // Search list of fully-qualified DNS names to query next (in DNS format).
952 std::deque
<std::string
> qnames_
;
953 size_t qnames_initial_size_
;
955 // List of attempts for the current name.
956 ScopedVector
<DnsAttempt
> attempts_
;
957 // Count of attempts, not reset when |attempts_| vector is cleared.
959 bool had_tcp_attempt_
;
961 // Index of the first server to try on each search query.
962 int first_server_index_
;
964 base::OneShotTimer
<DnsTransactionImpl
> timer_
;
966 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl
);
969 // ----------------------------------------------------------------------------
971 // Implementation of DnsTransactionFactory that returns instances of
972 // DnsTransactionImpl.
973 class DnsTransactionFactoryImpl
: public DnsTransactionFactory
{
975 explicit DnsTransactionFactoryImpl(DnsSession
* session
) {
979 scoped_ptr
<DnsTransaction
> CreateTransaction(
980 const std::string
& hostname
,
982 const CallbackType
& callback
,
983 const BoundNetLog
& net_log
) override
{
984 return scoped_ptr
<DnsTransaction
>(new DnsTransactionImpl(
985 session_
.get(), hostname
, qtype
, callback
, net_log
));
989 scoped_refptr
<DnsSession
> session_
;
995 scoped_ptr
<DnsTransactionFactory
> DnsTransactionFactory::CreateFactory(
996 DnsSession
* session
) {
997 return scoped_ptr
<DnsTransactionFactory
>(
998 new DnsTransactionFactoryImpl(session
));