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