dns_transaction.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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.
4
5#include "net/dns/dns_transaction.h"
6
7#include <deque>
8#include <string>
9#include <vector>
10
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/strings/string_piece.h"
21#include "base/threading/non_thread_safe.h"
22#include "base/timer/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"
37
38namespace net {
39
40namespace {
41
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)
46
47// Count labels in the fully-qualified name in DNS format.
48int 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;
53}
54
55bool IsIPLiteral(const std::string& hostname) {
56  IPAddressNumber ip;
57  return ParseIPLiteralToNumber(hostname, &ip);
58}
59
60base::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;
67};
68
69// ----------------------------------------------------------------------------
70
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.
74class DnsAttempt {
75 public:
76  explicit DnsAttempt(unsigned server_index)
77      : result_(ERR_FAILED), server_index_(server_index) {}
78
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;
83
84  // Returns the query of this attempt.
85  virtual const DnsQuery* GetQuery() const = 0;
86
87  // Returns the response or NULL if has not received a matching response from
88  // the server.
89  virtual const DnsResponse* GetResponse() const = 0;
90
91  // Returns the net log bound to the source of the socket.
92  virtual const BoundNetLog& GetSocketNetLog() const = 0;
93
94  // Returns the index of the destination server within DnsConfig::nameservers.
95  unsigned server_index() const { return server_index_; }
96
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());
102
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;
108  }
109
110  void set_result(int result) {
111    result_ = result;
112  }
113
114  // True if current attempt is pending (waiting for server response).
115  bool is_pending() const {
116    return result_ == ERR_IO_PENDING;
117  }
118
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);
123  }
124
125 private:
126  // Result of last operation.
127  int result_;
128
129  const unsigned server_index_;
130};
131
132class 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()) {}
142
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);
150  }
151
152  virtual const DnsQuery* GetQuery() const OVERRIDE {
153    return query_.get();
154  }
155
156  virtual const DnsResponse* GetResponse() const OVERRIDE {
157    const DnsResponse* resp = response_.get();
158    return (resp != NULL && resp->IsValid()) ? resp : NULL;
159  }
160
161  virtual const BoundNetLog& GetSocketNetLog() const OVERRIDE {
162    return socket_lease_->socket()->NetLog();
163  }
164
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,
172  };
173
174  DatagramClientSocket* socket() {
175    return socket_lease_->socket();
176  }
177
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;
200      }
201    } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
202
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_);
215    }
216    return rv;
217  }
218
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)));
225  }
226
227  int DoSendQueryComplete(int rv) {
228    DCHECK_NE(ERR_IO_PENDING, rv);
229    if (rv < 0)
230      return rv;
231
232    // Writing to UDP should not result in a partial datagram.
233    if (rv != query_->io_buffer()->size())
234      return ERR_MSG_TOO_BIG;
235
236    next_state_ = STATE_READ_RESPONSE;
237    return OK;
238  }
239
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)));
247  }
248
249  int DoReadResponseComplete(int rv) {
250    DCHECK_NE(ERR_IO_PENDING, rv);
251    if (rv < 0)
252      return rv;
253
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;
264    }
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;
272
273    return OK;
274  }
275
276  void OnIOComplete(int rv) {
277    rv = DoLoop(rv);
278    if (rv != ERR_IO_PENDING)
279      callback_.Run(rv);
280  }
281
282  State next_state_;
283  bool received_malformed_response_;
284  base::TimeTicks start_time_;
285
286  scoped_ptr<DnsSession::SocketLease> socket_lease_;
287  scoped_ptr<DnsQuery> query_;
288
289  scoped_ptr<DnsResponse> response_;
290
291  CompletionCallback callback_;
292
293  DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt);
294};
295
296class 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) {}
307
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;
319    }
320    return DoLoop(rv);
321  }
322
323  virtual const DnsQuery* GetQuery() const OVERRIDE {
324    return query_.get();
325  }
326
327  virtual const DnsResponse* GetResponse() const OVERRIDE {
328    const DnsResponse* resp = response_.get();
329    return (resp != NULL && resp->IsValid()) ? resp : NULL;
330  }
331
332  virtual const BoundNetLog& GetSocketNetLog() const OVERRIDE {
333    return socket_->NetLog();
334  }
335
336 private:
337  enum State {
338    STATE_CONNECT_COMPLETE,
339    STATE_SEND_LENGTH,
340    STATE_SEND_QUERY,
341    STATE_READ_LENGTH,
342    STATE_READ_RESPONSE,
343    STATE_NONE,
344  };
345
346  int DoLoop(int result) {
347    CHECK_NE(STATE_NONE, next_state_);
348    int rv = result;
349    do {
350      State state = next_state_;
351      next_state_ = STATE_NONE;
352      switch (state) {
353        case STATE_CONNECT_COMPLETE:
354          rv = DoConnectComplete(rv);
355          break;
356        case STATE_SEND_LENGTH:
357          rv = DoSendLength(rv);
358          break;
359        case STATE_SEND_QUERY:
360          rv = DoSendQuery(rv);
361          break;
362        case STATE_READ_LENGTH:
363          rv = DoReadLength(rv);
364          break;
365        case STATE_READ_RESPONSE:
366          rv = DoReadResponse(rv);
367          break;
368        default:
369          NOTREACHED();
370          break;
371      }
372    } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
373
374    set_result(rv);
375    if (rv == OK) {
376      DCHECK_EQ(STATE_NONE, next_state_);
377      DNS_HISTOGRAM("AsyncDNS.TCPAttemptSuccess",
378                    base::TimeTicks::Now() - start_time_);
379    } else if (rv != ERR_IO_PENDING) {
380      DNS_HISTOGRAM("AsyncDNS.TCPAttemptFail",
381                    base::TimeTicks::Now() - start_time_);
382    }
383    return rv;
384  }
385
386  int DoConnectComplete(int rv) {
387    DCHECK_NE(ERR_IO_PENDING, rv);
388    if (rv < 0)
389      return rv;
390
391    WriteBigEndian<uint16>(length_buffer_->data(), query_->io_buffer()->size());
392    buffer_ =
393        new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
394    next_state_ = STATE_SEND_LENGTH;
395    return OK;
396  }
397
398  int DoSendLength(int rv) {
399    DCHECK_NE(ERR_IO_PENDING, rv);
400    if (rv < 0)
401      return rv;
402
403    buffer_->DidConsume(rv);
404    if (buffer_->BytesRemaining() > 0) {
405      next_state_ = STATE_SEND_LENGTH;
406      return socket_->Write(
407          buffer_.get(),
408          buffer_->BytesRemaining(),
409          base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
410    }
411    buffer_ = new DrainableIOBuffer(query_->io_buffer(),
412                                    query_->io_buffer()->size());
413    next_state_ = STATE_SEND_QUERY;
414    return OK;
415  }
416
417  int DoSendQuery(int rv) {
418    DCHECK_NE(ERR_IO_PENDING, rv);
419    if (rv < 0)
420      return rv;
421
422    buffer_->DidConsume(rv);
423    if (buffer_->BytesRemaining() > 0) {
424      next_state_ = STATE_SEND_QUERY;
425      return socket_->Write(
426          buffer_.get(),
427          buffer_->BytesRemaining(),
428          base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
429    }
430    buffer_ =
431        new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
432    next_state_ = STATE_READ_LENGTH;
433    return OK;
434  }
435
436  int DoReadLength(int rv) {
437    DCHECK_NE(ERR_IO_PENDING, rv);
438    if (rv < 0)
439      return rv;
440
441    buffer_->DidConsume(rv);
442    if (buffer_->BytesRemaining() > 0) {
443      next_state_ = STATE_READ_LENGTH;
444      return socket_->Read(
445          buffer_.get(),
446          buffer_->BytesRemaining(),
447          base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
448    }
449    ReadBigEndian<uint16>(length_buffer_->data(), &response_length_);
450    // Check if advertised response is too short. (Optimization only.)
451    if (response_length_ < query_->io_buffer()->size())
452      return ERR_DNS_MALFORMED_RESPONSE;
453    // Allocate more space so that DnsResponse::InitParse sanity check passes.
454    response_.reset(new DnsResponse(response_length_ + 1));
455    buffer_ = new DrainableIOBuffer(response_->io_buffer(), response_length_);
456    next_state_ = STATE_READ_RESPONSE;
457    return OK;
458  }
459
460  int DoReadResponse(int rv) {
461    DCHECK_NE(ERR_IO_PENDING, rv);
462    if (rv < 0)
463      return rv;
464
465    buffer_->DidConsume(rv);
466    if (buffer_->BytesRemaining() > 0) {
467      next_state_ = STATE_READ_RESPONSE;
468      return socket_->Read(
469          buffer_.get(),
470          buffer_->BytesRemaining(),
471          base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
472    }
473    if (!response_->InitParse(buffer_->BytesConsumed(), *query_))
474      return ERR_DNS_MALFORMED_RESPONSE;
475    if (response_->flags() & dns_protocol::kFlagTC)
476      return ERR_UNEXPECTED;
477    // TODO(szym): Frankly, none of these are expected.
478    if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
479      return ERR_NAME_NOT_RESOLVED;
480    if (response_->rcode() != dns_protocol::kRcodeNOERROR)
481      return ERR_DNS_SERVER_FAILED;
482
483    return OK;
484  }
485
486  void OnIOComplete(int rv) {
487    rv = DoLoop(rv);
488    if (rv != ERR_IO_PENDING)
489      callback_.Run(rv);
490  }
491
492  State next_state_;
493  base::TimeTicks start_time_;
494
495  scoped_ptr<StreamSocket> socket_;
496  scoped_ptr<DnsQuery> query_;
497  scoped_refptr<IOBufferWithSize> length_buffer_;
498  scoped_refptr<DrainableIOBuffer> buffer_;
499
500  uint16 response_length_;
501  scoped_ptr<DnsResponse> response_;
502
503  CompletionCallback callback_;
504
505  DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt);
506};
507
508// ----------------------------------------------------------------------------
509
510// Implements DnsTransaction. Configuration is supplied by DnsSession.
511// The suffix list is built according to the DnsConfig from the session.
512// The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout.
513// The first server to attempt on each query is given by
514// DnsSession::NextFirstServerIndex, and the order is round-robin afterwards.
515// Each server is attempted DnsConfig::attempts times.
516class DnsTransactionImpl : public DnsTransaction,
517                           public base::NonThreadSafe,
518                           public base::SupportsWeakPtr<DnsTransactionImpl> {
519 public:
520  DnsTransactionImpl(DnsSession* session,
521                     const std::string& hostname,
522                     uint16 qtype,
523                     const DnsTransactionFactory::CallbackType& callback,
524                     const BoundNetLog& net_log)
525    : session_(session),
526      hostname_(hostname),
527      qtype_(qtype),
528      callback_(callback),
529      net_log_(net_log),
530      qnames_initial_size_(0),
531      had_tcp_attempt_(false),
532      first_server_index_(0) {
533    DCHECK(session_.get());
534    DCHECK(!hostname_.empty());
535    DCHECK(!callback_.is_null());
536    DCHECK(!IsIPLiteral(hostname_));
537  }
538
539  virtual ~DnsTransactionImpl() {
540    if (!callback_.is_null()) {
541      net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION,
542                                        ERR_ABORTED);
543    }  // otherwise logged in DoCallback or Start
544  }
545
546  virtual const std::string& GetHostname() const OVERRIDE {
547    DCHECK(CalledOnValidThread());
548    return hostname_;
549  }
550
551  virtual uint16 GetType() const OVERRIDE {
552    DCHECK(CalledOnValidThread());
553    return qtype_;
554  }
555
556  virtual int Start() OVERRIDE {
557    DCHECK(!callback_.is_null());
558    DCHECK(attempts_.empty());
559    net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION,
560                        base::Bind(&NetLogStartCallback, &hostname_, qtype_));
561    int rv = PrepareSearch();
562    if (rv == OK) {
563      qnames_initial_size_ = qnames_.size();
564      if (qtype_ == dns_protocol::kTypeA)
565        UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchStart", qnames_.size());
566      AttemptResult result = ProcessAttemptResult(StartQuery());
567      if (result.rv == OK) {
568        // DnsTransaction must never succeed synchronously.
569        base::MessageLoop::current()->PostTask(
570            FROM_HERE,
571            base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result));
572        return ERR_IO_PENDING;
573      }
574      rv = result.rv;
575    }
576    if (rv != ERR_IO_PENDING) {
577      callback_.Reset();
578      net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, rv);
579    }
580    DCHECK_NE(OK, rv);
581    return rv;
582  }
583
584 private:
585  // Wrapper for the result of a DnsUDPAttempt.
586  struct AttemptResult {
587    AttemptResult(int rv, const DnsAttempt* attempt)
588        : rv(rv), attempt(attempt) {}
589
590    int rv;
591    const DnsAttempt* attempt;
592  };
593
594  // Prepares |qnames_| according to the DnsConfig.
595  int PrepareSearch() {
596    const DnsConfig& config = session_->config();
597
598    std::string labeled_hostname;
599    if (!DNSDomainFromDot(hostname_, &labeled_hostname))
600      return ERR_INVALID_ARGUMENT;
601
602    if (hostname_[hostname_.size() - 1] == '.') {
603      // It's a fully-qualified name, no suffix search.
604      qnames_.push_back(labeled_hostname);
605      return OK;
606    }
607
608    int ndots = CountLabels(labeled_hostname) - 1;
609
610    if (ndots > 0 && !config.append_to_multi_label_name) {
611      qnames_.push_back(labeled_hostname);
612      return OK;
613    }
614
615    // Set true when |labeled_hostname| is put on the list.
616    bool had_hostname = false;
617
618    if (ndots >= config.ndots) {
619      qnames_.push_back(labeled_hostname);
620      had_hostname = true;
621    }
622
623    std::string qname;
624    for (size_t i = 0; i < config.search.size(); ++i) {
625      // Ignore invalid (too long) combinations.
626      if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname))
627        continue;
628      if (qname.size() == labeled_hostname.size()) {
629        if (had_hostname)
630          continue;
631        had_hostname = true;
632      }
633      qnames_.push_back(qname);
634    }
635
636    if (ndots > 0 && !had_hostname)
637      qnames_.push_back(labeled_hostname);
638
639    return qnames_.empty() ? ERR_DNS_SEARCH_EMPTY : OK;
640  }
641
642  void DoCallback(AttemptResult result) {
643    DCHECK(!callback_.is_null());
644    DCHECK_NE(ERR_IO_PENDING, result.rv);
645    const DnsResponse* response = result.attempt ?
646        result.attempt->GetResponse() : NULL;
647    CHECK(result.rv != OK || response != NULL);
648
649    timer_.Stop();
650    RecordLostPacketsIfAny();
651
652    if (response && qtype_ == dns_protocol::kTypeA) {
653      UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchRemain", qnames_.size());
654      UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchDone",
655                           qnames_initial_size_ - qnames_.size());
656    }
657
658    DnsTransactionFactory::CallbackType callback = callback_;
659    callback_.Reset();
660
661    net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, result.rv);
662    callback.Run(this, result.rv, response);
663  }
664
665  // Makes another attempt at the current name, |qnames_.front()|, using the
666  // next nameserver.
667  AttemptResult MakeAttempt() {
668    unsigned attempt_number = attempts_.size();
669
670    uint16 id = session_->NextQueryId();
671    scoped_ptr<DnsQuery> query;
672    if (attempts_.empty()) {
673      query.reset(new DnsQuery(id, qnames_.front(), qtype_));
674    } else {
675      query.reset(attempts_[0]->GetQuery()->CloneWithNewId(id));
676    }
677
678    const DnsConfig& config = session_->config();
679
680    unsigned server_index =
681        (first_server_index_ + attempt_number) % config.nameservers.size();
682    // Skip over known failed servers.
683    server_index = session_->NextGoodServerIndex(server_index);
684
685    scoped_ptr<DnsSession::SocketLease> lease =
686        session_->AllocateSocket(server_index, net_log_.source());
687
688    bool got_socket = !!lease.get();
689
690    DnsUDPAttempt* attempt =
691        new DnsUDPAttempt(server_index, lease.Pass(), query.Pass());
692
693    attempts_.push_back(attempt);
694
695    if (!got_socket)
696      return AttemptResult(ERR_CONNECTION_REFUSED, NULL);
697
698    net_log_.AddEvent(
699        NetLog::TYPE_DNS_TRANSACTION_ATTEMPT,
700        attempt->GetSocketNetLog().source().ToEventParametersCallback());
701
702    int rv = attempt->Start(
703        base::Bind(&DnsTransactionImpl::OnUdpAttemptComplete,
704                   base::Unretained(this), attempt_number,
705                   base::TimeTicks::Now()));
706    if (rv == ERR_IO_PENDING) {
707      base::TimeDelta timeout = session_->NextTimeout(server_index,
708                                                      attempt_number);
709      timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
710    }
711    return AttemptResult(rv, attempt);
712  }
713
714  AttemptResult MakeTCPAttempt(const DnsAttempt* previous_attempt) {
715    DCHECK(previous_attempt);
716    DCHECK(!had_tcp_attempt_);
717
718    unsigned server_index = previous_attempt->server_index();
719
720    scoped_ptr<StreamSocket> socket(
721        session_->CreateTCPSocket(server_index, net_log_.source()));
722
723    // TODO(szym): Reuse the same id to help the server?
724    uint16 id = session_->NextQueryId();
725    scoped_ptr<DnsQuery> query(
726        previous_attempt->GetQuery()->CloneWithNewId(id));
727
728    RecordLostPacketsIfAny();
729    // Cancel all other attempts, no point waiting on them.
730    attempts_.clear();
731
732    unsigned attempt_number = attempts_.size();
733
734    DnsTCPAttempt* attempt = new DnsTCPAttempt(server_index, socket.Pass(),
735                                               query.Pass());
736
737    attempts_.push_back(attempt);
738    had_tcp_attempt_ = true;
739
740    net_log_.AddEvent(
741        NetLog::TYPE_DNS_TRANSACTION_TCP_ATTEMPT,
742        attempt->GetSocketNetLog().source().ToEventParametersCallback());
743
744    int rv = attempt->Start(base::Bind(&DnsTransactionImpl::OnAttemptComplete,
745                                       base::Unretained(this),
746                                       attempt_number));
747    if (rv == ERR_IO_PENDING) {
748      // Custom timeout for TCP attempt.
749      base::TimeDelta timeout = timer_.GetCurrentDelay() * 2;
750      timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
751    }
752    return AttemptResult(rv, attempt);
753  }
754
755  // Begins query for the current name. Makes the first attempt.
756  AttemptResult StartQuery() {
757    std::string dotted_qname = DNSDomainToString(qnames_.front());
758    net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION_QUERY,
759                        NetLog::StringCallback("qname", &dotted_qname));
760
761    first_server_index_ = session_->NextFirstServerIndex();
762    RecordLostPacketsIfAny();
763    attempts_.clear();
764    had_tcp_attempt_ = false;
765    return MakeAttempt();
766  }
767
768  void OnUdpAttemptComplete(unsigned attempt_number,
769                            base::TimeTicks start,
770                            int rv) {
771    DCHECK_LT(attempt_number, attempts_.size());
772    const DnsAttempt* attempt = attempts_[attempt_number];
773    if (attempt->GetResponse()) {
774      session_->RecordRTT(attempt->server_index(),
775                          base::TimeTicks::Now() - start);
776    }
777    OnAttemptComplete(attempt_number, rv);
778  }
779
780  void OnAttemptComplete(unsigned attempt_number, int rv) {
781    if (callback_.is_null())
782      return;
783    DCHECK_LT(attempt_number, attempts_.size());
784    const DnsAttempt* attempt = attempts_[attempt_number];
785    AttemptResult result = ProcessAttemptResult(AttemptResult(rv, attempt));
786    if (result.rv != ERR_IO_PENDING)
787      DoCallback(result);
788  }
789
790  // Record packet loss for any incomplete attempts.
791  void RecordLostPacketsIfAny() {
792    // Loop through attempts until we find first that is completed
793    size_t first_completed = 0;
794    for (first_completed = 0; first_completed < attempts_.size();
795        ++first_completed) {
796      if (attempts_[first_completed]->is_completed())
797        break;
798    }
799    // If there were no completed attempts, then we must be offline, so don't
800    // record any attempts as lost packets.
801    if (first_completed == attempts_.size())
802      return;
803
804    size_t num_servers = session_->config().nameservers.size();
805    std::vector<int> server_attempts(num_servers);
806    for (size_t i = 0; i < first_completed; ++i) {
807      unsigned server_index = attempts_[i]->server_index();
808      int server_attempt = server_attempts[server_index]++;
809      // Don't record lost packet unless attempt is in pending state.
810      if (!attempts_[i]->is_pending())
811        continue;
812      session_->RecordLostPacket(server_index, server_attempt);
813    }
814  }
815
816  void LogResponse(const DnsAttempt* attempt) {
817    if (attempt && attempt->GetResponse()) {
818      net_log_.AddEvent(
819          NetLog::TYPE_DNS_TRANSACTION_RESPONSE,
820          base::Bind(&DnsAttempt::NetLogResponseCallback,
821                     base::Unretained(attempt)));
822    }
823  }
824
825  bool MoreAttemptsAllowed() const {
826    if (had_tcp_attempt_)
827      return false;
828    const DnsConfig& config = session_->config();
829    return attempts_.size() < config.attempts * config.nameservers.size();
830  }
831
832  // Resolves the result of a DnsAttempt until a terminal result is reached
833  // or it will complete asynchronously (ERR_IO_PENDING).
834  AttemptResult ProcessAttemptResult(AttemptResult result) {
835    while (result.rv != ERR_IO_PENDING) {
836      LogResponse(result.attempt);
837
838      switch (result.rv) {
839        case OK:
840          session_->RecordServerSuccess(result.attempt->server_index());
841          net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
842                                            result.rv);
843          DCHECK(result.attempt);
844          DCHECK(result.attempt->GetResponse());
845          return result;
846        case ERR_NAME_NOT_RESOLVED:
847          session_->RecordServerSuccess(result.attempt->server_index());
848          net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
849                                            result.rv);
850          // Try next suffix.
851          qnames_.pop_front();
852          if (qnames_.empty()) {
853            return AttemptResult(ERR_NAME_NOT_RESOLVED, NULL);
854          } else {
855            result = StartQuery();
856          }
857          break;
858        case ERR_CONNECTION_REFUSED:
859        case ERR_DNS_TIMED_OUT:
860          if (result.attempt)
861            session_->RecordServerFailure(result.attempt->server_index());
862          if (MoreAttemptsAllowed()) {
863            result = MakeAttempt();
864          } else {
865            return result;
866          }
867          break;
868        case ERR_DNS_SERVER_REQUIRES_TCP:
869          result = MakeTCPAttempt(result.attempt);
870          break;
871        default:
872          // Server failure.
873          DCHECK(result.attempt);
874          if (result.attempt != attempts_.back()) {
875            // This attempt already timed out. Ignore it.
876            session_->RecordServerFailure(result.attempt->server_index());
877            return AttemptResult(ERR_IO_PENDING, NULL);
878          }
879          if (MoreAttemptsAllowed()) {
880            result = MakeAttempt();
881          } else if (result.rv == ERR_DNS_MALFORMED_RESPONSE &&
882                     !had_tcp_attempt_) {
883            // For UDP only, ignore the response and wait until the last attempt
884            // times out.
885            return AttemptResult(ERR_IO_PENDING, NULL);
886          } else {
887            return AttemptResult(result.rv, NULL);
888          }
889          break;
890      }
891    }
892    return result;
893  }
894
895  void OnTimeout() {
896    if (callback_.is_null())
897      return;
898    AttemptResult result = ProcessAttemptResult(
899        AttemptResult(ERR_DNS_TIMED_OUT, NULL));
900    if (result.rv != ERR_IO_PENDING)
901      DoCallback(result);
902  }
903
904  scoped_refptr<DnsSession> session_;
905  std::string hostname_;
906  uint16 qtype_;
907  // Cleared in DoCallback.
908  DnsTransactionFactory::CallbackType callback_;
909
910  BoundNetLog net_log_;
911
912  // Search list of fully-qualified DNS names to query next (in DNS format).
913  std::deque<std::string> qnames_;
914  size_t qnames_initial_size_;
915
916  // List of attempts for the current name.
917  ScopedVector<DnsAttempt> attempts_;
918  bool had_tcp_attempt_;
919
920  // Index of the first server to try on each search query.
921  int first_server_index_;
922
923  base::OneShotTimer<DnsTransactionImpl> timer_;
924
925  DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl);
926};
927
928// ----------------------------------------------------------------------------
929
930// Implementation of DnsTransactionFactory that returns instances of
931// DnsTransactionImpl.
932class DnsTransactionFactoryImpl : public DnsTransactionFactory {
933 public:
934  explicit DnsTransactionFactoryImpl(DnsSession* session) {
935    session_ = session;
936  }
937
938  virtual scoped_ptr<DnsTransaction> CreateTransaction(
939      const std::string& hostname,
940      uint16 qtype,
941      const CallbackType& callback,
942      const BoundNetLog& net_log) OVERRIDE {
943    return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(
944        session_.get(), hostname, qtype, callback, net_log));
945  }
946
947 private:
948  scoped_refptr<DnsSession> session_;
949};
950
951}  // namespace
952
953// static
954scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory(
955    DnsSession* session) {
956  return scoped_ptr<DnsTransactionFactory>(
957      new DnsTransactionFactoryImpl(session));
958}
959
960}  // namespace net
961