Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / net / dns / dns_transaction.cc
blob0424d48b048085ea68676c4a5e5a66c54e2fe6c9
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/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"
38 namespace net {
40 namespace {
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) {
49 size_t count = 0;
50 for (size_t i = 0; i < name.size() && name[i]; i += name[i] + 1)
51 ++count;
52 return count;
55 bool IsIPLiteral(const std::string& hostname) {
56 IPAddressNumber ip;
57 return ParseIPLiteralToNumber(hostname, &ip);
60 base::Value* NetLogStartCallback(const std::string* hostname,
61 uint16 qtype,
62 NetLog::LogLevel /* log_level */) {
63 base::DictionaryValue* dict = new base::DictionaryValue();
64 dict->SetString("hostname", *hostname);
65 dict->SetInteger("query_type", qtype);
66 return dict;
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.
74 class DnsAttempt {
75 public:
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
88 // the server.
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);
107 return dict;
110 void set_result(int result) {
111 result_ = 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);
125 private:
126 // Result of last operation.
127 int result_;
129 const unsigned server_index_;
132 class DnsUDPAttempt : public DnsAttempt {
133 public:
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()) {}
143 // DnsAttempt:
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;
149 return DoLoop(OK);
152 virtual const DnsQuery* GetQuery() const OVERRIDE {
153 return query_.get();
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();
165 private:
166 enum State {
167 STATE_SEND_QUERY,
168 STATE_SEND_QUERY_COMPLETE,
169 STATE_READ_RESPONSE,
170 STATE_READ_RESPONSE_COMPLETE,
171 STATE_NONE,
174 DatagramClientSocket* socket() {
175 return socket_lease_->socket();
178 int DoLoop(int result) {
179 CHECK_NE(STATE_NONE, next_state_);
180 int rv = result;
181 do {
182 State state = next_state_;
183 next_state_ = STATE_NONE;
184 switch (state) {
185 case STATE_SEND_QUERY:
186 rv = DoSendQuery();
187 break;
188 case STATE_SEND_QUERY_COMPLETE:
189 rv = DoSendQueryComplete(rv);
190 break;
191 case STATE_READ_RESPONSE:
192 rv = DoReadResponse();
193 break;
194 case STATE_READ_RESPONSE_COMPLETE:
195 rv = DoReadResponseComplete(rv);
196 break;
197 default:
198 NOTREACHED();
199 break;
201 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
203 set_result(rv);
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;
208 if (rv == OK) {
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_);
216 return rv;
219 int DoSendQuery() {
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);
229 if (rv < 0)
230 return 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;
237 return OK;
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);
251 if (rv < 0)
252 return rv;
254 DCHECK(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;
263 return OK;
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;
273 return OK;
276 void OnIOComplete(int rv) {
277 rv = DoLoop(rv);
278 if (rv != ERR_IO_PENDING)
279 callback_.Run(rv);
282 State next_state_;
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 {
297 public:
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) {}
308 // DnsAttempt:
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) {
317 set_result(rv);
318 return rv;
320 return DoLoop(rv);
323 virtual const DnsQuery* GetQuery() const OVERRIDE {
324 return query_.get();
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();
336 private:
337 enum State {
338 STATE_CONNECT_COMPLETE,
339 STATE_SEND_LENGTH,
340 STATE_SEND_QUERY,
341 STATE_READ_LENGTH,
342 STATE_READ_LENGTH_COMPLETE,
343 STATE_READ_RESPONSE,
344 STATE_READ_RESPONSE_COMPLETE,
345 STATE_NONE,
348 int DoLoop(int result) {
349 CHECK_NE(STATE_NONE, next_state_);
350 int rv = result;
351 do {
352 State state = next_state_;
353 next_state_ = STATE_NONE;
354 switch (state) {
355 case STATE_CONNECT_COMPLETE:
356 rv = DoConnectComplete(rv);
357 break;
358 case STATE_SEND_LENGTH:
359 rv = DoSendLength(rv);
360 break;
361 case STATE_SEND_QUERY:
362 rv = DoSendQuery(rv);
363 break;
364 case STATE_READ_LENGTH:
365 rv = DoReadLength(rv);
366 break;
367 case STATE_READ_LENGTH_COMPLETE:
368 rv = DoReadLengthComplete(rv);
369 break;
370 case STATE_READ_RESPONSE:
371 rv = DoReadResponse(rv);
372 break;
373 case STATE_READ_RESPONSE_COMPLETE:
374 rv = DoReadResponseComplete(rv);
375 break;
376 default:
377 NOTREACHED();
378 break;
380 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
382 set_result(rv);
383 if (rv == OK) {
384 DCHECK_EQ(STATE_NONE, next_state_);
385 DNS_HISTOGRAM("AsyncDNS.TCPAttemptSuccess",
386 base::TimeTicks::Now() - start_time_);
387 } else if (rv != ERR_IO_PENDING) {
388 DNS_HISTOGRAM("AsyncDNS.TCPAttemptFail",
389 base::TimeTicks::Now() - start_time_);
391 return rv;
394 int DoConnectComplete(int rv) {
395 DCHECK_NE(ERR_IO_PENDING, rv);
396 if (rv < 0)
397 return rv;
399 base::WriteBigEndian<uint16>(length_buffer_->data(),
400 query_->io_buffer()->size());
401 buffer_ =
402 new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
403 next_state_ = STATE_SEND_LENGTH;
404 return OK;
407 int DoSendLength(int rv) {
408 DCHECK_NE(ERR_IO_PENDING, rv);
409 if (rv < 0)
410 return rv;
412 buffer_->DidConsume(rv);
413 if (buffer_->BytesRemaining() > 0) {
414 next_state_ = STATE_SEND_LENGTH;
415 return socket_->Write(
416 buffer_.get(),
417 buffer_->BytesRemaining(),
418 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
420 buffer_ = new DrainableIOBuffer(query_->io_buffer(),
421 query_->io_buffer()->size());
422 next_state_ = STATE_SEND_QUERY;
423 return OK;
426 int DoSendQuery(int rv) {
427 DCHECK_NE(ERR_IO_PENDING, rv);
428 if (rv < 0)
429 return rv;
431 buffer_->DidConsume(rv);
432 if (buffer_->BytesRemaining() > 0) {
433 next_state_ = STATE_SEND_QUERY;
434 return socket_->Write(
435 buffer_.get(),
436 buffer_->BytesRemaining(),
437 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
439 buffer_ =
440 new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
441 next_state_ = STATE_READ_LENGTH;
442 return OK;
445 int DoReadLength(int rv) {
446 DCHECK_EQ(OK, rv);
448 next_state_ = STATE_READ_LENGTH_COMPLETE;
449 return ReadIntoBuffer();
452 int DoReadLengthComplete(int rv) {
453 DCHECK_NE(ERR_IO_PENDING, rv);
454 if (rv < 0)
455 return rv;
456 if (rv == 0)
457 return ERR_CONNECTION_CLOSED;
459 buffer_->DidConsume(rv);
460 if (buffer_->BytesRemaining() > 0) {
461 next_state_ = STATE_READ_LENGTH;
462 return OK;
465 base::ReadBigEndian<uint16>(length_buffer_->data(), &response_length_);
466 // Check if advertised response is too short. (Optimization only.)
467 if (response_length_ < query_->io_buffer()->size())
468 return ERR_DNS_MALFORMED_RESPONSE;
469 // Allocate more space so that DnsResponse::InitParse sanity check passes.
470 response_.reset(new DnsResponse(response_length_ + 1));
471 buffer_ = new DrainableIOBuffer(response_->io_buffer(), response_length_);
472 next_state_ = STATE_READ_RESPONSE;
473 return OK;
476 int DoReadResponse(int rv) {
477 DCHECK_EQ(OK, rv);
479 next_state_ = STATE_READ_RESPONSE_COMPLETE;
480 return ReadIntoBuffer();
483 int DoReadResponseComplete(int rv) {
484 DCHECK_NE(ERR_IO_PENDING, rv);
485 if (rv < 0)
486 return rv;
487 if (rv == 0)
488 return ERR_CONNECTION_CLOSED;
490 buffer_->DidConsume(rv);
491 if (buffer_->BytesRemaining() > 0) {
492 next_state_ = STATE_READ_RESPONSE;
493 return OK;
496 if (!response_->InitParse(buffer_->BytesConsumed(), *query_))
497 return ERR_DNS_MALFORMED_RESPONSE;
498 if (response_->flags() & dns_protocol::kFlagTC)
499 return ERR_UNEXPECTED;
500 // TODO(szym): Frankly, none of these are expected.
501 if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
502 return ERR_NAME_NOT_RESOLVED;
503 if (response_->rcode() != dns_protocol::kRcodeNOERROR)
504 return ERR_DNS_SERVER_FAILED;
506 return OK;
509 void OnIOComplete(int rv) {
510 rv = DoLoop(rv);
511 if (rv != ERR_IO_PENDING)
512 callback_.Run(rv);
515 int ReadIntoBuffer() {
516 return socket_->Read(
517 buffer_.get(),
518 buffer_->BytesRemaining(),
519 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
522 State next_state_;
523 base::TimeTicks start_time_;
525 scoped_ptr<StreamSocket> socket_;
526 scoped_ptr<DnsQuery> query_;
527 scoped_refptr<IOBufferWithSize> length_buffer_;
528 scoped_refptr<DrainableIOBuffer> buffer_;
530 uint16 response_length_;
531 scoped_ptr<DnsResponse> response_;
533 CompletionCallback callback_;
535 DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt);
538 // ----------------------------------------------------------------------------
540 // Implements DnsTransaction. Configuration is supplied by DnsSession.
541 // The suffix list is built according to the DnsConfig from the session.
542 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout.
543 // The first server to attempt on each query is given by
544 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards.
545 // Each server is attempted DnsConfig::attempts times.
546 class DnsTransactionImpl : public DnsTransaction,
547 public base::NonThreadSafe,
548 public base::SupportsWeakPtr<DnsTransactionImpl> {
549 public:
550 DnsTransactionImpl(DnsSession* session,
551 const std::string& hostname,
552 uint16 qtype,
553 const DnsTransactionFactory::CallbackType& callback,
554 const BoundNetLog& net_log)
555 : session_(session),
556 hostname_(hostname),
557 qtype_(qtype),
558 callback_(callback),
559 net_log_(net_log),
560 qnames_initial_size_(0),
561 attempts_count_(0),
562 had_tcp_attempt_(false),
563 first_server_index_(0) {
564 DCHECK(session_.get());
565 DCHECK(!hostname_.empty());
566 DCHECK(!callback_.is_null());
567 DCHECK(!IsIPLiteral(hostname_));
570 virtual ~DnsTransactionImpl() {
571 if (!callback_.is_null()) {
572 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION,
573 ERR_ABORTED);
574 } // otherwise logged in DoCallback or Start
577 virtual const std::string& GetHostname() const OVERRIDE {
578 DCHECK(CalledOnValidThread());
579 return hostname_;
582 virtual uint16 GetType() const OVERRIDE {
583 DCHECK(CalledOnValidThread());
584 return qtype_;
587 virtual void Start() OVERRIDE {
588 DCHECK(!callback_.is_null());
589 DCHECK(attempts_.empty());
590 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION,
591 base::Bind(&NetLogStartCallback, &hostname_, qtype_));
592 AttemptResult result(PrepareSearch(), NULL);
593 if (result.rv == OK) {
594 qnames_initial_size_ = qnames_.size();
595 if (qtype_ == dns_protocol::kTypeA)
596 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchStart", qnames_.size());
597 result = ProcessAttemptResult(StartQuery());
600 // Must always return result asynchronously, to avoid reentrancy.
601 if (result.rv != ERR_IO_PENDING) {
602 base::MessageLoop::current()->PostTask(
603 FROM_HERE,
604 base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result));
608 private:
609 // Wrapper for the result of a DnsUDPAttempt.
610 struct AttemptResult {
611 AttemptResult(int rv, const DnsAttempt* attempt)
612 : rv(rv), attempt(attempt) {}
614 int rv;
615 const DnsAttempt* attempt;
618 // Prepares |qnames_| according to the DnsConfig.
619 int PrepareSearch() {
620 const DnsConfig& config = session_->config();
622 std::string labeled_hostname;
623 if (!DNSDomainFromDot(hostname_, &labeled_hostname))
624 return ERR_INVALID_ARGUMENT;
626 if (hostname_[hostname_.size() - 1] == '.') {
627 // It's a fully-qualified name, no suffix search.
628 qnames_.push_back(labeled_hostname);
629 return OK;
632 int ndots = CountLabels(labeled_hostname) - 1;
634 if (ndots > 0 && !config.append_to_multi_label_name) {
635 qnames_.push_back(labeled_hostname);
636 return OK;
639 // Set true when |labeled_hostname| is put on the list.
640 bool had_hostname = false;
642 if (ndots >= config.ndots) {
643 qnames_.push_back(labeled_hostname);
644 had_hostname = true;
647 std::string qname;
648 for (size_t i = 0; i < config.search.size(); ++i) {
649 // Ignore invalid (too long) combinations.
650 if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname))
651 continue;
652 if (qname.size() == labeled_hostname.size()) {
653 if (had_hostname)
654 continue;
655 had_hostname = true;
657 qnames_.push_back(qname);
660 if (ndots > 0 && !had_hostname)
661 qnames_.push_back(labeled_hostname);
663 return qnames_.empty() ? ERR_DNS_SEARCH_EMPTY : OK;
666 void DoCallback(AttemptResult result) {
667 DCHECK(!callback_.is_null());
668 DCHECK_NE(ERR_IO_PENDING, result.rv);
669 const DnsResponse* response = result.attempt ?
670 result.attempt->GetResponse() : NULL;
671 CHECK(result.rv != OK || response != NULL);
673 timer_.Stop();
674 RecordLostPacketsIfAny();
675 if (result.rv == OK)
676 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountSuccess", attempts_count_);
677 else
678 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountFail", attempts_count_);
680 if (response && qtype_ == dns_protocol::kTypeA) {
681 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchRemain", qnames_.size());
682 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchDone",
683 qnames_initial_size_ - qnames_.size());
686 DnsTransactionFactory::CallbackType callback = callback_;
687 callback_.Reset();
689 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, result.rv);
690 callback.Run(this, result.rv, response);
693 // Makes another attempt at the current name, |qnames_.front()|, using the
694 // next nameserver.
695 AttemptResult MakeAttempt() {
696 unsigned attempt_number = attempts_.size();
698 uint16 id = session_->NextQueryId();
699 scoped_ptr<DnsQuery> query;
700 if (attempts_.empty()) {
701 query.reset(new DnsQuery(id, qnames_.front(), qtype_));
702 } else {
703 query.reset(attempts_[0]->GetQuery()->CloneWithNewId(id));
706 const DnsConfig& config = session_->config();
708 unsigned server_index =
709 (first_server_index_ + attempt_number) % config.nameservers.size();
710 // Skip over known failed servers.
711 server_index = session_->NextGoodServerIndex(server_index);
713 scoped_ptr<DnsSession::SocketLease> lease =
714 session_->AllocateSocket(server_index, net_log_.source());
716 bool got_socket = !!lease.get();
718 DnsUDPAttempt* attempt =
719 new DnsUDPAttempt(server_index, lease.Pass(), query.Pass());
721 attempts_.push_back(attempt);
722 ++attempts_count_;
724 if (!got_socket)
725 return AttemptResult(ERR_CONNECTION_REFUSED, NULL);
727 net_log_.AddEvent(
728 NetLog::TYPE_DNS_TRANSACTION_ATTEMPT,
729 attempt->GetSocketNetLog().source().ToEventParametersCallback());
731 int rv = attempt->Start(
732 base::Bind(&DnsTransactionImpl::OnUdpAttemptComplete,
733 base::Unretained(this), attempt_number,
734 base::TimeTicks::Now()));
735 if (rv == ERR_IO_PENDING) {
736 base::TimeDelta timeout = session_->NextTimeout(server_index,
737 attempt_number);
738 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
740 return AttemptResult(rv, attempt);
743 AttemptResult MakeTCPAttempt(const DnsAttempt* previous_attempt) {
744 DCHECK(previous_attempt);
745 DCHECK(!had_tcp_attempt_);
747 unsigned server_index = previous_attempt->server_index();
749 scoped_ptr<StreamSocket> socket(
750 session_->CreateTCPSocket(server_index, net_log_.source()));
752 // TODO(szym): Reuse the same id to help the server?
753 uint16 id = session_->NextQueryId();
754 scoped_ptr<DnsQuery> query(
755 previous_attempt->GetQuery()->CloneWithNewId(id));
757 RecordLostPacketsIfAny();
758 // Cancel all other attempts, no point waiting on them.
759 attempts_.clear();
761 unsigned attempt_number = attempts_.size();
763 DnsTCPAttempt* attempt = new DnsTCPAttempt(server_index, socket.Pass(),
764 query.Pass());
766 attempts_.push_back(attempt);
767 ++attempts_count_;
768 had_tcp_attempt_ = true;
770 net_log_.AddEvent(
771 NetLog::TYPE_DNS_TRANSACTION_TCP_ATTEMPT,
772 attempt->GetSocketNetLog().source().ToEventParametersCallback());
774 int rv = attempt->Start(base::Bind(&DnsTransactionImpl::OnAttemptComplete,
775 base::Unretained(this),
776 attempt_number));
777 if (rv == ERR_IO_PENDING) {
778 // Custom timeout for TCP attempt.
779 base::TimeDelta timeout = timer_.GetCurrentDelay() * 2;
780 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
782 return AttemptResult(rv, attempt);
785 // Begins query for the current name. Makes the first attempt.
786 AttemptResult StartQuery() {
787 std::string dotted_qname = DNSDomainToString(qnames_.front());
788 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION_QUERY,
789 NetLog::StringCallback("qname", &dotted_qname));
791 first_server_index_ = session_->NextFirstServerIndex();
792 RecordLostPacketsIfAny();
793 attempts_.clear();
794 had_tcp_attempt_ = false;
795 return MakeAttempt();
798 void OnUdpAttemptComplete(unsigned attempt_number,
799 base::TimeTicks start,
800 int rv) {
801 DCHECK_LT(attempt_number, attempts_.size());
802 const DnsAttempt* attempt = attempts_[attempt_number];
803 if (attempt->GetResponse()) {
804 session_->RecordRTT(attempt->server_index(),
805 base::TimeTicks::Now() - start);
807 OnAttemptComplete(attempt_number, rv);
810 void OnAttemptComplete(unsigned attempt_number, int rv) {
811 if (callback_.is_null())
812 return;
813 DCHECK_LT(attempt_number, attempts_.size());
814 const DnsAttempt* attempt = attempts_[attempt_number];
815 AttemptResult result = ProcessAttemptResult(AttemptResult(rv, attempt));
816 if (result.rv != ERR_IO_PENDING)
817 DoCallback(result);
820 // Record packet loss for any incomplete attempts.
821 void RecordLostPacketsIfAny() {
822 // Loop through attempts until we find first that is completed
823 size_t first_completed = 0;
824 for (first_completed = 0; first_completed < attempts_.size();
825 ++first_completed) {
826 if (attempts_[first_completed]->is_completed())
827 break;
829 // If there were no completed attempts, then we must be offline, so don't
830 // record any attempts as lost packets.
831 if (first_completed == attempts_.size())
832 return;
834 size_t num_servers = session_->config().nameservers.size();
835 std::vector<int> server_attempts(num_servers);
836 for (size_t i = 0; i < first_completed; ++i) {
837 unsigned server_index = attempts_[i]->server_index();
838 int server_attempt = server_attempts[server_index]++;
839 // Don't record lost packet unless attempt is in pending state.
840 if (!attempts_[i]->is_pending())
841 continue;
842 session_->RecordLostPacket(server_index, server_attempt);
846 void LogResponse(const DnsAttempt* attempt) {
847 if (attempt && attempt->GetResponse()) {
848 net_log_.AddEvent(
849 NetLog::TYPE_DNS_TRANSACTION_RESPONSE,
850 base::Bind(&DnsAttempt::NetLogResponseCallback,
851 base::Unretained(attempt)));
855 bool MoreAttemptsAllowed() const {
856 if (had_tcp_attempt_)
857 return false;
858 const DnsConfig& config = session_->config();
859 return attempts_.size() < config.attempts * config.nameservers.size();
862 // Resolves the result of a DnsAttempt until a terminal result is reached
863 // or it will complete asynchronously (ERR_IO_PENDING).
864 AttemptResult ProcessAttemptResult(AttemptResult result) {
865 while (result.rv != ERR_IO_PENDING) {
866 LogResponse(result.attempt);
868 switch (result.rv) {
869 case OK:
870 session_->RecordServerSuccess(result.attempt->server_index());
871 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
872 result.rv);
873 DCHECK(result.attempt);
874 DCHECK(result.attempt->GetResponse());
875 return result;
876 case ERR_NAME_NOT_RESOLVED:
877 session_->RecordServerSuccess(result.attempt->server_index());
878 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
879 result.rv);
880 // Try next suffix.
881 qnames_.pop_front();
882 if (qnames_.empty()) {
883 return AttemptResult(ERR_NAME_NOT_RESOLVED, NULL);
884 } else {
885 result = StartQuery();
887 break;
888 case ERR_CONNECTION_REFUSED:
889 case ERR_DNS_TIMED_OUT:
890 if (result.attempt)
891 session_->RecordServerFailure(result.attempt->server_index());
892 if (MoreAttemptsAllowed()) {
893 result = MakeAttempt();
894 } else {
895 return result;
897 break;
898 case ERR_DNS_SERVER_REQUIRES_TCP:
899 result = MakeTCPAttempt(result.attempt);
900 break;
901 default:
902 // Server failure.
903 DCHECK(result.attempt);
904 if (result.attempt != attempts_.back()) {
905 // This attempt already timed out. Ignore it.
906 session_->RecordServerFailure(result.attempt->server_index());
907 return AttemptResult(ERR_IO_PENDING, NULL);
909 if (MoreAttemptsAllowed()) {
910 result = MakeAttempt();
911 } else if (result.rv == ERR_DNS_MALFORMED_RESPONSE &&
912 !had_tcp_attempt_) {
913 // For UDP only, ignore the response and wait until the last attempt
914 // times out.
915 return AttemptResult(ERR_IO_PENDING, NULL);
916 } else {
917 return AttemptResult(result.rv, NULL);
919 break;
922 return result;
925 void OnTimeout() {
926 if (callback_.is_null())
927 return;
928 DCHECK(!attempts_.empty());
929 AttemptResult result = ProcessAttemptResult(
930 AttemptResult(ERR_DNS_TIMED_OUT, attempts_.back()));
931 if (result.rv != ERR_IO_PENDING)
932 DoCallback(result);
935 scoped_refptr<DnsSession> session_;
936 std::string hostname_;
937 uint16 qtype_;
938 // Cleared in DoCallback.
939 DnsTransactionFactory::CallbackType callback_;
941 BoundNetLog net_log_;
943 // Search list of fully-qualified DNS names to query next (in DNS format).
944 std::deque<std::string> qnames_;
945 size_t qnames_initial_size_;
947 // List of attempts for the current name.
948 ScopedVector<DnsAttempt> attempts_;
949 // Count of attempts, not reset when |attempts_| vector is cleared.
950 int attempts_count_;
951 bool had_tcp_attempt_;
953 // Index of the first server to try on each search query.
954 int first_server_index_;
956 base::OneShotTimer<DnsTransactionImpl> timer_;
958 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl);
961 // ----------------------------------------------------------------------------
963 // Implementation of DnsTransactionFactory that returns instances of
964 // DnsTransactionImpl.
965 class DnsTransactionFactoryImpl : public DnsTransactionFactory {
966 public:
967 explicit DnsTransactionFactoryImpl(DnsSession* session) {
968 session_ = session;
971 virtual scoped_ptr<DnsTransaction> CreateTransaction(
972 const std::string& hostname,
973 uint16 qtype,
974 const CallbackType& callback,
975 const BoundNetLog& net_log) OVERRIDE {
976 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(
977 session_.get(), hostname, qtype, callback, net_log));
980 private:
981 scoped_refptr<DnsSession> session_;
984 } // namespace
986 // static
987 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory(
988 DnsSession* session) {
989 return scoped_ptr<DnsTransactionFactory>(
990 new DnsTransactionFactoryImpl(session));
993 } // namespace net