- add sources.
[platform/framework/web/crosswalk.git] / src / net / dns / dns_transaction.cc
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/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
38 namespace net {
39
40 namespace {
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.
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;
53 }
54
55 bool IsIPLiteral(const std::string& hostname) {
56   IPAddressNumber ip;
57   return ParseIPLiteralToNumber(hostname, &ip);
58 }
59
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;
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.
74 class 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
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()) {}
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
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) {}
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.
516 class 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       attempts_count_(0),
532       had_tcp_attempt_(false),
533       first_server_index_(0) {
534     DCHECK(session_.get());
535     DCHECK(!hostname_.empty());
536     DCHECK(!callback_.is_null());
537     DCHECK(!IsIPLiteral(hostname_));
538   }
539
540   virtual ~DnsTransactionImpl() {
541     if (!callback_.is_null()) {
542       net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION,
543                                         ERR_ABORTED);
544     }  // otherwise logged in DoCallback or Start
545   }
546
547   virtual const std::string& GetHostname() const OVERRIDE {
548     DCHECK(CalledOnValidThread());
549     return hostname_;
550   }
551
552   virtual uint16 GetType() const OVERRIDE {
553     DCHECK(CalledOnValidThread());
554     return qtype_;
555   }
556
557   virtual void Start() OVERRIDE {
558     DCHECK(!callback_.is_null());
559     DCHECK(attempts_.empty());
560     net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION,
561                         base::Bind(&NetLogStartCallback, &hostname_, qtype_));
562     AttemptResult result(PrepareSearch(), NULL);
563     if (result.rv == OK) {
564       qnames_initial_size_ = qnames_.size();
565       if (qtype_ == dns_protocol::kTypeA)
566         UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchStart", qnames_.size());
567       result = ProcessAttemptResult(StartQuery());
568     }
569
570     // Must always return result asynchronously, to avoid reentrancy.
571     if (result.rv != ERR_IO_PENDING) {
572       base::MessageLoop::current()->PostTask(
573           FROM_HERE,
574           base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result));
575     }
576   }
577
578  private:
579   // Wrapper for the result of a DnsUDPAttempt.
580   struct AttemptResult {
581     AttemptResult(int rv, const DnsAttempt* attempt)
582         : rv(rv), attempt(attempt) {}
583
584     int rv;
585     const DnsAttempt* attempt;
586   };
587
588   // Prepares |qnames_| according to the DnsConfig.
589   int PrepareSearch() {
590     const DnsConfig& config = session_->config();
591
592     std::string labeled_hostname;
593     if (!DNSDomainFromDot(hostname_, &labeled_hostname))
594       return ERR_INVALID_ARGUMENT;
595
596     if (hostname_[hostname_.size() - 1] == '.') {
597       // It's a fully-qualified name, no suffix search.
598       qnames_.push_back(labeled_hostname);
599       return OK;
600     }
601
602     int ndots = CountLabels(labeled_hostname) - 1;
603
604     if (ndots > 0 && !config.append_to_multi_label_name) {
605       qnames_.push_back(labeled_hostname);
606       return OK;
607     }
608
609     // Set true when |labeled_hostname| is put on the list.
610     bool had_hostname = false;
611
612     if (ndots >= config.ndots) {
613       qnames_.push_back(labeled_hostname);
614       had_hostname = true;
615     }
616
617     std::string qname;
618     for (size_t i = 0; i < config.search.size(); ++i) {
619       // Ignore invalid (too long) combinations.
620       if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname))
621         continue;
622       if (qname.size() == labeled_hostname.size()) {
623         if (had_hostname)
624           continue;
625         had_hostname = true;
626       }
627       qnames_.push_back(qname);
628     }
629
630     if (ndots > 0 && !had_hostname)
631       qnames_.push_back(labeled_hostname);
632
633     return qnames_.empty() ? ERR_DNS_SEARCH_EMPTY : OK;
634   }
635
636   void DoCallback(AttemptResult result) {
637     DCHECK(!callback_.is_null());
638     DCHECK_NE(ERR_IO_PENDING, result.rv);
639     const DnsResponse* response = result.attempt ?
640         result.attempt->GetResponse() : NULL;
641     CHECK(result.rv != OK || response != NULL);
642
643     timer_.Stop();
644     RecordLostPacketsIfAny();
645     if (result.rv == OK)
646       UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountSuccess", attempts_count_);
647     else
648       UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountFail", attempts_count_);
649
650     if (response && qtype_ == dns_protocol::kTypeA) {
651       UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchRemain", qnames_.size());
652       UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchDone",
653                            qnames_initial_size_ - qnames_.size());
654     }
655
656     DnsTransactionFactory::CallbackType callback = callback_;
657     callback_.Reset();
658
659     net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, result.rv);
660     callback.Run(this, result.rv, response);
661   }
662
663   // Makes another attempt at the current name, |qnames_.front()|, using the
664   // next nameserver.
665   AttemptResult MakeAttempt() {
666     unsigned attempt_number = attempts_.size();
667
668     uint16 id = session_->NextQueryId();
669     scoped_ptr<DnsQuery> query;
670     if (attempts_.empty()) {
671       query.reset(new DnsQuery(id, qnames_.front(), qtype_));
672     } else {
673       query.reset(attempts_[0]->GetQuery()->CloneWithNewId(id));
674     }
675
676     const DnsConfig& config = session_->config();
677
678     unsigned server_index =
679         (first_server_index_ + attempt_number) % config.nameservers.size();
680     // Skip over known failed servers.
681     server_index = session_->NextGoodServerIndex(server_index);
682
683     scoped_ptr<DnsSession::SocketLease> lease =
684         session_->AllocateSocket(server_index, net_log_.source());
685
686     bool got_socket = !!lease.get();
687
688     DnsUDPAttempt* attempt =
689         new DnsUDPAttempt(server_index, lease.Pass(), query.Pass());
690
691     attempts_.push_back(attempt);
692     ++attempts_count_;
693
694     if (!got_socket)
695       return AttemptResult(ERR_CONNECTION_REFUSED, NULL);
696
697     net_log_.AddEvent(
698         NetLog::TYPE_DNS_TRANSACTION_ATTEMPT,
699         attempt->GetSocketNetLog().source().ToEventParametersCallback());
700
701     int rv = attempt->Start(
702         base::Bind(&DnsTransactionImpl::OnUdpAttemptComplete,
703                    base::Unretained(this), attempt_number,
704                    base::TimeTicks::Now()));
705     if (rv == ERR_IO_PENDING) {
706       base::TimeDelta timeout = session_->NextTimeout(server_index,
707                                                       attempt_number);
708       timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
709     }
710     return AttemptResult(rv, attempt);
711   }
712
713   AttemptResult MakeTCPAttempt(const DnsAttempt* previous_attempt) {
714     DCHECK(previous_attempt);
715     DCHECK(!had_tcp_attempt_);
716
717     unsigned server_index = previous_attempt->server_index();
718
719     scoped_ptr<StreamSocket> socket(
720         session_->CreateTCPSocket(server_index, net_log_.source()));
721
722     // TODO(szym): Reuse the same id to help the server?
723     uint16 id = session_->NextQueryId();
724     scoped_ptr<DnsQuery> query(
725         previous_attempt->GetQuery()->CloneWithNewId(id));
726
727     RecordLostPacketsIfAny();
728     // Cancel all other attempts, no point waiting on them.
729     attempts_.clear();
730
731     unsigned attempt_number = attempts_.size();
732
733     DnsTCPAttempt* attempt = new DnsTCPAttempt(server_index, socket.Pass(),
734                                                query.Pass());
735
736     attempts_.push_back(attempt);
737     ++attempts_count_;
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     DCHECK(!attempts_.empty());
899     AttemptResult result = ProcessAttemptResult(
900         AttemptResult(ERR_DNS_TIMED_OUT, attempts_.back()));
901     if (result.rv != ERR_IO_PENDING)
902       DoCallback(result);
903   }
904
905   scoped_refptr<DnsSession> session_;
906   std::string hostname_;
907   uint16 qtype_;
908   // Cleared in DoCallback.
909   DnsTransactionFactory::CallbackType callback_;
910
911   BoundNetLog net_log_;
912
913   // Search list of fully-qualified DNS names to query next (in DNS format).
914   std::deque<std::string> qnames_;
915   size_t qnames_initial_size_;
916
917   // List of attempts for the current name.
918   ScopedVector<DnsAttempt> attempts_;
919   // Count of attempts, not reset when |attempts_| vector is cleared.
920   int  attempts_count_;
921   bool had_tcp_attempt_;
922
923   // Index of the first server to try on each search query.
924   int first_server_index_;
925
926   base::OneShotTimer<DnsTransactionImpl> timer_;
927
928   DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl);
929 };
930
931 // ----------------------------------------------------------------------------
932
933 // Implementation of DnsTransactionFactory that returns instances of
934 // DnsTransactionImpl.
935 class DnsTransactionFactoryImpl : public DnsTransactionFactory {
936  public:
937   explicit DnsTransactionFactoryImpl(DnsSession* session) {
938     session_ = session;
939   }
940
941   virtual scoped_ptr<DnsTransaction> CreateTransaction(
942       const std::string& hostname,
943       uint16 qtype,
944       const CallbackType& callback,
945       const BoundNetLog& net_log) OVERRIDE {
946     return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(
947         session_.get(), hostname, qtype, callback, net_log));
948   }
949
950  private:
951   scoped_refptr<DnsSession> session_;
952 };
953
954 }  // namespace
955
956 // static
957 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory(
958     DnsSession* session) {
959   return scoped_ptr<DnsTransactionFactory>(
960       new DnsTransactionFactoryImpl(session));
961 }
962
963 }  // namespace net