Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / net / dns / dns_transaction.cc
blobd9286a098785ddf58d9c7bc4b99161d7815c2201
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"
7 #include <deque>
8 #include <string>
9 #include <vector>
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"
39 namespace net {
41 namespace {
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) {
50 size_t count = 0;
51 for (size_t i = 0; i < name.size() && name[i]; i += name[i] + 1)
52 ++count;
53 return count;
56 bool IsIPLiteral(const std::string& hostname) {
57 IPAddressNumber ip;
58 return ParseIPLiteralToNumber(hostname, &ip);
61 scoped_ptr<base::Value> NetLogStartCallback(
62 const std::string* hostname,
63 uint16 qtype,
64 NetLogCaptureMode /* capture_mode */) {
65 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
66 dict->SetString("hostname", *hostname);
67 dict->SetInteger("query_type", qtype);
68 return dict.Pass();
71 // ----------------------------------------------------------------------------
73 // A single asynchronous DNS exchange, which consists of sending out a
74 // DNS query, waiting for a response, and returning the response that it
75 // matches. Logging is done in the socket and in the outer DnsTransaction.
76 class DnsAttempt {
77 public:
78 explicit DnsAttempt(unsigned server_index)
79 : result_(ERR_FAILED), server_index_(server_index) {}
81 virtual ~DnsAttempt() {}
82 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously
83 // and calls |callback| upon completion.
84 virtual int Start(const CompletionCallback& callback) = 0;
86 // Returns the query of this attempt.
87 virtual const DnsQuery* GetQuery() const = 0;
89 // Returns the response or NULL if has not received a matching response from
90 // the server.
91 virtual const DnsResponse* GetResponse() const = 0;
93 // Returns the net log bound to the source of the socket.
94 virtual const BoundNetLog& GetSocketNetLog() const = 0;
96 // Returns the index of the destination server within DnsConfig::nameservers.
97 unsigned server_index() const { return server_index_; }
99 // Returns a Value representing the received response, along with a reference
100 // to the NetLog source source of the UDP socket used. The request must have
101 // completed before this is called.
102 scoped_ptr<base::Value> NetLogResponseCallback(
103 NetLogCaptureMode capture_mode) const {
104 DCHECK(GetResponse()->IsValid());
106 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
107 dict->SetInteger("rcode", GetResponse()->rcode());
108 dict->SetInteger("answer_count", GetResponse()->answer_count());
109 GetSocketNetLog().source().AddToEventParameters(dict.get());
110 return dict.Pass();
113 void set_result(int result) {
114 result_ = result;
117 // True if current attempt is pending (waiting for server response).
118 bool is_pending() const {
119 return result_ == ERR_IO_PENDING;
122 // True if attempt is completed (received server response).
123 bool is_completed() const {
124 return (result_ == OK) || (result_ == ERR_NAME_NOT_RESOLVED) ||
125 (result_ == ERR_DNS_SERVER_REQUIRES_TCP);
128 private:
129 // Result of last operation.
130 int result_;
132 const unsigned server_index_;
135 class DnsUDPAttempt : public DnsAttempt {
136 public:
137 DnsUDPAttempt(unsigned server_index,
138 scoped_ptr<DnsSession::SocketLease> socket_lease,
139 scoped_ptr<DnsQuery> query)
140 : DnsAttempt(server_index),
141 next_state_(STATE_NONE),
142 received_malformed_response_(false),
143 socket_lease_(socket_lease.Pass()),
144 query_(query.Pass()) {}
146 // DnsAttempt:
147 int Start(const CompletionCallback& callback) override {
148 DCHECK_EQ(STATE_NONE, next_state_);
149 callback_ = callback;
150 start_time_ = base::TimeTicks::Now();
151 next_state_ = STATE_SEND_QUERY;
152 return DoLoop(OK);
155 const DnsQuery* GetQuery() const override { return query_.get(); }
157 const DnsResponse* GetResponse() const override {
158 const DnsResponse* resp = response_.get();
159 return (resp != NULL && resp->IsValid()) ? resp : NULL;
162 const BoundNetLog& GetSocketNetLog() const override {
163 return socket_lease_->socket()->NetLog();
166 private:
167 enum State {
168 STATE_SEND_QUERY,
169 STATE_SEND_QUERY_COMPLETE,
170 STATE_READ_RESPONSE,
171 STATE_READ_RESPONSE_COMPLETE,
172 STATE_NONE,
175 DatagramClientSocket* socket() {
176 return socket_lease_->socket();
179 int DoLoop(int result) {
180 CHECK_NE(STATE_NONE, next_state_);
181 int rv = result;
182 do {
183 State state = next_state_;
184 next_state_ = STATE_NONE;
185 switch (state) {
186 case STATE_SEND_QUERY:
187 rv = DoSendQuery();
188 break;
189 case STATE_SEND_QUERY_COMPLETE:
190 rv = DoSendQueryComplete(rv);
191 break;
192 case STATE_READ_RESPONSE:
193 rv = DoReadResponse();
194 break;
195 case STATE_READ_RESPONSE_COMPLETE:
196 rv = DoReadResponseComplete(rv);
197 break;
198 default:
199 NOTREACHED();
200 break;
202 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
204 set_result(rv);
205 // If we received a malformed response, and are now waiting for another one,
206 // indicate to the transaction that the server might be misbehaving.
207 if (rv == ERR_IO_PENDING && received_malformed_response_)
208 return ERR_DNS_MALFORMED_RESPONSE;
209 if (rv == OK) {
210 DCHECK_EQ(STATE_NONE, next_state_);
211 DNS_HISTOGRAM("AsyncDNS.UDPAttemptSuccess",
212 base::TimeTicks::Now() - start_time_);
213 } else if (rv != ERR_IO_PENDING) {
214 DNS_HISTOGRAM("AsyncDNS.UDPAttemptFail",
215 base::TimeTicks::Now() - start_time_);
217 return rv;
220 int DoSendQuery() {
221 next_state_ = STATE_SEND_QUERY_COMPLETE;
222 return socket()->Write(query_->io_buffer(),
223 query_->io_buffer()->size(),
224 base::Bind(&DnsUDPAttempt::OnIOComplete,
225 base::Unretained(this)));
228 int DoSendQueryComplete(int rv) {
229 DCHECK_NE(ERR_IO_PENDING, rv);
230 if (rv < 0)
231 return rv;
233 // Writing to UDP should not result in a partial datagram.
234 if (rv != query_->io_buffer()->size())
235 return ERR_MSG_TOO_BIG;
237 next_state_ = STATE_READ_RESPONSE;
238 return OK;
241 int DoReadResponse() {
242 next_state_ = STATE_READ_RESPONSE_COMPLETE;
243 response_.reset(new DnsResponse());
244 return socket()->Read(response_->io_buffer(),
245 response_->io_buffer()->size(),
246 base::Bind(&DnsUDPAttempt::OnIOComplete,
247 base::Unretained(this)));
250 int DoReadResponseComplete(int rv) {
251 DCHECK_NE(ERR_IO_PENDING, rv);
252 if (rv < 0)
253 return rv;
255 DCHECK(rv);
256 if (!response_->InitParse(rv, *query_)) {
257 // Other implementations simply ignore mismatched responses. Since each
258 // DnsUDPAttempt binds to a different port, we might find that responses
259 // to previously timed out queries lead to failures in the future.
260 // Our solution is to make another attempt, in case the query truly
261 // failed, but keep this attempt alive, in case it was a false alarm.
262 received_malformed_response_ = true;
263 next_state_ = STATE_READ_RESPONSE;
264 return OK;
266 if (response_->flags() & dns_protocol::kFlagTC)
267 return ERR_DNS_SERVER_REQUIRES_TCP;
268 // TODO(szym): Extract TTL for NXDOMAIN results. http://crbug.com/115051
269 if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
270 return ERR_NAME_NOT_RESOLVED;
271 if (response_->rcode() != dns_protocol::kRcodeNOERROR)
272 return ERR_DNS_SERVER_FAILED;
274 return OK;
277 void OnIOComplete(int rv) {
278 rv = DoLoop(rv);
279 if (rv != ERR_IO_PENDING)
280 callback_.Run(rv);
283 State next_state_;
284 bool received_malformed_response_;
285 base::TimeTicks start_time_;
287 scoped_ptr<DnsSession::SocketLease> socket_lease_;
288 scoped_ptr<DnsQuery> query_;
290 scoped_ptr<DnsResponse> response_;
292 CompletionCallback callback_;
294 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt);
297 class DnsTCPAttempt : public DnsAttempt {
298 public:
299 DnsTCPAttempt(unsigned server_index,
300 scoped_ptr<StreamSocket> socket,
301 scoped_ptr<DnsQuery> query)
302 : DnsAttempt(server_index),
303 next_state_(STATE_NONE),
304 socket_(socket.Pass()),
305 query_(query.Pass()),
306 length_buffer_(new IOBufferWithSize(sizeof(uint16))),
307 response_length_(0) {}
309 // DnsAttempt:
310 int Start(const CompletionCallback& callback) override {
311 DCHECK_EQ(STATE_NONE, next_state_);
312 callback_ = callback;
313 start_time_ = base::TimeTicks::Now();
314 next_state_ = STATE_CONNECT_COMPLETE;
315 int rv = socket_->Connect(base::Bind(&DnsTCPAttempt::OnIOComplete,
316 base::Unretained(this)));
317 if (rv == ERR_IO_PENDING) {
318 set_result(rv);
319 return rv;
321 return DoLoop(rv);
324 const DnsQuery* GetQuery() const override { return query_.get(); }
326 const DnsResponse* GetResponse() const override {
327 const DnsResponse* resp = response_.get();
328 return (resp != NULL && resp->IsValid()) ? resp : NULL;
331 const BoundNetLog& GetSocketNetLog() const override {
332 return socket_->NetLog();
335 private:
336 enum State {
337 STATE_CONNECT_COMPLETE,
338 STATE_SEND_LENGTH,
339 STATE_SEND_QUERY,
340 STATE_READ_LENGTH,
341 STATE_READ_LENGTH_COMPLETE,
342 STATE_READ_RESPONSE,
343 STATE_READ_RESPONSE_COMPLETE,
344 STATE_NONE,
347 int DoLoop(int result) {
348 CHECK_NE(STATE_NONE, next_state_);
349 int rv = result;
350 do {
351 State state = next_state_;
352 next_state_ = STATE_NONE;
353 switch (state) {
354 case STATE_CONNECT_COMPLETE:
355 rv = DoConnectComplete(rv);
356 break;
357 case STATE_SEND_LENGTH:
358 rv = DoSendLength(rv);
359 break;
360 case STATE_SEND_QUERY:
361 rv = DoSendQuery(rv);
362 break;
363 case STATE_READ_LENGTH:
364 rv = DoReadLength(rv);
365 break;
366 case STATE_READ_LENGTH_COMPLETE:
367 rv = DoReadLengthComplete(rv);
368 break;
369 case STATE_READ_RESPONSE:
370 rv = DoReadResponse(rv);
371 break;
372 case STATE_READ_RESPONSE_COMPLETE:
373 rv = DoReadResponseComplete(rv);
374 break;
375 default:
376 NOTREACHED();
377 break;
379 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
381 set_result(rv);
382 if (rv == OK) {
383 DCHECK_EQ(STATE_NONE, next_state_);
384 DNS_HISTOGRAM("AsyncDNS.TCPAttemptSuccess",
385 base::TimeTicks::Now() - start_time_);
386 } else if (rv != ERR_IO_PENDING) {
387 DNS_HISTOGRAM("AsyncDNS.TCPAttemptFail",
388 base::TimeTicks::Now() - start_time_);
390 return rv;
393 int DoConnectComplete(int rv) {
394 // TODO(rvargas): Remove ScopedTracker below once crbug.com/462784 is fixed.
395 tracked_objects::ScopedTracker tracking_profile(
396 FROM_HERE_WITH_EXPLICIT_FUNCTION(
397 "462784 DnsTCPAttempt::DoConnectComplete"));
399 DCHECK_NE(ERR_IO_PENDING, rv);
400 if (rv < 0)
401 return rv;
403 uint16 query_size = static_cast<uint16>(query_->io_buffer()->size());
404 if (static_cast<int>(query_size) != query_->io_buffer()->size())
405 return ERR_FAILED;
406 base::WriteBigEndian<uint16>(length_buffer_->data(), query_size);
407 buffer_ =
408 new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
409 next_state_ = STATE_SEND_LENGTH;
410 return OK;
413 int DoSendLength(int rv) {
414 DCHECK_NE(ERR_IO_PENDING, rv);
415 if (rv < 0)
416 return rv;
418 buffer_->DidConsume(rv);
419 if (buffer_->BytesRemaining() > 0) {
420 next_state_ = STATE_SEND_LENGTH;
421 return socket_->Write(
422 buffer_.get(),
423 buffer_->BytesRemaining(),
424 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
426 buffer_ = new DrainableIOBuffer(query_->io_buffer(),
427 query_->io_buffer()->size());
428 next_state_ = STATE_SEND_QUERY;
429 return OK;
432 int DoSendQuery(int rv) {
433 DCHECK_NE(ERR_IO_PENDING, rv);
434 if (rv < 0)
435 return rv;
437 buffer_->DidConsume(rv);
438 if (buffer_->BytesRemaining() > 0) {
439 next_state_ = STATE_SEND_QUERY;
440 return socket_->Write(
441 buffer_.get(),
442 buffer_->BytesRemaining(),
443 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
445 buffer_ =
446 new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
447 next_state_ = STATE_READ_LENGTH;
448 return OK;
451 int DoReadLength(int rv) {
452 DCHECK_EQ(OK, rv);
454 next_state_ = STATE_READ_LENGTH_COMPLETE;
455 return ReadIntoBuffer();
458 int DoReadLengthComplete(int rv) {
459 DCHECK_NE(ERR_IO_PENDING, rv);
460 if (rv < 0)
461 return rv;
462 if (rv == 0)
463 return ERR_CONNECTION_CLOSED;
465 buffer_->DidConsume(rv);
466 if (buffer_->BytesRemaining() > 0) {
467 next_state_ = STATE_READ_LENGTH;
468 return OK;
471 base::ReadBigEndian<uint16>(length_buffer_->data(), &response_length_);
472 // Check if advertised response is too short. (Optimization only.)
473 if (response_length_ < query_->io_buffer()->size())
474 return ERR_DNS_MALFORMED_RESPONSE;
475 // Allocate more space so that DnsResponse::InitParse sanity check passes.
476 response_.reset(new DnsResponse(response_length_ + 1));
477 buffer_ = new DrainableIOBuffer(response_->io_buffer(), response_length_);
478 next_state_ = STATE_READ_RESPONSE;
479 return OK;
482 int DoReadResponse(int rv) {
483 DCHECK_EQ(OK, rv);
485 next_state_ = STATE_READ_RESPONSE_COMPLETE;
486 return ReadIntoBuffer();
489 int DoReadResponseComplete(int rv) {
490 DCHECK_NE(ERR_IO_PENDING, rv);
491 if (rv < 0)
492 return rv;
493 if (rv == 0)
494 return ERR_CONNECTION_CLOSED;
496 buffer_->DidConsume(rv);
497 if (buffer_->BytesRemaining() > 0) {
498 next_state_ = STATE_READ_RESPONSE;
499 return OK;
502 if (!response_->InitParse(buffer_->BytesConsumed(), *query_))
503 return ERR_DNS_MALFORMED_RESPONSE;
504 if (response_->flags() & dns_protocol::kFlagTC)
505 return ERR_UNEXPECTED;
506 // TODO(szym): Frankly, none of these are expected.
507 if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
508 return ERR_NAME_NOT_RESOLVED;
509 if (response_->rcode() != dns_protocol::kRcodeNOERROR)
510 return ERR_DNS_SERVER_FAILED;
512 return OK;
515 void OnIOComplete(int rv) {
516 rv = DoLoop(rv);
517 if (rv != ERR_IO_PENDING)
518 callback_.Run(rv);
521 int ReadIntoBuffer() {
522 return socket_->Read(
523 buffer_.get(),
524 buffer_->BytesRemaining(),
525 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
528 State next_state_;
529 base::TimeTicks start_time_;
531 scoped_ptr<StreamSocket> socket_;
532 scoped_ptr<DnsQuery> query_;
533 scoped_refptr<IOBufferWithSize> length_buffer_;
534 scoped_refptr<DrainableIOBuffer> buffer_;
536 uint16 response_length_;
537 scoped_ptr<DnsResponse> response_;
539 CompletionCallback callback_;
541 DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt);
544 // ----------------------------------------------------------------------------
546 // Implements DnsTransaction. Configuration is supplied by DnsSession.
547 // The suffix list is built according to the DnsConfig from the session.
548 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout.
549 // The first server to attempt on each query is given by
550 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards.
551 // Each server is attempted DnsConfig::attempts times.
552 class DnsTransactionImpl : public DnsTransaction,
553 public base::NonThreadSafe,
554 public base::SupportsWeakPtr<DnsTransactionImpl> {
555 public:
556 DnsTransactionImpl(DnsSession* session,
557 const std::string& hostname,
558 uint16 qtype,
559 const DnsTransactionFactory::CallbackType& callback,
560 const BoundNetLog& net_log)
561 : session_(session),
562 hostname_(hostname),
563 qtype_(qtype),
564 callback_(callback),
565 net_log_(net_log),
566 qnames_initial_size_(0),
567 attempts_count_(0),
568 had_tcp_attempt_(false),
569 first_server_index_(0) {
570 DCHECK(session_.get());
571 DCHECK(!hostname_.empty());
572 DCHECK(!callback_.is_null());
573 DCHECK(!IsIPLiteral(hostname_));
576 ~DnsTransactionImpl() override {
577 if (!callback_.is_null()) {
578 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION,
579 ERR_ABORTED);
580 } // otherwise logged in DoCallback or Start
583 const std::string& GetHostname() const override {
584 DCHECK(CalledOnValidThread());
585 return hostname_;
588 uint16 GetType() const override {
589 DCHECK(CalledOnValidThread());
590 return qtype_;
593 void Start() override {
594 DCHECK(!callback_.is_null());
595 DCHECK(attempts_.empty());
596 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION,
597 base::Bind(&NetLogStartCallback, &hostname_, qtype_));
598 AttemptResult result(PrepareSearch(), NULL);
599 if (result.rv == OK) {
600 qnames_initial_size_ = qnames_.size();
601 if (qtype_ == dns_protocol::kTypeA)
602 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchStart", qnames_.size());
603 result = ProcessAttemptResult(StartQuery());
606 // Must always return result asynchronously, to avoid reentrancy.
607 if (result.rv != ERR_IO_PENDING) {
608 base::MessageLoop::current()->PostTask(
609 FROM_HERE,
610 base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result));
614 private:
615 // Wrapper for the result of a DnsUDPAttempt.
616 struct AttemptResult {
617 AttemptResult(int rv, const DnsAttempt* attempt)
618 : rv(rv), attempt(attempt) {}
620 int rv;
621 const DnsAttempt* attempt;
624 // Prepares |qnames_| according to the DnsConfig.
625 int PrepareSearch() {
626 const DnsConfig& config = session_->config();
628 std::string labeled_hostname;
629 if (!DNSDomainFromDot(hostname_, &labeled_hostname))
630 return ERR_INVALID_ARGUMENT;
632 if (hostname_[hostname_.size() - 1] == '.') {
633 // It's a fully-qualified name, no suffix search.
634 qnames_.push_back(labeled_hostname);
635 return OK;
638 int ndots = CountLabels(labeled_hostname) - 1;
640 if (ndots > 0 && !config.append_to_multi_label_name) {
641 qnames_.push_back(labeled_hostname);
642 return OK;
645 // Set true when |labeled_hostname| is put on the list.
646 bool had_hostname = false;
648 if (ndots >= config.ndots) {
649 qnames_.push_back(labeled_hostname);
650 had_hostname = true;
653 std::string qname;
654 for (size_t i = 0; i < config.search.size(); ++i) {
655 // Ignore invalid (too long) combinations.
656 if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname))
657 continue;
658 if (qname.size() == labeled_hostname.size()) {
659 if (had_hostname)
660 continue;
661 had_hostname = true;
663 qnames_.push_back(qname);
666 if (ndots > 0 && !had_hostname)
667 qnames_.push_back(labeled_hostname);
669 return qnames_.empty() ? ERR_DNS_SEARCH_EMPTY : OK;
672 void DoCallback(AttemptResult result) {
673 DCHECK(!callback_.is_null());
674 DCHECK_NE(ERR_IO_PENDING, result.rv);
675 const DnsResponse* response = result.attempt ?
676 result.attempt->GetResponse() : NULL;
677 CHECK(result.rv != OK || response != NULL);
679 timer_.Stop();
680 RecordLostPacketsIfAny();
681 if (result.rv == OK)
682 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountSuccess", attempts_count_);
683 else
684 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountFail", attempts_count_);
686 if (response && qtype_ == dns_protocol::kTypeA) {
687 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchRemain", qnames_.size());
688 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchDone",
689 qnames_initial_size_ - qnames_.size());
692 DnsTransactionFactory::CallbackType callback = callback_;
693 callback_.Reset();
695 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, result.rv);
696 callback.Run(this, result.rv, response);
699 // Makes another attempt at the current name, |qnames_.front()|, using the
700 // next nameserver.
701 AttemptResult MakeAttempt() {
702 unsigned attempt_number = attempts_.size();
704 uint16 id = session_->NextQueryId();
705 scoped_ptr<DnsQuery> query;
706 if (attempts_.empty()) {
707 query.reset(new DnsQuery(id, qnames_.front(), qtype_));
708 } else {
709 query.reset(attempts_[0]->GetQuery()->CloneWithNewId(id));
712 const DnsConfig& config = session_->config();
714 unsigned server_index =
715 (first_server_index_ + attempt_number) % config.nameservers.size();
716 // Skip over known failed servers.
717 server_index = session_->NextGoodServerIndex(server_index);
719 scoped_ptr<DnsSession::SocketLease> lease =
720 session_->AllocateSocket(server_index, net_log_.source());
722 bool got_socket = !!lease.get();
724 DnsUDPAttempt* attempt =
725 new DnsUDPAttempt(server_index, lease.Pass(), query.Pass());
727 attempts_.push_back(attempt);
728 ++attempts_count_;
730 if (!got_socket)
731 return AttemptResult(ERR_CONNECTION_REFUSED, NULL);
733 net_log_.AddEvent(
734 NetLog::TYPE_DNS_TRANSACTION_ATTEMPT,
735 attempt->GetSocketNetLog().source().ToEventParametersCallback());
737 int rv = attempt->Start(
738 base::Bind(&DnsTransactionImpl::OnUdpAttemptComplete,
739 base::Unretained(this), attempt_number,
740 base::TimeTicks::Now()));
741 if (rv == ERR_IO_PENDING) {
742 base::TimeDelta timeout = session_->NextTimeout(server_index,
743 attempt_number);
744 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
746 return AttemptResult(rv, attempt);
749 AttemptResult MakeTCPAttempt(const DnsAttempt* previous_attempt) {
750 DCHECK(previous_attempt);
751 DCHECK(!had_tcp_attempt_);
753 unsigned server_index = previous_attempt->server_index();
755 scoped_ptr<StreamSocket> socket(
756 session_->CreateTCPSocket(server_index, net_log_.source()));
758 // TODO(szym): Reuse the same id to help the server?
759 uint16 id = session_->NextQueryId();
760 scoped_ptr<DnsQuery> query(
761 previous_attempt->GetQuery()->CloneWithNewId(id));
763 RecordLostPacketsIfAny();
764 // Cancel all other attempts, no point waiting on them.
765 attempts_.clear();
767 unsigned attempt_number = attempts_.size();
769 DnsTCPAttempt* attempt = new DnsTCPAttempt(server_index, socket.Pass(),
770 query.Pass());
772 attempts_.push_back(attempt);
773 ++attempts_count_;
774 had_tcp_attempt_ = true;
776 net_log_.AddEvent(
777 NetLog::TYPE_DNS_TRANSACTION_TCP_ATTEMPT,
778 attempt->GetSocketNetLog().source().ToEventParametersCallback());
780 int rv = attempt->Start(base::Bind(&DnsTransactionImpl::OnAttemptComplete,
781 base::Unretained(this),
782 attempt_number));
783 if (rv == ERR_IO_PENDING) {
784 // Custom timeout for TCP attempt.
785 base::TimeDelta timeout = timer_.GetCurrentDelay() * 2;
786 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
788 return AttemptResult(rv, attempt);
791 // Begins query for the current name. Makes the first attempt.
792 AttemptResult StartQuery() {
793 std::string dotted_qname = DNSDomainToString(qnames_.front());
794 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION_QUERY,
795 NetLog::StringCallback("qname", &dotted_qname));
797 first_server_index_ = session_->NextFirstServerIndex();
798 RecordLostPacketsIfAny();
799 attempts_.clear();
800 had_tcp_attempt_ = false;
801 return MakeAttempt();
804 void OnUdpAttemptComplete(unsigned attempt_number,
805 base::TimeTicks start,
806 int rv) {
807 DCHECK_LT(attempt_number, attempts_.size());
808 const DnsAttempt* attempt = attempts_[attempt_number];
809 if (attempt->GetResponse()) {
810 session_->RecordRTT(attempt->server_index(),
811 base::TimeTicks::Now() - start);
813 OnAttemptComplete(attempt_number, rv);
816 void OnAttemptComplete(unsigned attempt_number, int rv) {
817 if (callback_.is_null())
818 return;
819 DCHECK_LT(attempt_number, attempts_.size());
820 const DnsAttempt* attempt = attempts_[attempt_number];
821 AttemptResult result = ProcessAttemptResult(AttemptResult(rv, attempt));
822 if (result.rv != ERR_IO_PENDING)
823 DoCallback(result);
826 // Record packet loss for any incomplete attempts.
827 void RecordLostPacketsIfAny() {
828 // Loop through attempts until we find first that is completed
829 size_t first_completed = 0;
830 for (first_completed = 0; first_completed < attempts_.size();
831 ++first_completed) {
832 if (attempts_[first_completed]->is_completed())
833 break;
835 // If there were no completed attempts, then we must be offline, so don't
836 // record any attempts as lost packets.
837 if (first_completed == attempts_.size())
838 return;
840 size_t num_servers = session_->config().nameservers.size();
841 std::vector<int> server_attempts(num_servers);
842 for (size_t i = 0; i < first_completed; ++i) {
843 unsigned server_index = attempts_[i]->server_index();
844 int server_attempt = server_attempts[server_index]++;
845 // Don't record lost packet unless attempt is in pending state.
846 if (!attempts_[i]->is_pending())
847 continue;
848 session_->RecordLostPacket(server_index, server_attempt);
852 void LogResponse(const DnsAttempt* attempt) {
853 if (attempt && attempt->GetResponse()) {
854 net_log_.AddEvent(
855 NetLog::TYPE_DNS_TRANSACTION_RESPONSE,
856 base::Bind(&DnsAttempt::NetLogResponseCallback,
857 base::Unretained(attempt)));
861 bool MoreAttemptsAllowed() const {
862 if (had_tcp_attempt_)
863 return false;
864 const DnsConfig& config = session_->config();
865 return attempts_.size() < config.attempts * config.nameservers.size();
868 // Resolves the result of a DnsAttempt until a terminal result is reached
869 // or it will complete asynchronously (ERR_IO_PENDING).
870 AttemptResult ProcessAttemptResult(AttemptResult result) {
871 while (result.rv != ERR_IO_PENDING) {
872 LogResponse(result.attempt);
874 switch (result.rv) {
875 case OK:
876 session_->RecordServerSuccess(result.attempt->server_index());
877 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
878 result.rv);
879 DCHECK(result.attempt);
880 DCHECK(result.attempt->GetResponse());
881 return result;
882 case ERR_NAME_NOT_RESOLVED:
883 session_->RecordServerSuccess(result.attempt->server_index());
884 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
885 result.rv);
886 // Try next suffix.
887 qnames_.pop_front();
888 if (qnames_.empty()) {
889 return AttemptResult(ERR_NAME_NOT_RESOLVED, NULL);
890 } else {
891 result = StartQuery();
893 break;
894 case ERR_CONNECTION_REFUSED:
895 case ERR_DNS_TIMED_OUT:
896 if (result.attempt)
897 session_->RecordServerFailure(result.attempt->server_index());
898 if (MoreAttemptsAllowed()) {
899 result = MakeAttempt();
900 } else {
901 return result;
903 break;
904 case ERR_DNS_SERVER_REQUIRES_TCP:
905 result = MakeTCPAttempt(result.attempt);
906 break;
907 default:
908 // Server failure.
909 DCHECK(result.attempt);
910 if (result.attempt != attempts_.back()) {
911 // This attempt already timed out. Ignore it.
912 session_->RecordServerFailure(result.attempt->server_index());
913 return AttemptResult(ERR_IO_PENDING, NULL);
915 if (MoreAttemptsAllowed()) {
916 result = MakeAttempt();
917 } else if (result.rv == ERR_DNS_MALFORMED_RESPONSE &&
918 !had_tcp_attempt_) {
919 // For UDP only, ignore the response and wait until the last attempt
920 // times out.
921 return AttemptResult(ERR_IO_PENDING, NULL);
922 } else {
923 return AttemptResult(result.rv, NULL);
925 break;
928 return result;
931 void OnTimeout() {
932 if (callback_.is_null())
933 return;
934 DCHECK(!attempts_.empty());
935 AttemptResult result = ProcessAttemptResult(
936 AttemptResult(ERR_DNS_TIMED_OUT, attempts_.back()));
937 if (result.rv != ERR_IO_PENDING)
938 DoCallback(result);
941 scoped_refptr<DnsSession> session_;
942 std::string hostname_;
943 uint16 qtype_;
944 // Cleared in DoCallback.
945 DnsTransactionFactory::CallbackType callback_;
947 BoundNetLog net_log_;
949 // Search list of fully-qualified DNS names to query next (in DNS format).
950 std::deque<std::string> qnames_;
951 size_t qnames_initial_size_;
953 // List of attempts for the current name.
954 ScopedVector<DnsAttempt> attempts_;
955 // Count of attempts, not reset when |attempts_| vector is cleared.
956 int attempts_count_;
957 bool had_tcp_attempt_;
959 // Index of the first server to try on each search query.
960 int first_server_index_;
962 base::OneShotTimer<DnsTransactionImpl> timer_;
964 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl);
967 // ----------------------------------------------------------------------------
969 // Implementation of DnsTransactionFactory that returns instances of
970 // DnsTransactionImpl.
971 class DnsTransactionFactoryImpl : public DnsTransactionFactory {
972 public:
973 explicit DnsTransactionFactoryImpl(DnsSession* session) {
974 session_ = session;
977 scoped_ptr<DnsTransaction> CreateTransaction(
978 const std::string& hostname,
979 uint16 qtype,
980 const CallbackType& callback,
981 const BoundNetLog& net_log) override {
982 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(
983 session_.get(), hostname, qtype, callback, net_log));
986 private:
987 scoped_refptr<DnsSession> session_;
990 } // namespace
992 // static
993 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory(
994 DnsSession* session) {
995 return scoped_ptr<DnsTransactionFactory>(
996 new DnsTransactionFactoryImpl(session));
999 } // namespace net