Roll src/third_party/skia ff271c2:b679ca8
[chromium-blink-merge.git] / net / dns / dns_transaction.cc
blob8c0b28ba9a9aa4440a19f68eb6cdfd8b544e8e7a
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 base::Value* NetLogStartCallback(const std::string* hostname,
62 uint16 qtype,
63 NetLog::LogLevel /* log_level */) {
64 base::DictionaryValue* dict = new base::DictionaryValue();
65 dict->SetString("hostname", *hostname);
66 dict->SetInteger("query_type", qtype);
67 return dict;
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.
75 class DnsAttempt {
76 public:
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
89 // the server.
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);
108 return dict;
111 void set_result(int result) {
112 result_ = 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);
126 private:
127 // Result of last operation.
128 int result_;
130 const unsigned server_index_;
133 class DnsUDPAttempt : public DnsAttempt {
134 public:
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()) {}
144 // DnsAttempt:
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;
150 return DoLoop(OK);
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();
164 private:
165 enum State {
166 STATE_SEND_QUERY,
167 STATE_SEND_QUERY_COMPLETE,
168 STATE_READ_RESPONSE,
169 STATE_READ_RESPONSE_COMPLETE,
170 STATE_NONE,
173 DatagramClientSocket* socket() {
174 return socket_lease_->socket();
177 int DoLoop(int result) {
178 CHECK_NE(STATE_NONE, next_state_);
179 int rv = result;
180 do {
181 State state = next_state_;
182 next_state_ = STATE_NONE;
183 switch (state) {
184 case STATE_SEND_QUERY:
185 rv = DoSendQuery();
186 break;
187 case STATE_SEND_QUERY_COMPLETE:
188 rv = DoSendQueryComplete(rv);
189 break;
190 case STATE_READ_RESPONSE:
191 rv = DoReadResponse();
192 break;
193 case STATE_READ_RESPONSE_COMPLETE:
194 rv = DoReadResponseComplete(rv);
195 break;
196 default:
197 NOTREACHED();
198 break;
200 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
202 set_result(rv);
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;
207 if (rv == OK) {
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_);
215 return rv;
218 int DoSendQuery() {
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);
228 if (rv < 0)
229 return 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;
236 return OK;
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);
250 if (rv < 0)
251 return rv;
253 DCHECK(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;
262 return OK;
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;
272 return OK;
275 void OnIOComplete(int rv) {
276 rv = DoLoop(rv);
277 if (rv != ERR_IO_PENDING)
278 callback_.Run(rv);
281 State next_state_;
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 {
296 public:
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) {}
307 // DnsAttempt:
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) {
316 set_result(rv);
317 return rv;
319 return DoLoop(rv);
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();
333 private:
334 enum State {
335 STATE_CONNECT_COMPLETE,
336 STATE_SEND_LENGTH,
337 STATE_SEND_QUERY,
338 STATE_READ_LENGTH,
339 STATE_READ_LENGTH_COMPLETE,
340 STATE_READ_RESPONSE,
341 STATE_READ_RESPONSE_COMPLETE,
342 STATE_NONE,
345 int DoLoop(int result) {
346 CHECK_NE(STATE_NONE, next_state_);
347 int rv = result;
348 do {
349 State state = next_state_;
350 next_state_ = STATE_NONE;
351 switch (state) {
352 case STATE_CONNECT_COMPLETE:
353 rv = DoConnectComplete(rv);
354 break;
355 case STATE_SEND_LENGTH:
356 rv = DoSendLength(rv);
357 break;
358 case STATE_SEND_QUERY:
359 rv = DoSendQuery(rv);
360 break;
361 case STATE_READ_LENGTH:
362 rv = DoReadLength(rv);
363 break;
364 case STATE_READ_LENGTH_COMPLETE:
365 rv = DoReadLengthComplete(rv);
366 break;
367 case STATE_READ_RESPONSE:
368 rv = DoReadResponse(rv);
369 break;
370 case STATE_READ_RESPONSE_COMPLETE:
371 rv = DoReadResponseComplete(rv);
372 break;
373 default:
374 NOTREACHED();
375 break;
377 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
379 set_result(rv);
380 if (rv == OK) {
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_);
388 return rv;
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);
398 if (rv < 0)
399 return rv;
401 uint16 query_size = static_cast<uint16>(query_->io_buffer()->size());
402 if (static_cast<int>(query_size) != query_->io_buffer()->size())
403 return ERR_FAILED;
404 base::WriteBigEndian<uint16>(length_buffer_->data(), query_size);
405 buffer_ =
406 new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
407 next_state_ = STATE_SEND_LENGTH;
408 return OK;
411 int DoSendLength(int rv) {
412 DCHECK_NE(ERR_IO_PENDING, rv);
413 if (rv < 0)
414 return rv;
416 buffer_->DidConsume(rv);
417 if (buffer_->BytesRemaining() > 0) {
418 next_state_ = STATE_SEND_LENGTH;
419 return socket_->Write(
420 buffer_.get(),
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;
427 return OK;
430 int DoSendQuery(int rv) {
431 DCHECK_NE(ERR_IO_PENDING, rv);
432 if (rv < 0)
433 return rv;
435 buffer_->DidConsume(rv);
436 if (buffer_->BytesRemaining() > 0) {
437 next_state_ = STATE_SEND_QUERY;
438 return socket_->Write(
439 buffer_.get(),
440 buffer_->BytesRemaining(),
441 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
443 buffer_ =
444 new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
445 next_state_ = STATE_READ_LENGTH;
446 return OK;
449 int DoReadLength(int rv) {
450 DCHECK_EQ(OK, rv);
452 next_state_ = STATE_READ_LENGTH_COMPLETE;
453 return ReadIntoBuffer();
456 int DoReadLengthComplete(int rv) {
457 DCHECK_NE(ERR_IO_PENDING, rv);
458 if (rv < 0)
459 return rv;
460 if (rv == 0)
461 return ERR_CONNECTION_CLOSED;
463 buffer_->DidConsume(rv);
464 if (buffer_->BytesRemaining() > 0) {
465 next_state_ = STATE_READ_LENGTH;
466 return OK;
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;
477 return OK;
480 int DoReadResponse(int rv) {
481 DCHECK_EQ(OK, rv);
483 next_state_ = STATE_READ_RESPONSE_COMPLETE;
484 return ReadIntoBuffer();
487 int DoReadResponseComplete(int rv) {
488 DCHECK_NE(ERR_IO_PENDING, rv);
489 if (rv < 0)
490 return rv;
491 if (rv == 0)
492 return ERR_CONNECTION_CLOSED;
494 buffer_->DidConsume(rv);
495 if (buffer_->BytesRemaining() > 0) {
496 next_state_ = STATE_READ_RESPONSE;
497 return OK;
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;
510 return OK;
513 void OnIOComplete(int rv) {
514 rv = DoLoop(rv);
515 if (rv != ERR_IO_PENDING)
516 callback_.Run(rv);
519 int ReadIntoBuffer() {
520 return socket_->Read(
521 buffer_.get(),
522 buffer_->BytesRemaining(),
523 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
526 State next_state_;
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> {
553 public:
554 DnsTransactionImpl(DnsSession* session,
555 const std::string& hostname,
556 uint16 qtype,
557 const DnsTransactionFactory::CallbackType& callback,
558 const BoundNetLog& net_log)
559 : session_(session),
560 hostname_(hostname),
561 qtype_(qtype),
562 callback_(callback),
563 net_log_(net_log),
564 qnames_initial_size_(0),
565 attempts_count_(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,
577 ERR_ABORTED);
578 } // otherwise logged in DoCallback or Start
581 const std::string& GetHostname() const override {
582 DCHECK(CalledOnValidThread());
583 return hostname_;
586 uint16 GetType() const override {
587 DCHECK(CalledOnValidThread());
588 return qtype_;
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(
607 FROM_HERE,
608 base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result));
612 private:
613 // Wrapper for the result of a DnsUDPAttempt.
614 struct AttemptResult {
615 AttemptResult(int rv, const DnsAttempt* attempt)
616 : rv(rv), attempt(attempt) {}
618 int rv;
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);
633 return OK;
636 int ndots = CountLabels(labeled_hostname) - 1;
638 if (ndots > 0 && !config.append_to_multi_label_name) {
639 qnames_.push_back(labeled_hostname);
640 return OK;
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);
648 had_hostname = true;
651 std::string qname;
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))
655 continue;
656 if (qname.size() == labeled_hostname.size()) {
657 if (had_hostname)
658 continue;
659 had_hostname = true;
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);
677 timer_.Stop();
678 RecordLostPacketsIfAny();
679 if (result.rv == OK)
680 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountSuccess", attempts_count_);
681 else
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_;
691 callback_.Reset();
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
698 // next nameserver.
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_));
706 } else {
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);
726 ++attempts_count_;
728 if (!got_socket)
729 return AttemptResult(ERR_CONNECTION_REFUSED, NULL);
731 net_log_.AddEvent(
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,
741 attempt_number);
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.
763 attempts_.clear();
765 unsigned attempt_number = attempts_.size();
767 DnsTCPAttempt* attempt = new DnsTCPAttempt(server_index, socket.Pass(),
768 query.Pass());
770 attempts_.push_back(attempt);
771 ++attempts_count_;
772 had_tcp_attempt_ = true;
774 net_log_.AddEvent(
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),
780 attempt_number));
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();
797 attempts_.clear();
798 had_tcp_attempt_ = false;
799 return MakeAttempt();
802 void OnUdpAttemptComplete(unsigned attempt_number,
803 base::TimeTicks start,
804 int rv) {
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())
816 return;
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)
821 DoCallback(result);
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();
829 ++first_completed) {
830 if (attempts_[first_completed]->is_completed())
831 break;
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())
836 return;
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())
845 continue;
846 session_->RecordLostPacket(server_index, server_attempt);
850 void LogResponse(const DnsAttempt* attempt) {
851 if (attempt && attempt->GetResponse()) {
852 net_log_.AddEvent(
853 NetLog::TYPE_DNS_TRANSACTION_RESPONSE,
854 base::Bind(&DnsAttempt::NetLogResponseCallback,
855 base::Unretained(attempt)));
859 bool MoreAttemptsAllowed() const {
860 if (had_tcp_attempt_)
861 return false;
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);
872 switch (result.rv) {
873 case OK:
874 session_->RecordServerSuccess(result.attempt->server_index());
875 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
876 result.rv);
877 DCHECK(result.attempt);
878 DCHECK(result.attempt->GetResponse());
879 return result;
880 case ERR_NAME_NOT_RESOLVED:
881 session_->RecordServerSuccess(result.attempt->server_index());
882 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
883 result.rv);
884 // Try next suffix.
885 qnames_.pop_front();
886 if (qnames_.empty()) {
887 return AttemptResult(ERR_NAME_NOT_RESOLVED, NULL);
888 } else {
889 result = StartQuery();
891 break;
892 case ERR_CONNECTION_REFUSED:
893 case ERR_DNS_TIMED_OUT:
894 if (result.attempt)
895 session_->RecordServerFailure(result.attempt->server_index());
896 if (MoreAttemptsAllowed()) {
897 result = MakeAttempt();
898 } else {
899 return result;
901 break;
902 case ERR_DNS_SERVER_REQUIRES_TCP:
903 result = MakeTCPAttempt(result.attempt);
904 break;
905 default:
906 // Server failure.
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 &&
916 !had_tcp_attempt_) {
917 // For UDP only, ignore the response and wait until the last attempt
918 // times out.
919 return AttemptResult(ERR_IO_PENDING, NULL);
920 } else {
921 return AttemptResult(result.rv, NULL);
923 break;
926 return result;
929 void OnTimeout() {
930 if (callback_.is_null())
931 return;
932 DCHECK(!attempts_.empty());
933 AttemptResult result = ProcessAttemptResult(
934 AttemptResult(ERR_DNS_TIMED_OUT, attempts_.back()));
935 if (result.rv != ERR_IO_PENDING)
936 DoCallback(result);
939 scoped_refptr<DnsSession> session_;
940 std::string hostname_;
941 uint16 qtype_;
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.
954 int attempts_count_;
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 {
970 public:
971 explicit DnsTransactionFactoryImpl(DnsSession* session) {
972 session_ = session;
975 scoped_ptr<DnsTransaction> CreateTransaction(
976 const std::string& hostname,
977 uint16 qtype,
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));
984 private:
985 scoped_refptr<DnsSession> session_;
988 } // namespace
990 // static
991 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory(
992 DnsSession* session) {
993 return scoped_ptr<DnsTransactionFactory>(
994 new DnsTransactionFactoryImpl(session));
997 } // namespace net