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/memory/ref_counted.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/histogram.h"
19 #include "base/profiler/scoped_tracker.h"
20 #include "base/rand_util.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string_piece.h"
23 #include "base/threading/non_thread_safe.h"
24 #include "base/timer/timer.h"
25 #include "base/values.h"
26 #include "net/base/completion_callback.h"
27 #include "net/base/dns_util.h"
28 #include "net/base/io_buffer.h"
29 #include "net/base/ip_endpoint.h"
30 #include "net/base/net_errors.h"
31 #include "net/dns/dns_protocol.h"
32 #include "net/dns/dns_query.h"
33 #include "net/dns/dns_response.h"
34 #include "net/dns/dns_session.h"
35 #include "net/log/net_log.h"
36 #include "net/socket/stream_socket.h"
37 #include "net/udp/datagram_client_socket.h"
43 // Provide a common macro to simplify code and readability. We must use a
44 // macro as the underlying HISTOGRAM macro creates static variables.
45 #define DNS_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES(name, time, \
46 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1), 100)
48 // Count labels in the fully-qualified name in DNS format.
49 int CountLabels(const std::string
& name
) {
51 for (size_t i
= 0; i
< name
.size() && name
[i
]; i
+= name
[i
] + 1)
56 bool IsIPLiteral(const std::string
& hostname
) {
58 return ParseIPLiteralToNumber(hostname
, &ip
);
61 base::Value
* NetLogStartCallback(const std::string
* hostname
,
63 NetLog::LogLevel
/* log_level */) {
64 base::DictionaryValue
* dict
= new base::DictionaryValue();
65 dict
->SetString("hostname", *hostname
);
66 dict
->SetInteger("query_type", qtype
);
70 // ----------------------------------------------------------------------------
72 // A single asynchronous DNS exchange, which consists of sending out a
73 // DNS query, waiting for a response, and returning the response that it
74 // matches. Logging is done in the socket and in the outer DnsTransaction.
77 explicit DnsAttempt(unsigned server_index
)
78 : result_(ERR_FAILED
), server_index_(server_index
) {}
80 virtual ~DnsAttempt() {}
81 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously
82 // and calls |callback| upon completion.
83 virtual int Start(const CompletionCallback
& callback
) = 0;
85 // Returns the query of this attempt.
86 virtual const DnsQuery
* GetQuery() const = 0;
88 // Returns the response or NULL if has not received a matching response from
90 virtual const DnsResponse
* GetResponse() const = 0;
92 // Returns the net log bound to the source of the socket.
93 virtual const BoundNetLog
& GetSocketNetLog() const = 0;
95 // Returns the index of the destination server within DnsConfig::nameservers.
96 unsigned server_index() const { return server_index_
; }
98 // Returns a Value representing the received response, along with a reference
99 // to the NetLog source source of the UDP socket used. The request must have
100 // completed before this is called.
101 base::Value
* NetLogResponseCallback(NetLog::LogLevel log_level
) const {
102 DCHECK(GetResponse()->IsValid());
104 base::DictionaryValue
* dict
= new base::DictionaryValue();
105 dict
->SetInteger("rcode", GetResponse()->rcode());
106 dict
->SetInteger("answer_count", GetResponse()->answer_count());
107 GetSocketNetLog().source().AddToEventParameters(dict
);
111 void set_result(int result
) {
115 // True if current attempt is pending (waiting for server response).
116 bool is_pending() const {
117 return result_
== ERR_IO_PENDING
;
120 // True if attempt is completed (received server response).
121 bool is_completed() const {
122 return (result_
== OK
) || (result_
== ERR_NAME_NOT_RESOLVED
) ||
123 (result_
== ERR_DNS_SERVER_REQUIRES_TCP
);
127 // Result of last operation.
130 const unsigned server_index_
;
133 class DnsUDPAttempt
: public DnsAttempt
{
135 DnsUDPAttempt(unsigned server_index
,
136 scoped_ptr
<DnsSession::SocketLease
> socket_lease
,
137 scoped_ptr
<DnsQuery
> query
)
138 : DnsAttempt(server_index
),
139 next_state_(STATE_NONE
),
140 received_malformed_response_(false),
141 socket_lease_(socket_lease
.Pass()),
142 query_(query
.Pass()) {}
145 int Start(const CompletionCallback
& callback
) override
{
146 DCHECK_EQ(STATE_NONE
, next_state_
);
147 callback_
= callback
;
148 start_time_
= base::TimeTicks::Now();
149 next_state_
= STATE_SEND_QUERY
;
153 const DnsQuery
* GetQuery() const override
{ return query_
.get(); }
155 const DnsResponse
* GetResponse() const override
{
156 const DnsResponse
* resp
= response_
.get();
157 return (resp
!= NULL
&& resp
->IsValid()) ? resp
: NULL
;
160 const BoundNetLog
& GetSocketNetLog() const override
{
161 return socket_lease_
->socket()->NetLog();
167 STATE_SEND_QUERY_COMPLETE
,
169 STATE_READ_RESPONSE_COMPLETE
,
173 DatagramClientSocket
* socket() {
174 return socket_lease_
->socket();
177 int DoLoop(int result
) {
178 CHECK_NE(STATE_NONE
, next_state_
);
181 State state
= next_state_
;
182 next_state_
= STATE_NONE
;
184 case STATE_SEND_QUERY
:
187 case STATE_SEND_QUERY_COMPLETE
:
188 rv
= DoSendQueryComplete(rv
);
190 case STATE_READ_RESPONSE
:
191 rv
= DoReadResponse();
193 case STATE_READ_RESPONSE_COMPLETE
:
194 rv
= DoReadResponseComplete(rv
);
200 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
203 // If we received a malformed response, and are now waiting for another one,
204 // indicate to the transaction that the server might be misbehaving.
205 if (rv
== ERR_IO_PENDING
&& received_malformed_response_
)
206 return ERR_DNS_MALFORMED_RESPONSE
;
208 DCHECK_EQ(STATE_NONE
, next_state_
);
209 DNS_HISTOGRAM("AsyncDNS.UDPAttemptSuccess",
210 base::TimeTicks::Now() - start_time_
);
211 } else if (rv
!= ERR_IO_PENDING
) {
212 DNS_HISTOGRAM("AsyncDNS.UDPAttemptFail",
213 base::TimeTicks::Now() - start_time_
);
219 next_state_
= STATE_SEND_QUERY_COMPLETE
;
220 return socket()->Write(query_
->io_buffer(),
221 query_
->io_buffer()->size(),
222 base::Bind(&DnsUDPAttempt::OnIOComplete
,
223 base::Unretained(this)));
226 int DoSendQueryComplete(int rv
) {
227 DCHECK_NE(ERR_IO_PENDING
, rv
);
231 // Writing to UDP should not result in a partial datagram.
232 if (rv
!= query_
->io_buffer()->size())
233 return ERR_MSG_TOO_BIG
;
235 next_state_
= STATE_READ_RESPONSE
;
239 int DoReadResponse() {
240 next_state_
= STATE_READ_RESPONSE_COMPLETE
;
241 response_
.reset(new DnsResponse());
242 return socket()->Read(response_
->io_buffer(),
243 response_
->io_buffer()->size(),
244 base::Bind(&DnsUDPAttempt::OnIOComplete
,
245 base::Unretained(this)));
248 int DoReadResponseComplete(int rv
) {
249 DCHECK_NE(ERR_IO_PENDING
, rv
);
254 if (!response_
->InitParse(rv
, *query_
)) {
255 // Other implementations simply ignore mismatched responses. Since each
256 // DnsUDPAttempt binds to a different port, we might find that responses
257 // to previously timed out queries lead to failures in the future.
258 // Our solution is to make another attempt, in case the query truly
259 // failed, but keep this attempt alive, in case it was a false alarm.
260 received_malformed_response_
= true;
261 next_state_
= STATE_READ_RESPONSE
;
264 if (response_
->flags() & dns_protocol::kFlagTC
)
265 return ERR_DNS_SERVER_REQUIRES_TCP
;
266 // TODO(szym): Extract TTL for NXDOMAIN results. http://crbug.com/115051
267 if (response_
->rcode() == dns_protocol::kRcodeNXDOMAIN
)
268 return ERR_NAME_NOT_RESOLVED
;
269 if (response_
->rcode() != dns_protocol::kRcodeNOERROR
)
270 return ERR_DNS_SERVER_FAILED
;
275 void OnIOComplete(int rv
) {
277 if (rv
!= ERR_IO_PENDING
)
282 bool received_malformed_response_
;
283 base::TimeTicks start_time_
;
285 scoped_ptr
<DnsSession::SocketLease
> socket_lease_
;
286 scoped_ptr
<DnsQuery
> query_
;
288 scoped_ptr
<DnsResponse
> response_
;
290 CompletionCallback callback_
;
292 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt
);
295 class DnsTCPAttempt
: public DnsAttempt
{
297 DnsTCPAttempt(unsigned server_index
,
298 scoped_ptr
<StreamSocket
> socket
,
299 scoped_ptr
<DnsQuery
> query
)
300 : DnsAttempt(server_index
),
301 next_state_(STATE_NONE
),
302 socket_(socket
.Pass()),
303 query_(query
.Pass()),
304 length_buffer_(new IOBufferWithSize(sizeof(uint16
))),
305 response_length_(0) {}
308 int Start(const CompletionCallback
& callback
) override
{
309 DCHECK_EQ(STATE_NONE
, next_state_
);
310 callback_
= callback
;
311 start_time_
= base::TimeTicks::Now();
312 next_state_
= STATE_CONNECT_COMPLETE
;
313 int rv
= socket_
->Connect(base::Bind(&DnsTCPAttempt::OnIOComplete
,
314 base::Unretained(this)));
315 if (rv
== ERR_IO_PENDING
) {
322 const DnsQuery
* GetQuery() const override
{ return query_
.get(); }
324 const DnsResponse
* GetResponse() const override
{
325 const DnsResponse
* resp
= response_
.get();
326 return (resp
!= NULL
&& resp
->IsValid()) ? resp
: NULL
;
329 const BoundNetLog
& GetSocketNetLog() const override
{
330 return socket_
->NetLog();
335 STATE_CONNECT_COMPLETE
,
339 STATE_READ_LENGTH_COMPLETE
,
341 STATE_READ_RESPONSE_COMPLETE
,
345 int DoLoop(int result
) {
346 CHECK_NE(STATE_NONE
, next_state_
);
349 State state
= next_state_
;
350 next_state_
= STATE_NONE
;
352 case STATE_CONNECT_COMPLETE
:
353 rv
= DoConnectComplete(rv
);
355 case STATE_SEND_LENGTH
:
356 rv
= DoSendLength(rv
);
358 case STATE_SEND_QUERY
:
359 rv
= DoSendQuery(rv
);
361 case STATE_READ_LENGTH
:
362 rv
= DoReadLength(rv
);
364 case STATE_READ_LENGTH_COMPLETE
:
365 rv
= DoReadLengthComplete(rv
);
367 case STATE_READ_RESPONSE
:
368 rv
= DoReadResponse(rv
);
370 case STATE_READ_RESPONSE_COMPLETE
:
371 rv
= DoReadResponseComplete(rv
);
377 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
381 DCHECK_EQ(STATE_NONE
, next_state_
);
382 DNS_HISTOGRAM("AsyncDNS.TCPAttemptSuccess",
383 base::TimeTicks::Now() - start_time_
);
384 } else if (rv
!= ERR_IO_PENDING
) {
385 DNS_HISTOGRAM("AsyncDNS.TCPAttemptFail",
386 base::TimeTicks::Now() - start_time_
);
391 int DoConnectComplete(int rv
) {
392 // TODO(rvargas): Remove ScopedTracker below once crbug.com/462784 is fixed.
393 tracked_objects::ScopedTracker
tracking_profile(
394 FROM_HERE_WITH_EXPLICIT_FUNCTION(
395 "462784 DnsTCPAttempt::DoConnectComplete"));
397 DCHECK_NE(ERR_IO_PENDING
, rv
);
401 uint16 query_size
= static_cast<uint16
>(query_
->io_buffer()->size());
402 if (static_cast<int>(query_size
) != query_
->io_buffer()->size())
404 base::WriteBigEndian
<uint16
>(length_buffer_
->data(), query_size
);
406 new DrainableIOBuffer(length_buffer_
.get(), length_buffer_
->size());
407 next_state_
= STATE_SEND_LENGTH
;
411 int DoSendLength(int rv
) {
412 DCHECK_NE(ERR_IO_PENDING
, rv
);
416 buffer_
->DidConsume(rv
);
417 if (buffer_
->BytesRemaining() > 0) {
418 next_state_
= STATE_SEND_LENGTH
;
419 return socket_
->Write(
421 buffer_
->BytesRemaining(),
422 base::Bind(&DnsTCPAttempt::OnIOComplete
, base::Unretained(this)));
424 buffer_
= new DrainableIOBuffer(query_
->io_buffer(),
425 query_
->io_buffer()->size());
426 next_state_
= STATE_SEND_QUERY
;
430 int DoSendQuery(int rv
) {
431 DCHECK_NE(ERR_IO_PENDING
, rv
);
435 buffer_
->DidConsume(rv
);
436 if (buffer_
->BytesRemaining() > 0) {
437 next_state_
= STATE_SEND_QUERY
;
438 return socket_
->Write(
440 buffer_
->BytesRemaining(),
441 base::Bind(&DnsTCPAttempt::OnIOComplete
, base::Unretained(this)));
444 new DrainableIOBuffer(length_buffer_
.get(), length_buffer_
->size());
445 next_state_
= STATE_READ_LENGTH
;
449 int DoReadLength(int rv
) {
452 next_state_
= STATE_READ_LENGTH_COMPLETE
;
453 return ReadIntoBuffer();
456 int DoReadLengthComplete(int rv
) {
457 DCHECK_NE(ERR_IO_PENDING
, rv
);
461 return ERR_CONNECTION_CLOSED
;
463 buffer_
->DidConsume(rv
);
464 if (buffer_
->BytesRemaining() > 0) {
465 next_state_
= STATE_READ_LENGTH
;
469 base::ReadBigEndian
<uint16
>(length_buffer_
->data(), &response_length_
);
470 // Check if advertised response is too short. (Optimization only.)
471 if (response_length_
< query_
->io_buffer()->size())
472 return ERR_DNS_MALFORMED_RESPONSE
;
473 // Allocate more space so that DnsResponse::InitParse sanity check passes.
474 response_
.reset(new DnsResponse(response_length_
+ 1));
475 buffer_
= new DrainableIOBuffer(response_
->io_buffer(), response_length_
);
476 next_state_
= STATE_READ_RESPONSE
;
480 int DoReadResponse(int rv
) {
483 next_state_
= STATE_READ_RESPONSE_COMPLETE
;
484 return ReadIntoBuffer();
487 int DoReadResponseComplete(int rv
) {
488 DCHECK_NE(ERR_IO_PENDING
, rv
);
492 return ERR_CONNECTION_CLOSED
;
494 buffer_
->DidConsume(rv
);
495 if (buffer_
->BytesRemaining() > 0) {
496 next_state_
= STATE_READ_RESPONSE
;
500 if (!response_
->InitParse(buffer_
->BytesConsumed(), *query_
))
501 return ERR_DNS_MALFORMED_RESPONSE
;
502 if (response_
->flags() & dns_protocol::kFlagTC
)
503 return ERR_UNEXPECTED
;
504 // TODO(szym): Frankly, none of these are expected.
505 if (response_
->rcode() == dns_protocol::kRcodeNXDOMAIN
)
506 return ERR_NAME_NOT_RESOLVED
;
507 if (response_
->rcode() != dns_protocol::kRcodeNOERROR
)
508 return ERR_DNS_SERVER_FAILED
;
513 void OnIOComplete(int rv
) {
515 if (rv
!= ERR_IO_PENDING
)
519 int ReadIntoBuffer() {
520 return socket_
->Read(
522 buffer_
->BytesRemaining(),
523 base::Bind(&DnsTCPAttempt::OnIOComplete
, base::Unretained(this)));
527 base::TimeTicks start_time_
;
529 scoped_ptr
<StreamSocket
> socket_
;
530 scoped_ptr
<DnsQuery
> query_
;
531 scoped_refptr
<IOBufferWithSize
> length_buffer_
;
532 scoped_refptr
<DrainableIOBuffer
> buffer_
;
534 uint16 response_length_
;
535 scoped_ptr
<DnsResponse
> response_
;
537 CompletionCallback callback_
;
539 DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt
);
542 // ----------------------------------------------------------------------------
544 // Implements DnsTransaction. Configuration is supplied by DnsSession.
545 // The suffix list is built according to the DnsConfig from the session.
546 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout.
547 // The first server to attempt on each query is given by
548 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards.
549 // Each server is attempted DnsConfig::attempts times.
550 class DnsTransactionImpl
: public DnsTransaction
,
551 public base::NonThreadSafe
,
552 public base::SupportsWeakPtr
<DnsTransactionImpl
> {
554 DnsTransactionImpl(DnsSession
* session
,
555 const std::string
& hostname
,
557 const DnsTransactionFactory::CallbackType
& callback
,
558 const BoundNetLog
& net_log
)
564 qnames_initial_size_(0),
566 had_tcp_attempt_(false),
567 first_server_index_(0) {
568 DCHECK(session_
.get());
569 DCHECK(!hostname_
.empty());
570 DCHECK(!callback_
.is_null());
571 DCHECK(!IsIPLiteral(hostname_
));
574 ~DnsTransactionImpl() override
{
575 if (!callback_
.is_null()) {
576 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION
,
578 } // otherwise logged in DoCallback or Start
581 const std::string
& GetHostname() const override
{
582 DCHECK(CalledOnValidThread());
586 uint16
GetType() const override
{
587 DCHECK(CalledOnValidThread());
591 void Start() override
{
592 DCHECK(!callback_
.is_null());
593 DCHECK(attempts_
.empty());
594 net_log_
.BeginEvent(NetLog::TYPE_DNS_TRANSACTION
,
595 base::Bind(&NetLogStartCallback
, &hostname_
, qtype_
));
596 AttemptResult
result(PrepareSearch(), NULL
);
597 if (result
.rv
== OK
) {
598 qnames_initial_size_
= qnames_
.size();
599 if (qtype_
== dns_protocol::kTypeA
)
600 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchStart", qnames_
.size());
601 result
= ProcessAttemptResult(StartQuery());
604 // Must always return result asynchronously, to avoid reentrancy.
605 if (result
.rv
!= ERR_IO_PENDING
) {
606 base::MessageLoop::current()->PostTask(
608 base::Bind(&DnsTransactionImpl::DoCallback
, AsWeakPtr(), result
));
613 // Wrapper for the result of a DnsUDPAttempt.
614 struct AttemptResult
{
615 AttemptResult(int rv
, const DnsAttempt
* attempt
)
616 : rv(rv
), attempt(attempt
) {}
619 const DnsAttempt
* attempt
;
622 // Prepares |qnames_| according to the DnsConfig.
623 int PrepareSearch() {
624 const DnsConfig
& config
= session_
->config();
626 std::string labeled_hostname
;
627 if (!DNSDomainFromDot(hostname_
, &labeled_hostname
))
628 return ERR_INVALID_ARGUMENT
;
630 if (hostname_
[hostname_
.size() - 1] == '.') {
631 // It's a fully-qualified name, no suffix search.
632 qnames_
.push_back(labeled_hostname
);
636 int ndots
= CountLabels(labeled_hostname
) - 1;
638 if (ndots
> 0 && !config
.append_to_multi_label_name
) {
639 qnames_
.push_back(labeled_hostname
);
643 // Set true when |labeled_hostname| is put on the list.
644 bool had_hostname
= false;
646 if (ndots
>= config
.ndots
) {
647 qnames_
.push_back(labeled_hostname
);
652 for (size_t i
= 0; i
< config
.search
.size(); ++i
) {
653 // Ignore invalid (too long) combinations.
654 if (!DNSDomainFromDot(hostname_
+ "." + config
.search
[i
], &qname
))
656 if (qname
.size() == labeled_hostname
.size()) {
661 qnames_
.push_back(qname
);
664 if (ndots
> 0 && !had_hostname
)
665 qnames_
.push_back(labeled_hostname
);
667 return qnames_
.empty() ? ERR_DNS_SEARCH_EMPTY
: OK
;
670 void DoCallback(AttemptResult result
) {
671 DCHECK(!callback_
.is_null());
672 DCHECK_NE(ERR_IO_PENDING
, result
.rv
);
673 const DnsResponse
* response
= result
.attempt
?
674 result
.attempt
->GetResponse() : NULL
;
675 CHECK(result
.rv
!= OK
|| response
!= NULL
);
678 RecordLostPacketsIfAny();
680 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountSuccess", attempts_count_
);
682 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountFail", attempts_count_
);
684 if (response
&& qtype_
== dns_protocol::kTypeA
) {
685 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchRemain", qnames_
.size());
686 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchDone",
687 qnames_initial_size_
- qnames_
.size());
690 DnsTransactionFactory::CallbackType callback
= callback_
;
693 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION
, result
.rv
);
694 callback
.Run(this, result
.rv
, response
);
697 // Makes another attempt at the current name, |qnames_.front()|, using the
699 AttemptResult
MakeAttempt() {
700 unsigned attempt_number
= attempts_
.size();
702 uint16 id
= session_
->NextQueryId();
703 scoped_ptr
<DnsQuery
> query
;
704 if (attempts_
.empty()) {
705 query
.reset(new DnsQuery(id
, qnames_
.front(), qtype_
));
707 query
.reset(attempts_
[0]->GetQuery()->CloneWithNewId(id
));
710 const DnsConfig
& config
= session_
->config();
712 unsigned server_index
=
713 (first_server_index_
+ attempt_number
) % config
.nameservers
.size();
714 // Skip over known failed servers.
715 server_index
= session_
->NextGoodServerIndex(server_index
);
717 scoped_ptr
<DnsSession::SocketLease
> lease
=
718 session_
->AllocateSocket(server_index
, net_log_
.source());
720 bool got_socket
= !!lease
.get();
722 DnsUDPAttempt
* attempt
=
723 new DnsUDPAttempt(server_index
, lease
.Pass(), query
.Pass());
725 attempts_
.push_back(attempt
);
729 return AttemptResult(ERR_CONNECTION_REFUSED
, NULL
);
732 NetLog::TYPE_DNS_TRANSACTION_ATTEMPT
,
733 attempt
->GetSocketNetLog().source().ToEventParametersCallback());
735 int rv
= attempt
->Start(
736 base::Bind(&DnsTransactionImpl::OnUdpAttemptComplete
,
737 base::Unretained(this), attempt_number
,
738 base::TimeTicks::Now()));
739 if (rv
== ERR_IO_PENDING
) {
740 base::TimeDelta timeout
= session_
->NextTimeout(server_index
,
742 timer_
.Start(FROM_HERE
, timeout
, this, &DnsTransactionImpl::OnTimeout
);
744 return AttemptResult(rv
, attempt
);
747 AttemptResult
MakeTCPAttempt(const DnsAttempt
* previous_attempt
) {
748 DCHECK(previous_attempt
);
749 DCHECK(!had_tcp_attempt_
);
751 unsigned server_index
= previous_attempt
->server_index();
753 scoped_ptr
<StreamSocket
> socket(
754 session_
->CreateTCPSocket(server_index
, net_log_
.source()));
756 // TODO(szym): Reuse the same id to help the server?
757 uint16 id
= session_
->NextQueryId();
758 scoped_ptr
<DnsQuery
> query(
759 previous_attempt
->GetQuery()->CloneWithNewId(id
));
761 RecordLostPacketsIfAny();
762 // Cancel all other attempts, no point waiting on them.
765 unsigned attempt_number
= attempts_
.size();
767 DnsTCPAttempt
* attempt
= new DnsTCPAttempt(server_index
, socket
.Pass(),
770 attempts_
.push_back(attempt
);
772 had_tcp_attempt_
= true;
775 NetLog::TYPE_DNS_TRANSACTION_TCP_ATTEMPT
,
776 attempt
->GetSocketNetLog().source().ToEventParametersCallback());
778 int rv
= attempt
->Start(base::Bind(&DnsTransactionImpl::OnAttemptComplete
,
779 base::Unretained(this),
781 if (rv
== ERR_IO_PENDING
) {
782 // Custom timeout for TCP attempt.
783 base::TimeDelta timeout
= timer_
.GetCurrentDelay() * 2;
784 timer_
.Start(FROM_HERE
, timeout
, this, &DnsTransactionImpl::OnTimeout
);
786 return AttemptResult(rv
, attempt
);
789 // Begins query for the current name. Makes the first attempt.
790 AttemptResult
StartQuery() {
791 std::string dotted_qname
= DNSDomainToString(qnames_
.front());
792 net_log_
.BeginEvent(NetLog::TYPE_DNS_TRANSACTION_QUERY
,
793 NetLog::StringCallback("qname", &dotted_qname
));
795 first_server_index_
= session_
->NextFirstServerIndex();
796 RecordLostPacketsIfAny();
798 had_tcp_attempt_
= false;
799 return MakeAttempt();
802 void OnUdpAttemptComplete(unsigned attempt_number
,
803 base::TimeTicks start
,
805 DCHECK_LT(attempt_number
, attempts_
.size());
806 const DnsAttempt
* attempt
= attempts_
[attempt_number
];
807 if (attempt
->GetResponse()) {
808 session_
->RecordRTT(attempt
->server_index(),
809 base::TimeTicks::Now() - start
);
811 OnAttemptComplete(attempt_number
, rv
);
814 void OnAttemptComplete(unsigned attempt_number
, int rv
) {
815 if (callback_
.is_null())
817 DCHECK_LT(attempt_number
, attempts_
.size());
818 const DnsAttempt
* attempt
= attempts_
[attempt_number
];
819 AttemptResult result
= ProcessAttemptResult(AttemptResult(rv
, attempt
));
820 if (result
.rv
!= ERR_IO_PENDING
)
824 // Record packet loss for any incomplete attempts.
825 void RecordLostPacketsIfAny() {
826 // Loop through attempts until we find first that is completed
827 size_t first_completed
= 0;
828 for (first_completed
= 0; first_completed
< attempts_
.size();
830 if (attempts_
[first_completed
]->is_completed())
833 // If there were no completed attempts, then we must be offline, so don't
834 // record any attempts as lost packets.
835 if (first_completed
== attempts_
.size())
838 size_t num_servers
= session_
->config().nameservers
.size();
839 std::vector
<int> server_attempts(num_servers
);
840 for (size_t i
= 0; i
< first_completed
; ++i
) {
841 unsigned server_index
= attempts_
[i
]->server_index();
842 int server_attempt
= server_attempts
[server_index
]++;
843 // Don't record lost packet unless attempt is in pending state.
844 if (!attempts_
[i
]->is_pending())
846 session_
->RecordLostPacket(server_index
, server_attempt
);
850 void LogResponse(const DnsAttempt
* attempt
) {
851 if (attempt
&& attempt
->GetResponse()) {
853 NetLog::TYPE_DNS_TRANSACTION_RESPONSE
,
854 base::Bind(&DnsAttempt::NetLogResponseCallback
,
855 base::Unretained(attempt
)));
859 bool MoreAttemptsAllowed() const {
860 if (had_tcp_attempt_
)
862 const DnsConfig
& config
= session_
->config();
863 return attempts_
.size() < config
.attempts
* config
.nameservers
.size();
866 // Resolves the result of a DnsAttempt until a terminal result is reached
867 // or it will complete asynchronously (ERR_IO_PENDING).
868 AttemptResult
ProcessAttemptResult(AttemptResult result
) {
869 while (result
.rv
!= ERR_IO_PENDING
) {
870 LogResponse(result
.attempt
);
874 session_
->RecordServerSuccess(result
.attempt
->server_index());
875 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY
,
877 DCHECK(result
.attempt
);
878 DCHECK(result
.attempt
->GetResponse());
880 case ERR_NAME_NOT_RESOLVED
:
881 session_
->RecordServerSuccess(result
.attempt
->server_index());
882 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY
,
886 if (qnames_
.empty()) {
887 return AttemptResult(ERR_NAME_NOT_RESOLVED
, NULL
);
889 result
= StartQuery();
892 case ERR_CONNECTION_REFUSED
:
893 case ERR_DNS_TIMED_OUT
:
895 session_
->RecordServerFailure(result
.attempt
->server_index());
896 if (MoreAttemptsAllowed()) {
897 result
= MakeAttempt();
902 case ERR_DNS_SERVER_REQUIRES_TCP
:
903 result
= MakeTCPAttempt(result
.attempt
);
907 DCHECK(result
.attempt
);
908 if (result
.attempt
!= attempts_
.back()) {
909 // This attempt already timed out. Ignore it.
910 session_
->RecordServerFailure(result
.attempt
->server_index());
911 return AttemptResult(ERR_IO_PENDING
, NULL
);
913 if (MoreAttemptsAllowed()) {
914 result
= MakeAttempt();
915 } else if (result
.rv
== ERR_DNS_MALFORMED_RESPONSE
&&
917 // For UDP only, ignore the response and wait until the last attempt
919 return AttemptResult(ERR_IO_PENDING
, NULL
);
921 return AttemptResult(result
.rv
, NULL
);
930 if (callback_
.is_null())
932 DCHECK(!attempts_
.empty());
933 AttemptResult result
= ProcessAttemptResult(
934 AttemptResult(ERR_DNS_TIMED_OUT
, attempts_
.back()));
935 if (result
.rv
!= ERR_IO_PENDING
)
939 scoped_refptr
<DnsSession
> session_
;
940 std::string hostname_
;
942 // Cleared in DoCallback.
943 DnsTransactionFactory::CallbackType callback_
;
945 BoundNetLog net_log_
;
947 // Search list of fully-qualified DNS names to query next (in DNS format).
948 std::deque
<std::string
> qnames_
;
949 size_t qnames_initial_size_
;
951 // List of attempts for the current name.
952 ScopedVector
<DnsAttempt
> attempts_
;
953 // Count of attempts, not reset when |attempts_| vector is cleared.
955 bool had_tcp_attempt_
;
957 // Index of the first server to try on each search query.
958 int first_server_index_
;
960 base::OneShotTimer
<DnsTransactionImpl
> timer_
;
962 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl
);
965 // ----------------------------------------------------------------------------
967 // Implementation of DnsTransactionFactory that returns instances of
968 // DnsTransactionImpl.
969 class DnsTransactionFactoryImpl
: public DnsTransactionFactory
{
971 explicit DnsTransactionFactoryImpl(DnsSession
* session
) {
975 scoped_ptr
<DnsTransaction
> CreateTransaction(
976 const std::string
& hostname
,
978 const CallbackType
& callback
,
979 const BoundNetLog
& net_log
) override
{
980 return scoped_ptr
<DnsTransaction
>(new DnsTransactionImpl(
981 session_
.get(), hostname
, qtype
, callback
, net_log
));
985 scoped_refptr
<DnsSession
> session_
;
991 scoped_ptr
<DnsTransactionFactory
> DnsTransactionFactory::CreateFactory(
992 DnsSession
* session
) {
993 return scoped_ptr
<DnsTransactionFactory
>(
994 new DnsTransactionFactoryImpl(session
));