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/rand_util.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_piece.h"
22 #include "base/threading/non_thread_safe.h"
23 #include "base/timer/timer.h"
24 #include "base/values.h"
25 #include "net/base/completion_callback.h"
26 #include "net/base/dns_util.h"
27 #include "net/base/io_buffer.h"
28 #include "net/base/ip_endpoint.h"
29 #include "net/base/net_errors.h"
30 #include "net/base/net_log.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/socket/stream_socket.h"
36 #include "net/udp/datagram_client_socket.h"
42 // Provide a common macro to simplify code and readability. We must use a
43 // macro as the underlying HISTOGRAM macro creates static variables.
44 #define DNS_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES(name, time, \
45 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1), 100)
47 // Count labels in the fully-qualified name in DNS format.
48 int CountLabels(const std::string
& name
) {
50 for (size_t i
= 0; i
< name
.size() && name
[i
]; i
+= name
[i
] + 1)
55 bool IsIPLiteral(const std::string
& hostname
) {
57 return ParseIPLiteralToNumber(hostname
, &ip
);
60 base::Value
* NetLogStartCallback(const std::string
* hostname
,
62 NetLog::LogLevel
/* log_level */) {
63 base::DictionaryValue
* dict
= new base::DictionaryValue();
64 dict
->SetString("hostname", *hostname
);
65 dict
->SetInteger("query_type", qtype
);
69 // ----------------------------------------------------------------------------
71 // A single asynchronous DNS exchange, which consists of sending out a
72 // DNS query, waiting for a response, and returning the response that it
73 // matches. Logging is done in the socket and in the outer DnsTransaction.
76 explicit DnsAttempt(unsigned server_index
)
77 : result_(ERR_FAILED
), server_index_(server_index
) {}
79 virtual ~DnsAttempt() {}
80 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously
81 // and calls |callback| upon completion.
82 virtual int Start(const CompletionCallback
& callback
) = 0;
84 // Returns the query of this attempt.
85 virtual const DnsQuery
* GetQuery() const = 0;
87 // Returns the response or NULL if has not received a matching response from
89 virtual const DnsResponse
* GetResponse() const = 0;
91 // Returns the net log bound to the source of the socket.
92 virtual const BoundNetLog
& GetSocketNetLog() const = 0;
94 // Returns the index of the destination server within DnsConfig::nameservers.
95 unsigned server_index() const { return server_index_
; }
97 // Returns a Value representing the received response, along with a reference
98 // to the NetLog source source of the UDP socket used. The request must have
99 // completed before this is called.
100 base::Value
* NetLogResponseCallback(NetLog::LogLevel log_level
) const {
101 DCHECK(GetResponse()->IsValid());
103 base::DictionaryValue
* dict
= new base::DictionaryValue();
104 dict
->SetInteger("rcode", GetResponse()->rcode());
105 dict
->SetInteger("answer_count", GetResponse()->answer_count());
106 GetSocketNetLog().source().AddToEventParameters(dict
);
110 void set_result(int result
) {
114 // True if current attempt is pending (waiting for server response).
115 bool is_pending() const {
116 return result_
== ERR_IO_PENDING
;
119 // True if attempt is completed (received server response).
120 bool is_completed() const {
121 return (result_
== OK
) || (result_
== ERR_NAME_NOT_RESOLVED
) ||
122 (result_
== ERR_DNS_SERVER_REQUIRES_TCP
);
126 // Result of last operation.
129 const unsigned server_index_
;
132 class DnsUDPAttempt
: public DnsAttempt
{
134 DnsUDPAttempt(unsigned server_index
,
135 scoped_ptr
<DnsSession::SocketLease
> socket_lease
,
136 scoped_ptr
<DnsQuery
> query
)
137 : DnsAttempt(server_index
),
138 next_state_(STATE_NONE
),
139 received_malformed_response_(false),
140 socket_lease_(socket_lease
.Pass()),
141 query_(query
.Pass()) {}
144 virtual int Start(const CompletionCallback
& callback
) OVERRIDE
{
145 DCHECK_EQ(STATE_NONE
, next_state_
);
146 callback_
= callback
;
147 start_time_
= base::TimeTicks::Now();
148 next_state_
= STATE_SEND_QUERY
;
152 virtual const DnsQuery
* GetQuery() const OVERRIDE
{
156 virtual const DnsResponse
* GetResponse() const OVERRIDE
{
157 const DnsResponse
* resp
= response_
.get();
158 return (resp
!= NULL
&& resp
->IsValid()) ? resp
: NULL
;
161 virtual const BoundNetLog
& GetSocketNetLog() const OVERRIDE
{
162 return socket_lease_
->socket()->NetLog();
168 STATE_SEND_QUERY_COMPLETE
,
170 STATE_READ_RESPONSE_COMPLETE
,
174 DatagramClientSocket
* socket() {
175 return socket_lease_
->socket();
178 int DoLoop(int result
) {
179 CHECK_NE(STATE_NONE
, next_state_
);
182 State state
= next_state_
;
183 next_state_
= STATE_NONE
;
185 case STATE_SEND_QUERY
:
188 case STATE_SEND_QUERY_COMPLETE
:
189 rv
= DoSendQueryComplete(rv
);
191 case STATE_READ_RESPONSE
:
192 rv
= DoReadResponse();
194 case STATE_READ_RESPONSE_COMPLETE
:
195 rv
= DoReadResponseComplete(rv
);
201 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
204 // If we received a malformed response, and are now waiting for another one,
205 // indicate to the transaction that the server might be misbehaving.
206 if (rv
== ERR_IO_PENDING
&& received_malformed_response_
)
207 return ERR_DNS_MALFORMED_RESPONSE
;
209 DCHECK_EQ(STATE_NONE
, next_state_
);
210 DNS_HISTOGRAM("AsyncDNS.UDPAttemptSuccess",
211 base::TimeTicks::Now() - start_time_
);
212 } else if (rv
!= ERR_IO_PENDING
) {
213 DNS_HISTOGRAM("AsyncDNS.UDPAttemptFail",
214 base::TimeTicks::Now() - start_time_
);
220 next_state_
= STATE_SEND_QUERY_COMPLETE
;
221 return socket()->Write(query_
->io_buffer(),
222 query_
->io_buffer()->size(),
223 base::Bind(&DnsUDPAttempt::OnIOComplete
,
224 base::Unretained(this)));
227 int DoSendQueryComplete(int rv
) {
228 DCHECK_NE(ERR_IO_PENDING
, rv
);
232 // Writing to UDP should not result in a partial datagram.
233 if (rv
!= query_
->io_buffer()->size())
234 return ERR_MSG_TOO_BIG
;
236 next_state_
= STATE_READ_RESPONSE
;
240 int DoReadResponse() {
241 next_state_
= STATE_READ_RESPONSE_COMPLETE
;
242 response_
.reset(new DnsResponse());
243 return socket()->Read(response_
->io_buffer(),
244 response_
->io_buffer()->size(),
245 base::Bind(&DnsUDPAttempt::OnIOComplete
,
246 base::Unretained(this)));
249 int DoReadResponseComplete(int rv
) {
250 DCHECK_NE(ERR_IO_PENDING
, rv
);
255 if (!response_
->InitParse(rv
, *query_
)) {
256 // Other implementations simply ignore mismatched responses. Since each
257 // DnsUDPAttempt binds to a different port, we might find that responses
258 // to previously timed out queries lead to failures in the future.
259 // Our solution is to make another attempt, in case the query truly
260 // failed, but keep this attempt alive, in case it was a false alarm.
261 received_malformed_response_
= true;
262 next_state_
= STATE_READ_RESPONSE
;
265 if (response_
->flags() & dns_protocol::kFlagTC
)
266 return ERR_DNS_SERVER_REQUIRES_TCP
;
267 // TODO(szym): Extract TTL for NXDOMAIN results. http://crbug.com/115051
268 if (response_
->rcode() == dns_protocol::kRcodeNXDOMAIN
)
269 return ERR_NAME_NOT_RESOLVED
;
270 if (response_
->rcode() != dns_protocol::kRcodeNOERROR
)
271 return ERR_DNS_SERVER_FAILED
;
276 void OnIOComplete(int rv
) {
278 if (rv
!= ERR_IO_PENDING
)
283 bool received_malformed_response_
;
284 base::TimeTicks start_time_
;
286 scoped_ptr
<DnsSession::SocketLease
> socket_lease_
;
287 scoped_ptr
<DnsQuery
> query_
;
289 scoped_ptr
<DnsResponse
> response_
;
291 CompletionCallback callback_
;
293 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt
);
296 class DnsTCPAttempt
: public DnsAttempt
{
298 DnsTCPAttempt(unsigned server_index
,
299 scoped_ptr
<StreamSocket
> socket
,
300 scoped_ptr
<DnsQuery
> query
)
301 : DnsAttempt(server_index
),
302 next_state_(STATE_NONE
),
303 socket_(socket
.Pass()),
304 query_(query
.Pass()),
305 length_buffer_(new IOBufferWithSize(sizeof(uint16
))),
306 response_length_(0) {}
309 virtual int Start(const CompletionCallback
& callback
) OVERRIDE
{
310 DCHECK_EQ(STATE_NONE
, next_state_
);
311 callback_
= callback
;
312 start_time_
= base::TimeTicks::Now();
313 next_state_
= STATE_CONNECT_COMPLETE
;
314 int rv
= socket_
->Connect(base::Bind(&DnsTCPAttempt::OnIOComplete
,
315 base::Unretained(this)));
316 if (rv
== ERR_IO_PENDING
) {
323 virtual const DnsQuery
* GetQuery() const OVERRIDE
{
327 virtual const DnsResponse
* GetResponse() const OVERRIDE
{
328 const DnsResponse
* resp
= response_
.get();
329 return (resp
!= NULL
&& resp
->IsValid()) ? resp
: NULL
;
332 virtual const BoundNetLog
& GetSocketNetLog() const OVERRIDE
{
333 return socket_
->NetLog();
338 STATE_CONNECT_COMPLETE
,
346 int DoLoop(int result
) {
347 CHECK_NE(STATE_NONE
, next_state_
);
350 State state
= next_state_
;
351 next_state_
= STATE_NONE
;
353 case STATE_CONNECT_COMPLETE
:
354 rv
= DoConnectComplete(rv
);
356 case STATE_SEND_LENGTH
:
357 rv
= DoSendLength(rv
);
359 case STATE_SEND_QUERY
:
360 rv
= DoSendQuery(rv
);
362 case STATE_READ_LENGTH
:
363 rv
= DoReadLength(rv
);
365 case STATE_READ_RESPONSE
:
366 rv
= DoReadResponse(rv
);
372 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
376 DCHECK_EQ(STATE_NONE
, next_state_
);
377 DNS_HISTOGRAM("AsyncDNS.TCPAttemptSuccess",
378 base::TimeTicks::Now() - start_time_
);
379 } else if (rv
!= ERR_IO_PENDING
) {
380 DNS_HISTOGRAM("AsyncDNS.TCPAttemptFail",
381 base::TimeTicks::Now() - start_time_
);
386 int DoConnectComplete(int rv
) {
387 DCHECK_NE(ERR_IO_PENDING
, rv
);
391 base::WriteBigEndian
<uint16
>(length_buffer_
->data(),
392 query_
->io_buffer()->size());
394 new DrainableIOBuffer(length_buffer_
.get(), length_buffer_
->size());
395 next_state_
= STATE_SEND_LENGTH
;
399 int DoSendLength(int rv
) {
400 DCHECK_NE(ERR_IO_PENDING
, rv
);
404 buffer_
->DidConsume(rv
);
405 if (buffer_
->BytesRemaining() > 0) {
406 next_state_
= STATE_SEND_LENGTH
;
407 return socket_
->Write(
409 buffer_
->BytesRemaining(),
410 base::Bind(&DnsTCPAttempt::OnIOComplete
, base::Unretained(this)));
412 buffer_
= new DrainableIOBuffer(query_
->io_buffer(),
413 query_
->io_buffer()->size());
414 next_state_
= STATE_SEND_QUERY
;
418 int DoSendQuery(int rv
) {
419 DCHECK_NE(ERR_IO_PENDING
, rv
);
423 buffer_
->DidConsume(rv
);
424 if (buffer_
->BytesRemaining() > 0) {
425 next_state_
= STATE_SEND_QUERY
;
426 return socket_
->Write(
428 buffer_
->BytesRemaining(),
429 base::Bind(&DnsTCPAttempt::OnIOComplete
, base::Unretained(this)));
432 new DrainableIOBuffer(length_buffer_
.get(), length_buffer_
->size());
433 next_state_
= STATE_READ_LENGTH
;
437 int DoReadLength(int rv
) {
438 DCHECK_NE(ERR_IO_PENDING
, rv
);
442 buffer_
->DidConsume(rv
);
443 if (buffer_
->BytesRemaining() > 0) {
444 next_state_
= STATE_READ_LENGTH
;
445 return socket_
->Read(
447 buffer_
->BytesRemaining(),
448 base::Bind(&DnsTCPAttempt::OnIOComplete
, base::Unretained(this)));
450 base::ReadBigEndian
<uint16
>(length_buffer_
->data(), &response_length_
);
451 // Check if advertised response is too short. (Optimization only.)
452 if (response_length_
< query_
->io_buffer()->size())
453 return ERR_DNS_MALFORMED_RESPONSE
;
454 // Allocate more space so that DnsResponse::InitParse sanity check passes.
455 response_
.reset(new DnsResponse(response_length_
+ 1));
456 buffer_
= new DrainableIOBuffer(response_
->io_buffer(), response_length_
);
457 next_state_
= STATE_READ_RESPONSE
;
461 int DoReadResponse(int rv
) {
462 DCHECK_NE(ERR_IO_PENDING
, rv
);
466 buffer_
->DidConsume(rv
);
467 if (buffer_
->BytesRemaining() > 0) {
468 next_state_
= STATE_READ_RESPONSE
;
469 return socket_
->Read(
471 buffer_
->BytesRemaining(),
472 base::Bind(&DnsTCPAttempt::OnIOComplete
, base::Unretained(this)));
474 if (!response_
->InitParse(buffer_
->BytesConsumed(), *query_
))
475 return ERR_DNS_MALFORMED_RESPONSE
;
476 if (response_
->flags() & dns_protocol::kFlagTC
)
477 return ERR_UNEXPECTED
;
478 // TODO(szym): Frankly, none of these are expected.
479 if (response_
->rcode() == dns_protocol::kRcodeNXDOMAIN
)
480 return ERR_NAME_NOT_RESOLVED
;
481 if (response_
->rcode() != dns_protocol::kRcodeNOERROR
)
482 return ERR_DNS_SERVER_FAILED
;
487 void OnIOComplete(int rv
) {
489 if (rv
!= ERR_IO_PENDING
)
494 base::TimeTicks start_time_
;
496 scoped_ptr
<StreamSocket
> socket_
;
497 scoped_ptr
<DnsQuery
> query_
;
498 scoped_refptr
<IOBufferWithSize
> length_buffer_
;
499 scoped_refptr
<DrainableIOBuffer
> buffer_
;
501 uint16 response_length_
;
502 scoped_ptr
<DnsResponse
> response_
;
504 CompletionCallback callback_
;
506 DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt
);
509 // ----------------------------------------------------------------------------
511 // Implements DnsTransaction. Configuration is supplied by DnsSession.
512 // The suffix list is built according to the DnsConfig from the session.
513 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout.
514 // The first server to attempt on each query is given by
515 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards.
516 // Each server is attempted DnsConfig::attempts times.
517 class DnsTransactionImpl
: public DnsTransaction
,
518 public base::NonThreadSafe
,
519 public base::SupportsWeakPtr
<DnsTransactionImpl
> {
521 DnsTransactionImpl(DnsSession
* session
,
522 const std::string
& hostname
,
524 const DnsTransactionFactory::CallbackType
& callback
,
525 const BoundNetLog
& net_log
)
531 qnames_initial_size_(0),
533 had_tcp_attempt_(false),
534 first_server_index_(0) {
535 DCHECK(session_
.get());
536 DCHECK(!hostname_
.empty());
537 DCHECK(!callback_
.is_null());
538 DCHECK(!IsIPLiteral(hostname_
));
541 virtual ~DnsTransactionImpl() {
542 if (!callback_
.is_null()) {
543 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION
,
545 } // otherwise logged in DoCallback or Start
548 virtual const std::string
& GetHostname() const OVERRIDE
{
549 DCHECK(CalledOnValidThread());
553 virtual uint16
GetType() const OVERRIDE
{
554 DCHECK(CalledOnValidThread());
558 virtual void Start() OVERRIDE
{
559 DCHECK(!callback_
.is_null());
560 DCHECK(attempts_
.empty());
561 net_log_
.BeginEvent(NetLog::TYPE_DNS_TRANSACTION
,
562 base::Bind(&NetLogStartCallback
, &hostname_
, qtype_
));
563 AttemptResult
result(PrepareSearch(), NULL
);
564 if (result
.rv
== OK
) {
565 qnames_initial_size_
= qnames_
.size();
566 if (qtype_
== dns_protocol::kTypeA
)
567 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchStart", qnames_
.size());
568 result
= ProcessAttemptResult(StartQuery());
571 // Must always return result asynchronously, to avoid reentrancy.
572 if (result
.rv
!= ERR_IO_PENDING
) {
573 base::MessageLoop::current()->PostTask(
575 base::Bind(&DnsTransactionImpl::DoCallback
, AsWeakPtr(), result
));
580 // Wrapper for the result of a DnsUDPAttempt.
581 struct AttemptResult
{
582 AttemptResult(int rv
, const DnsAttempt
* attempt
)
583 : rv(rv
), attempt(attempt
) {}
586 const DnsAttempt
* attempt
;
589 // Prepares |qnames_| according to the DnsConfig.
590 int PrepareSearch() {
591 const DnsConfig
& config
= session_
->config();
593 std::string labeled_hostname
;
594 if (!DNSDomainFromDot(hostname_
, &labeled_hostname
))
595 return ERR_INVALID_ARGUMENT
;
597 if (hostname_
[hostname_
.size() - 1] == '.') {
598 // It's a fully-qualified name, no suffix search.
599 qnames_
.push_back(labeled_hostname
);
603 int ndots
= CountLabels(labeled_hostname
) - 1;
605 if (ndots
> 0 && !config
.append_to_multi_label_name
) {
606 qnames_
.push_back(labeled_hostname
);
610 // Set true when |labeled_hostname| is put on the list.
611 bool had_hostname
= false;
613 if (ndots
>= config
.ndots
) {
614 qnames_
.push_back(labeled_hostname
);
619 for (size_t i
= 0; i
< config
.search
.size(); ++i
) {
620 // Ignore invalid (too long) combinations.
621 if (!DNSDomainFromDot(hostname_
+ "." + config
.search
[i
], &qname
))
623 if (qname
.size() == labeled_hostname
.size()) {
628 qnames_
.push_back(qname
);
631 if (ndots
> 0 && !had_hostname
)
632 qnames_
.push_back(labeled_hostname
);
634 return qnames_
.empty() ? ERR_DNS_SEARCH_EMPTY
: OK
;
637 void DoCallback(AttemptResult result
) {
638 DCHECK(!callback_
.is_null());
639 DCHECK_NE(ERR_IO_PENDING
, result
.rv
);
640 const DnsResponse
* response
= result
.attempt
?
641 result
.attempt
->GetResponse() : NULL
;
642 CHECK(result
.rv
!= OK
|| response
!= NULL
);
645 RecordLostPacketsIfAny();
647 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountSuccess", attempts_count_
);
649 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountFail", attempts_count_
);
651 if (response
&& qtype_
== dns_protocol::kTypeA
) {
652 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchRemain", qnames_
.size());
653 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchDone",
654 qnames_initial_size_
- qnames_
.size());
657 DnsTransactionFactory::CallbackType callback
= callback_
;
660 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION
, result
.rv
);
661 callback
.Run(this, result
.rv
, response
);
664 // Makes another attempt at the current name, |qnames_.front()|, using the
666 AttemptResult
MakeAttempt() {
667 unsigned attempt_number
= attempts_
.size();
669 uint16 id
= session_
->NextQueryId();
670 scoped_ptr
<DnsQuery
> query
;
671 if (attempts_
.empty()) {
672 query
.reset(new DnsQuery(id
, qnames_
.front(), qtype_
));
674 query
.reset(attempts_
[0]->GetQuery()->CloneWithNewId(id
));
677 const DnsConfig
& config
= session_
->config();
679 unsigned server_index
=
680 (first_server_index_
+ attempt_number
) % config
.nameservers
.size();
681 // Skip over known failed servers.
682 server_index
= session_
->NextGoodServerIndex(server_index
);
684 scoped_ptr
<DnsSession::SocketLease
> lease
=
685 session_
->AllocateSocket(server_index
, net_log_
.source());
687 bool got_socket
= !!lease
.get();
689 DnsUDPAttempt
* attempt
=
690 new DnsUDPAttempt(server_index
, lease
.Pass(), query
.Pass());
692 attempts_
.push_back(attempt
);
696 return AttemptResult(ERR_CONNECTION_REFUSED
, NULL
);
699 NetLog::TYPE_DNS_TRANSACTION_ATTEMPT
,
700 attempt
->GetSocketNetLog().source().ToEventParametersCallback());
702 int rv
= attempt
->Start(
703 base::Bind(&DnsTransactionImpl::OnUdpAttemptComplete
,
704 base::Unretained(this), attempt_number
,
705 base::TimeTicks::Now()));
706 if (rv
== ERR_IO_PENDING
) {
707 base::TimeDelta timeout
= session_
->NextTimeout(server_index
,
709 timer_
.Start(FROM_HERE
, timeout
, this, &DnsTransactionImpl::OnTimeout
);
711 return AttemptResult(rv
, attempt
);
714 AttemptResult
MakeTCPAttempt(const DnsAttempt
* previous_attempt
) {
715 DCHECK(previous_attempt
);
716 DCHECK(!had_tcp_attempt_
);
718 unsigned server_index
= previous_attempt
->server_index();
720 scoped_ptr
<StreamSocket
> socket(
721 session_
->CreateTCPSocket(server_index
, net_log_
.source()));
723 // TODO(szym): Reuse the same id to help the server?
724 uint16 id
= session_
->NextQueryId();
725 scoped_ptr
<DnsQuery
> query(
726 previous_attempt
->GetQuery()->CloneWithNewId(id
));
728 RecordLostPacketsIfAny();
729 // Cancel all other attempts, no point waiting on them.
732 unsigned attempt_number
= attempts_
.size();
734 DnsTCPAttempt
* attempt
= new DnsTCPAttempt(server_index
, socket
.Pass(),
737 attempts_
.push_back(attempt
);
739 had_tcp_attempt_
= true;
742 NetLog::TYPE_DNS_TRANSACTION_TCP_ATTEMPT
,
743 attempt
->GetSocketNetLog().source().ToEventParametersCallback());
745 int rv
= attempt
->Start(base::Bind(&DnsTransactionImpl::OnAttemptComplete
,
746 base::Unretained(this),
748 if (rv
== ERR_IO_PENDING
) {
749 // Custom timeout for TCP attempt.
750 base::TimeDelta timeout
= timer_
.GetCurrentDelay() * 2;
751 timer_
.Start(FROM_HERE
, timeout
, this, &DnsTransactionImpl::OnTimeout
);
753 return AttemptResult(rv
, attempt
);
756 // Begins query for the current name. Makes the first attempt.
757 AttemptResult
StartQuery() {
758 std::string dotted_qname
= DNSDomainToString(qnames_
.front());
759 net_log_
.BeginEvent(NetLog::TYPE_DNS_TRANSACTION_QUERY
,
760 NetLog::StringCallback("qname", &dotted_qname
));
762 first_server_index_
= session_
->NextFirstServerIndex();
763 RecordLostPacketsIfAny();
765 had_tcp_attempt_
= false;
766 return MakeAttempt();
769 void OnUdpAttemptComplete(unsigned attempt_number
,
770 base::TimeTicks start
,
772 DCHECK_LT(attempt_number
, attempts_
.size());
773 const DnsAttempt
* attempt
= attempts_
[attempt_number
];
774 if (attempt
->GetResponse()) {
775 session_
->RecordRTT(attempt
->server_index(),
776 base::TimeTicks::Now() - start
);
778 OnAttemptComplete(attempt_number
, rv
);
781 void OnAttemptComplete(unsigned attempt_number
, int rv
) {
782 if (callback_
.is_null())
784 DCHECK_LT(attempt_number
, attempts_
.size());
785 const DnsAttempt
* attempt
= attempts_
[attempt_number
];
786 AttemptResult result
= ProcessAttemptResult(AttemptResult(rv
, attempt
));
787 if (result
.rv
!= ERR_IO_PENDING
)
791 // Record packet loss for any incomplete attempts.
792 void RecordLostPacketsIfAny() {
793 // Loop through attempts until we find first that is completed
794 size_t first_completed
= 0;
795 for (first_completed
= 0; first_completed
< attempts_
.size();
797 if (attempts_
[first_completed
]->is_completed())
800 // If there were no completed attempts, then we must be offline, so don't
801 // record any attempts as lost packets.
802 if (first_completed
== attempts_
.size())
805 size_t num_servers
= session_
->config().nameservers
.size();
806 std::vector
<int> server_attempts(num_servers
);
807 for (size_t i
= 0; i
< first_completed
; ++i
) {
808 unsigned server_index
= attempts_
[i
]->server_index();
809 int server_attempt
= server_attempts
[server_index
]++;
810 // Don't record lost packet unless attempt is in pending state.
811 if (!attempts_
[i
]->is_pending())
813 session_
->RecordLostPacket(server_index
, server_attempt
);
817 void LogResponse(const DnsAttempt
* attempt
) {
818 if (attempt
&& attempt
->GetResponse()) {
820 NetLog::TYPE_DNS_TRANSACTION_RESPONSE
,
821 base::Bind(&DnsAttempt::NetLogResponseCallback
,
822 base::Unretained(attempt
)));
826 bool MoreAttemptsAllowed() const {
827 if (had_tcp_attempt_
)
829 const DnsConfig
& config
= session_
->config();
830 return attempts_
.size() < config
.attempts
* config
.nameservers
.size();
833 // Resolves the result of a DnsAttempt until a terminal result is reached
834 // or it will complete asynchronously (ERR_IO_PENDING).
835 AttemptResult
ProcessAttemptResult(AttemptResult result
) {
836 while (result
.rv
!= ERR_IO_PENDING
) {
837 LogResponse(result
.attempt
);
841 session_
->RecordServerSuccess(result
.attempt
->server_index());
842 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY
,
844 DCHECK(result
.attempt
);
845 DCHECK(result
.attempt
->GetResponse());
847 case ERR_NAME_NOT_RESOLVED
:
848 session_
->RecordServerSuccess(result
.attempt
->server_index());
849 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY
,
853 if (qnames_
.empty()) {
854 return AttemptResult(ERR_NAME_NOT_RESOLVED
, NULL
);
856 result
= StartQuery();
859 case ERR_CONNECTION_REFUSED
:
860 case ERR_DNS_TIMED_OUT
:
862 session_
->RecordServerFailure(result
.attempt
->server_index());
863 if (MoreAttemptsAllowed()) {
864 result
= MakeAttempt();
869 case ERR_DNS_SERVER_REQUIRES_TCP
:
870 result
= MakeTCPAttempt(result
.attempt
);
874 DCHECK(result
.attempt
);
875 if (result
.attempt
!= attempts_
.back()) {
876 // This attempt already timed out. Ignore it.
877 session_
->RecordServerFailure(result
.attempt
->server_index());
878 return AttemptResult(ERR_IO_PENDING
, NULL
);
880 if (MoreAttemptsAllowed()) {
881 result
= MakeAttempt();
882 } else if (result
.rv
== ERR_DNS_MALFORMED_RESPONSE
&&
884 // For UDP only, ignore the response and wait until the last attempt
886 return AttemptResult(ERR_IO_PENDING
, NULL
);
888 return AttemptResult(result
.rv
, NULL
);
897 if (callback_
.is_null())
899 DCHECK(!attempts_
.empty());
900 AttemptResult result
= ProcessAttemptResult(
901 AttemptResult(ERR_DNS_TIMED_OUT
, attempts_
.back()));
902 if (result
.rv
!= ERR_IO_PENDING
)
906 scoped_refptr
<DnsSession
> session_
;
907 std::string hostname_
;
909 // Cleared in DoCallback.
910 DnsTransactionFactory::CallbackType callback_
;
912 BoundNetLog net_log_
;
914 // Search list of fully-qualified DNS names to query next (in DNS format).
915 std::deque
<std::string
> qnames_
;
916 size_t qnames_initial_size_
;
918 // List of attempts for the current name.
919 ScopedVector
<DnsAttempt
> attempts_
;
920 // Count of attempts, not reset when |attempts_| vector is cleared.
922 bool had_tcp_attempt_
;
924 // Index of the first server to try on each search query.
925 int first_server_index_
;
927 base::OneShotTimer
<DnsTransactionImpl
> timer_
;
929 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl
);
932 // ----------------------------------------------------------------------------
934 // Implementation of DnsTransactionFactory that returns instances of
935 // DnsTransactionImpl.
936 class DnsTransactionFactoryImpl
: public DnsTransactionFactory
{
938 explicit DnsTransactionFactoryImpl(DnsSession
* session
) {
942 virtual scoped_ptr
<DnsTransaction
> CreateTransaction(
943 const std::string
& hostname
,
945 const CallbackType
& callback
,
946 const BoundNetLog
& net_log
) OVERRIDE
{
947 return scoped_ptr
<DnsTransaction
>(new DnsTransactionImpl(
948 session_
.get(), hostname
, qtype
, callback
, net_log
));
952 scoped_refptr
<DnsSession
> session_
;
958 scoped_ptr
<DnsTransactionFactory
> DnsTransactionFactory::CreateFactory(
959 DnsSession
* session
) {
960 return scoped_ptr
<DnsTransactionFactory
>(
961 new DnsTransactionFactoryImpl(session
));