In iossim, ignore harmless messages from launchd.
[chromium-blink-merge.git] / net / dns / dns_transaction.cc
blob545cce22a8b7ab610151cc820e90305ae0c6e715
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/bind.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/message_loop.h"
17 #include "base/metrics/histogram.h"
18 #include "base/rand_util.h"
19 #include "base/stl_util.h"
20 #include "base/string_piece.h"
21 #include "base/threading/non_thread_safe.h"
22 #include "base/timer.h"
23 #include "base/values.h"
24 #include "net/base/big_endian.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 Value* NetLogStartCallback(const std::string* hostname,
61 uint16 qtype,
62 NetLog::LogLevel /* log_level */) {
63 DictionaryValue* dict = new 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 virtual ~DnsAttempt() {}
77 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously
78 // and calls |callback| upon completion.
79 virtual int Start(const CompletionCallback& callback) = 0;
81 // Returns the query of this attempt.
82 virtual const DnsQuery* GetQuery() const = 0;
84 // Returns the response or NULL if has not received a matching response from
85 // the server.
86 virtual const DnsResponse* GetResponse() const = 0;
88 // Returns the net log bound to the source of the socket.
89 virtual const BoundNetLog& GetSocketNetLog() const = 0;
91 // Returns the index of the destination server within DnsConfig::nameservers.
92 virtual unsigned GetServerIndex() const = 0;
94 // Returns a Value representing the received response, along with a reference
95 // to the NetLog source source of the UDP socket used. The request must have
96 // completed before this is called.
97 Value* NetLogResponseCallback(NetLog::LogLevel log_level) const {
98 DCHECK(GetResponse()->IsValid());
100 DictionaryValue* dict = new DictionaryValue();
101 dict->SetInteger("rcode", GetResponse()->rcode());
102 dict->SetInteger("answer_count", GetResponse()->answer_count());
103 GetSocketNetLog().source().AddToEventParameters(dict);
104 return dict;
108 class DnsUDPAttempt : public DnsAttempt {
109 public:
110 DnsUDPAttempt(scoped_ptr<DnsSession::SocketLease> socket_lease,
111 scoped_ptr<DnsQuery> query)
112 : next_state_(STATE_NONE),
113 received_malformed_response_(false),
114 socket_lease_(socket_lease.Pass()),
115 query_(query.Pass()) {
118 // DnsAttempt:
119 virtual int Start(const CompletionCallback& callback) OVERRIDE {
120 DCHECK_EQ(STATE_NONE, next_state_);
121 callback_ = callback;
122 start_time_ = base::TimeTicks::Now();
123 next_state_ = STATE_SEND_QUERY;
124 return DoLoop(OK);
127 virtual const DnsQuery* GetQuery() const OVERRIDE {
128 return query_.get();
131 virtual const DnsResponse* GetResponse() const OVERRIDE {
132 const DnsResponse* resp = response_.get();
133 return (resp != NULL && resp->IsValid()) ? resp : NULL;
136 virtual const BoundNetLog& GetSocketNetLog() const OVERRIDE {
137 return socket_lease_->socket()->NetLog();
140 virtual unsigned GetServerIndex() const OVERRIDE {
141 return socket_lease_->server_index();
144 private:
145 enum State {
146 STATE_SEND_QUERY,
147 STATE_SEND_QUERY_COMPLETE,
148 STATE_READ_RESPONSE,
149 STATE_READ_RESPONSE_COMPLETE,
150 STATE_NONE,
153 DatagramClientSocket* socket() {
154 return socket_lease_->socket();
157 int DoLoop(int result) {
158 CHECK_NE(STATE_NONE, next_state_);
159 int rv = result;
160 do {
161 State state = next_state_;
162 next_state_ = STATE_NONE;
163 switch (state) {
164 case STATE_SEND_QUERY:
165 rv = DoSendQuery();
166 break;
167 case STATE_SEND_QUERY_COMPLETE:
168 rv = DoSendQueryComplete(rv);
169 break;
170 case STATE_READ_RESPONSE:
171 rv = DoReadResponse();
172 break;
173 case STATE_READ_RESPONSE_COMPLETE:
174 rv = DoReadResponseComplete(rv);
175 break;
176 default:
177 NOTREACHED();
178 break;
180 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
181 // If we received a malformed response, and are now waiting for another one,
182 // indicate to the transaction that the server might be misbehaving.
183 if (rv == ERR_IO_PENDING && received_malformed_response_)
184 return ERR_DNS_MALFORMED_RESPONSE;
185 if (rv == OK) {
186 DCHECK_EQ(STATE_NONE, next_state_);
187 DNS_HISTOGRAM("AsyncDNS.UDPAttemptSuccess",
188 base::TimeTicks::Now() - start_time_);
189 } else if (rv != ERR_IO_PENDING) {
190 DNS_HISTOGRAM("AsyncDNS.UDPAttemptFail",
191 base::TimeTicks::Now() - start_time_);
193 return rv;
196 int DoSendQuery() {
197 next_state_ = STATE_SEND_QUERY_COMPLETE;
198 return socket()->Write(query_->io_buffer(),
199 query_->io_buffer()->size(),
200 base::Bind(&DnsUDPAttempt::OnIOComplete,
201 base::Unretained(this)));
204 int DoSendQueryComplete(int rv) {
205 DCHECK_NE(ERR_IO_PENDING, rv);
206 if (rv < 0)
207 return rv;
209 // Writing to UDP should not result in a partial datagram.
210 if (rv != query_->io_buffer()->size())
211 return ERR_MSG_TOO_BIG;
213 next_state_ = STATE_READ_RESPONSE;
214 return OK;
217 int DoReadResponse() {
218 next_state_ = STATE_READ_RESPONSE_COMPLETE;
219 response_.reset(new DnsResponse());
220 return socket()->Read(response_->io_buffer(),
221 response_->io_buffer()->size(),
222 base::Bind(&DnsUDPAttempt::OnIOComplete,
223 base::Unretained(this)));
226 int DoReadResponseComplete(int rv) {
227 DCHECK_NE(ERR_IO_PENDING, rv);
228 if (rv < 0)
229 return rv;
231 DCHECK(rv);
232 if (!response_->InitParse(rv, *query_)) {
233 // Other implementations simply ignore mismatched responses. Since each
234 // DnsUDPAttempt binds to a different port, we might find that responses
235 // to previously timed out queries lead to failures in the future.
236 // Our solution is to make another attempt, in case the query truly
237 // failed, but keep this attempt alive, in case it was a false alarm.
238 received_malformed_response_ = true;
239 next_state_ = STATE_READ_RESPONSE;
240 return OK;
242 if (response_->flags() & dns_protocol::kFlagTC)
243 return ERR_DNS_SERVER_REQUIRES_TCP;
244 // TODO(szym): Extract TTL for NXDOMAIN results. http://crbug.com/115051
245 if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
246 return ERR_NAME_NOT_RESOLVED;
247 if (response_->rcode() != dns_protocol::kRcodeNOERROR)
248 return ERR_DNS_SERVER_FAILED;
250 return OK;
253 void OnIOComplete(int rv) {
254 rv = DoLoop(rv);
255 if (rv != ERR_IO_PENDING)
256 callback_.Run(rv);
259 State next_state_;
260 bool received_malformed_response_;
261 base::TimeTicks start_time_;
263 scoped_ptr<DnsSession::SocketLease> socket_lease_;
264 scoped_ptr<DnsQuery> query_;
266 scoped_ptr<DnsResponse> response_;
268 CompletionCallback callback_;
270 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt);
273 class DnsTCPAttempt : public DnsAttempt {
274 public:
275 DnsTCPAttempt(scoped_ptr<StreamSocket> socket,
276 scoped_ptr<DnsQuery> query)
277 : next_state_(STATE_NONE),
278 socket_(socket.Pass()),
279 query_(query.Pass()),
280 length_buffer_(new IOBufferWithSize(sizeof(uint16))),
281 response_length_(0) {
284 // DnsAttempt:
285 virtual int Start(const CompletionCallback& callback) OVERRIDE {
286 DCHECK_EQ(STATE_NONE, next_state_);
287 callback_ = callback;
288 start_time_ = base::TimeTicks::Now();
289 next_state_ = STATE_CONNECT_COMPLETE;
290 int rv = socket_->Connect(base::Bind(&DnsTCPAttempt::OnIOComplete,
291 base::Unretained(this)));
292 if (rv == ERR_IO_PENDING)
293 return rv;
294 return DoLoop(rv);
297 virtual const DnsQuery* GetQuery() const OVERRIDE {
298 return query_.get();
301 virtual const DnsResponse* GetResponse() const OVERRIDE {
302 const DnsResponse* resp = response_.get();
303 return (resp != NULL && resp->IsValid()) ? resp : NULL;
306 virtual const BoundNetLog& GetSocketNetLog() const OVERRIDE {
307 return socket_->NetLog();
310 virtual unsigned GetServerIndex() const OVERRIDE {
311 NOTREACHED();
312 return 0;
315 private:
316 enum State {
317 STATE_CONNECT_COMPLETE,
318 STATE_SEND_LENGTH,
319 STATE_SEND_QUERY,
320 STATE_READ_LENGTH,
321 STATE_READ_RESPONSE,
322 STATE_NONE,
325 int DoLoop(int result) {
326 CHECK_NE(STATE_NONE, next_state_);
327 int rv = result;
328 do {
329 State state = next_state_;
330 next_state_ = STATE_NONE;
331 switch (state) {
332 case STATE_CONNECT_COMPLETE:
333 rv = DoConnectComplete(rv);
334 break;
335 case STATE_SEND_LENGTH:
336 rv = DoSendLength(rv);
337 break;
338 case STATE_SEND_QUERY:
339 rv = DoSendQuery(rv);
340 break;
341 case STATE_READ_LENGTH:
342 rv = DoReadLength(rv);
343 break;
344 case STATE_READ_RESPONSE:
345 rv = DoReadResponse(rv);
346 break;
347 default:
348 NOTREACHED();
349 break;
351 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
352 if (rv == OK) {
353 DCHECK_EQ(STATE_NONE, next_state_);
354 DNS_HISTOGRAM("AsyncDNS.TCPAttemptSuccess",
355 base::TimeTicks::Now() - start_time_);
356 } else if (rv != ERR_IO_PENDING) {
357 DNS_HISTOGRAM("AsyncDNS.TCPAttemptFail",
358 base::TimeTicks::Now() - start_time_);
360 return rv;
363 int DoConnectComplete(int rv) {
364 DCHECK_NE(ERR_IO_PENDING, rv);
365 if (rv < 0)
366 return rv;
368 WriteBigEndian<uint16>(length_buffer_->data(), query_->io_buffer()->size());
369 buffer_ = new DrainableIOBuffer(length_buffer_, length_buffer_->size());
370 next_state_ = STATE_SEND_LENGTH;
371 return OK;
374 int DoSendLength(int rv) {
375 DCHECK_NE(ERR_IO_PENDING, rv);
376 if (rv < 0)
377 return rv;
379 buffer_->DidConsume(rv);
380 if (buffer_->BytesRemaining() > 0) {
381 next_state_ = STATE_SEND_LENGTH;
382 return socket_->Write(buffer_,
383 buffer_->BytesRemaining(),
384 base::Bind(&DnsTCPAttempt::OnIOComplete,
385 base::Unretained(this)));
387 buffer_ = new DrainableIOBuffer(query_->io_buffer(),
388 query_->io_buffer()->size());
389 next_state_ = STATE_SEND_QUERY;
390 return OK;
393 int DoSendQuery(int rv) {
394 DCHECK_NE(ERR_IO_PENDING, rv);
395 if (rv < 0)
396 return rv;
398 buffer_->DidConsume(rv);
399 if (buffer_->BytesRemaining() > 0) {
400 next_state_ = STATE_SEND_QUERY;
401 return socket_->Write(buffer_,
402 buffer_->BytesRemaining(),
403 base::Bind(&DnsTCPAttempt::OnIOComplete,
404 base::Unretained(this)));
406 buffer_ = new DrainableIOBuffer(length_buffer_, length_buffer_->size());
407 next_state_ = STATE_READ_LENGTH;
408 return OK;
411 int DoReadLength(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_READ_LENGTH;
419 return socket_->Read(buffer_,
420 buffer_->BytesRemaining(),
421 base::Bind(&DnsTCPAttempt::OnIOComplete,
422 base::Unretained(this)));
424 ReadBigEndian<uint16>(length_buffer_->data(), &response_length_);
425 // Check if advertised response is too short. (Optimization only.)
426 if (response_length_ < query_->io_buffer()->size())
427 return ERR_DNS_MALFORMED_RESPONSE;
428 // Allocate more space so that DnsResponse::InitParse sanity check passes.
429 response_.reset(new DnsResponse(response_length_ + 1));
430 buffer_ = new DrainableIOBuffer(response_->io_buffer(), response_length_);
431 next_state_ = STATE_READ_RESPONSE;
432 return OK;
435 int DoReadResponse(int rv) {
436 DCHECK_NE(ERR_IO_PENDING, rv);
437 if (rv < 0)
438 return rv;
440 buffer_->DidConsume(rv);
441 if (buffer_->BytesRemaining() > 0) {
442 next_state_ = STATE_READ_RESPONSE;
443 return socket_->Read(buffer_,
444 buffer_->BytesRemaining(),
445 base::Bind(&DnsTCPAttempt::OnIOComplete,
446 base::Unretained(this)));
448 if (!response_->InitParse(buffer_->BytesConsumed(), *query_))
449 return ERR_DNS_MALFORMED_RESPONSE;
450 if (response_->flags() & dns_protocol::kFlagTC)
451 return ERR_UNEXPECTED;
452 // TODO(szym): Frankly, none of these are expected.
453 if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
454 return ERR_NAME_NOT_RESOLVED;
455 if (response_->rcode() != dns_protocol::kRcodeNOERROR)
456 return ERR_DNS_SERVER_FAILED;
458 return OK;
461 void OnIOComplete(int rv) {
462 rv = DoLoop(rv);
463 if (rv != ERR_IO_PENDING)
464 callback_.Run(rv);
467 State next_state_;
468 base::TimeTicks start_time_;
470 scoped_ptr<StreamSocket> socket_;
471 scoped_ptr<DnsQuery> query_;
472 scoped_refptr<IOBufferWithSize> length_buffer_;
473 scoped_refptr<DrainableIOBuffer> buffer_;
475 uint16 response_length_;
476 scoped_ptr<DnsResponse> response_;
478 CompletionCallback callback_;
480 DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt);
483 // ----------------------------------------------------------------------------
485 // Implements DnsTransaction. Configuration is supplied by DnsSession.
486 // The suffix list is built according to the DnsConfig from the session.
487 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout.
488 // The first server to attempt on each query is given by
489 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards.
490 // Each server is attempted DnsConfig::attempts times.
491 class DnsTransactionImpl : public DnsTransaction,
492 public base::NonThreadSafe,
493 public base::SupportsWeakPtr<DnsTransactionImpl> {
494 public:
495 DnsTransactionImpl(DnsSession* session,
496 const std::string& hostname,
497 uint16 qtype,
498 const DnsTransactionFactory::CallbackType& callback,
499 const BoundNetLog& net_log)
500 : session_(session),
501 hostname_(hostname),
502 qtype_(qtype),
503 callback_(callback),
504 net_log_(net_log),
505 had_tcp_attempt_(false),
506 first_server_index_(0) {
507 DCHECK(session_);
508 DCHECK(!hostname_.empty());
509 DCHECK(!callback_.is_null());
510 DCHECK(!IsIPLiteral(hostname_));
513 virtual ~DnsTransactionImpl() {
514 if (!callback_.is_null()) {
515 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION,
516 ERR_ABORTED);
517 } // otherwise logged in DoCallback or Start
520 virtual const std::string& GetHostname() const OVERRIDE {
521 DCHECK(CalledOnValidThread());
522 return hostname_;
525 virtual uint16 GetType() const OVERRIDE {
526 DCHECK(CalledOnValidThread());
527 return qtype_;
530 virtual int Start() OVERRIDE {
531 DCHECK(!callback_.is_null());
532 DCHECK(attempts_.empty());
533 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION,
534 base::Bind(&NetLogStartCallback, &hostname_, qtype_));
535 int rv = PrepareSearch();
536 if (rv == OK) {
537 AttemptResult result = ProcessAttemptResult(StartQuery());
538 if (result.rv == OK) {
539 // DnsTransaction must never succeed synchronously.
540 MessageLoop::current()->PostTask(
541 FROM_HERE,
542 base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result));
543 return ERR_IO_PENDING;
545 rv = result.rv;
547 if (rv != ERR_IO_PENDING) {
548 callback_.Reset();
549 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, rv);
551 DCHECK_NE(OK, rv);
552 return rv;
555 private:
556 // Wrapper for the result of a DnsUDPAttempt.
557 struct AttemptResult {
558 AttemptResult(int rv, const DnsAttempt* attempt)
559 : rv(rv), attempt(attempt) {}
561 int rv;
562 const DnsAttempt* attempt;
565 // Prepares |qnames_| according to the DnsConfig.
566 int PrepareSearch() {
567 const DnsConfig& config = session_->config();
569 std::string labeled_hostname;
570 if (!DNSDomainFromDot(hostname_, &labeled_hostname))
571 return ERR_INVALID_ARGUMENT;
573 if (hostname_[hostname_.size() - 1] == '.') {
574 // It's a fully-qualified name, no suffix search.
575 qnames_.push_back(labeled_hostname);
576 return OK;
579 int ndots = CountLabels(labeled_hostname) - 1;
581 if (ndots > 0 && !config.append_to_multi_label_name) {
582 qnames_.push_back(labeled_hostname);
583 return OK;
586 // Set true when |labeled_hostname| is put on the list.
587 bool had_hostname = false;
589 if (ndots >= config.ndots) {
590 qnames_.push_back(labeled_hostname);
591 had_hostname = true;
594 std::string qname;
595 for (size_t i = 0; i < config.search.size(); ++i) {
596 // Ignore invalid (too long) combinations.
597 if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname))
598 continue;
599 if (qname.size() == labeled_hostname.size()) {
600 if (had_hostname)
601 continue;
602 had_hostname = true;
604 qnames_.push_back(qname);
607 if (ndots > 0 && !had_hostname)
608 qnames_.push_back(labeled_hostname);
610 return qnames_.empty() ? ERR_DNS_SEARCH_EMPTY : OK;
613 void DoCallback(AttemptResult result) {
614 DCHECK(!callback_.is_null());
615 DCHECK_NE(ERR_IO_PENDING, result.rv);
616 const DnsResponse* response = result.attempt ?
617 result.attempt->GetResponse() : NULL;
618 CHECK(result.rv != OK || response != NULL);
620 timer_.Stop();
622 DnsTransactionFactory::CallbackType callback = callback_;
623 callback_.Reset();
625 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, result.rv);
626 callback.Run(this, result.rv, response);
629 // Makes another attempt at the current name, |qnames_.front()|, using the
630 // next nameserver.
631 AttemptResult MakeAttempt() {
632 unsigned attempt_number = attempts_.size();
634 uint16 id = session_->NextQueryId();
635 scoped_ptr<DnsQuery> query;
636 if (attempts_.empty()) {
637 query.reset(new DnsQuery(id, qnames_.front(), qtype_));
638 } else {
639 query.reset(attempts_[0]->GetQuery()->CloneWithNewId(id));
642 const DnsConfig& config = session_->config();
644 unsigned server_index = first_server_index_ +
645 (attempt_number % config.nameservers.size());
647 scoped_ptr<DnsSession::SocketLease> lease =
648 session_->AllocateSocket(server_index, net_log_.source());
650 bool got_socket = !!lease.get();
652 DnsUDPAttempt* attempt = new DnsUDPAttempt(lease.Pass(), query.Pass());
654 attempts_.push_back(attempt);
656 if (!got_socket)
657 return AttemptResult(ERR_CONNECTION_REFUSED, NULL);
659 net_log_.AddEvent(
660 NetLog::TYPE_DNS_TRANSACTION_ATTEMPT,
661 attempt->GetSocketNetLog().source().ToEventParametersCallback());
663 int rv = attempt->Start(base::Bind(&DnsTransactionImpl::OnAttemptComplete,
664 base::Unretained(this),
665 attempt_number));
666 if (rv == ERR_IO_PENDING) {
667 base::TimeDelta timeout = session_->NextTimeout(attempt_number);
668 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
670 return AttemptResult(rv, attempt);
673 AttemptResult MakeTCPAttempt(const DnsAttempt* previous_attempt) {
674 DCHECK(previous_attempt);
675 DCHECK(!had_tcp_attempt_);
677 scoped_ptr<StreamSocket> socket(
678 session_->CreateTCPSocket(previous_attempt->GetServerIndex(),
679 net_log_.source()));
681 // TODO(szym): Reuse the same id to help the server?
682 uint16 id = session_->NextQueryId();
683 scoped_ptr<DnsQuery> query(
684 previous_attempt->GetQuery()->CloneWithNewId(id));
686 // Cancel all other attempts, no point waiting on them.
687 attempts_.clear();
689 unsigned attempt_number = attempts_.size();
691 DnsTCPAttempt* attempt = new DnsTCPAttempt(socket.Pass(), query.Pass());
693 attempts_.push_back(attempt);
694 had_tcp_attempt_ = true;
696 net_log_.AddEvent(
697 NetLog::TYPE_DNS_TRANSACTION_TCP_ATTEMPT,
698 attempt->GetSocketNetLog().source().ToEventParametersCallback());
700 int rv = attempt->Start(base::Bind(&DnsTransactionImpl::OnAttemptComplete,
701 base::Unretained(this),
702 attempt_number));
703 if (rv == ERR_IO_PENDING) {
704 // Custom timeout for TCP attempt.
705 base::TimeDelta timeout = timer_.GetCurrentDelay() * 2;
706 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
708 return AttemptResult(rv, attempt);
711 // Begins query for the current name. Makes the first attempt.
712 AttemptResult StartQuery() {
713 std::string dotted_qname = DNSDomainToString(qnames_.front());
714 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION_QUERY,
715 NetLog::StringCallback("qname", &dotted_qname));
717 first_server_index_ = session_->NextFirstServerIndex();
719 attempts_.clear();
720 had_tcp_attempt_ = false;
721 return MakeAttempt();
724 void OnAttemptComplete(unsigned attempt_number, int rv) {
725 if (callback_.is_null())
726 return;
727 DCHECK_LT(attempt_number, attempts_.size());
728 const DnsAttempt* attempt = attempts_[attempt_number];
729 AttemptResult result = ProcessAttemptResult(AttemptResult(rv, attempt));
730 if (result.rv != ERR_IO_PENDING)
731 DoCallback(result);
734 void LogResponse(const DnsAttempt* attempt) {
735 if (attempt && attempt->GetResponse()) {
736 net_log_.AddEvent(
737 NetLog::TYPE_DNS_TRANSACTION_RESPONSE,
738 base::Bind(&DnsAttempt::NetLogResponseCallback,
739 base::Unretained(attempt)));
743 bool MoreAttemptsAllowed() const {
744 if (had_tcp_attempt_)
745 return false;
746 const DnsConfig& config = session_->config();
747 return attempts_.size() < config.attempts * config.nameservers.size();
750 // Resolves the result of a DnsAttempt until a terminal result is reached
751 // or it will complete asynchronously (ERR_IO_PENDING).
752 AttemptResult ProcessAttemptResult(AttemptResult result) {
753 while (result.rv != ERR_IO_PENDING) {
754 LogResponse(result.attempt);
756 switch (result.rv) {
757 case OK:
758 net_log_.EndEventWithNetErrorCode(
759 NetLog::TYPE_DNS_TRANSACTION_QUERY, result.rv);
760 DCHECK(result.attempt);
761 DCHECK(result.attempt->GetResponse());
762 return result;
763 case ERR_NAME_NOT_RESOLVED:
764 net_log_.EndEventWithNetErrorCode(
765 NetLog::TYPE_DNS_TRANSACTION_QUERY, result.rv);
766 // Try next suffix.
767 qnames_.pop_front();
768 if (qnames_.empty()) {
769 return AttemptResult(ERR_NAME_NOT_RESOLVED, NULL);
770 } else {
771 result = StartQuery();
773 break;
774 case ERR_CONNECTION_REFUSED:
775 case ERR_DNS_TIMED_OUT:
776 if (MoreAttemptsAllowed()) {
777 result = MakeAttempt();
778 } else {
779 return result;
781 break;
782 case ERR_DNS_SERVER_REQUIRES_TCP:
783 result = MakeTCPAttempt(result.attempt);
784 break;
785 default:
786 // Server failure.
787 DCHECK(result.attempt);
788 if (result.attempt != attempts_.back()) {
789 // This attempt already timed out. Ignore it.
790 return AttemptResult(ERR_IO_PENDING, NULL);
792 if (MoreAttemptsAllowed()) {
793 result = MakeAttempt();
794 } else if (result.rv == ERR_DNS_MALFORMED_RESPONSE &&
795 !had_tcp_attempt_) {
796 // For UDP only, ignore the response and wait until the last attempt
797 // times out.
798 return AttemptResult(ERR_IO_PENDING, NULL);
799 } else {
800 return AttemptResult(result.rv, NULL);
802 break;
805 return result;
808 void OnTimeout() {
809 if (callback_.is_null())
810 return;
811 AttemptResult result = ProcessAttemptResult(
812 AttemptResult(ERR_DNS_TIMED_OUT, NULL));
813 if (result.rv != ERR_IO_PENDING)
814 DoCallback(result);
817 scoped_refptr<DnsSession> session_;
818 std::string hostname_;
819 uint16 qtype_;
820 // Cleared in DoCallback.
821 DnsTransactionFactory::CallbackType callback_;
823 BoundNetLog net_log_;
825 // Search list of fully-qualified DNS names to query next (in DNS format).
826 std::deque<std::string> qnames_;
828 // List of attempts for the current name.
829 ScopedVector<DnsAttempt> attempts_;
830 bool had_tcp_attempt_;
832 // Index of the first server to try on each search query.
833 int first_server_index_;
835 base::OneShotTimer<DnsTransactionImpl> timer_;
837 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl);
840 // ----------------------------------------------------------------------------
842 // Implementation of DnsTransactionFactory that returns instances of
843 // DnsTransactionImpl.
844 class DnsTransactionFactoryImpl : public DnsTransactionFactory {
845 public:
846 explicit DnsTransactionFactoryImpl(DnsSession* session) {
847 session_ = session;
850 virtual scoped_ptr<DnsTransaction> CreateTransaction(
851 const std::string& hostname,
852 uint16 qtype,
853 const CallbackType& callback,
854 const BoundNetLog& net_log) OVERRIDE {
855 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(session_,
856 hostname,
857 qtype,
858 callback,
859 net_log));
862 private:
863 scoped_refptr<DnsSession> session_;
866 } // namespace
868 // static
869 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory(
870 DnsSession* session) {
871 return scoped_ptr<DnsTransactionFactory>(
872 new DnsTransactionFactoryImpl(session));
875 } // namespace net