Telemetry: adding memory tests & solving problems on the go.
[chromium-blink-merge.git] / net / dns / dns_transaction.cc
blob091cab6a6b6cd2209dfd6437948468ec5383e563
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/completion_callback.h"
25 #include "net/base/dns_util.h"
26 #include "net/base/io_buffer.h"
27 #include "net/base/ip_endpoint.h"
28 #include "net/base/net_errors.h"
29 #include "net/base/net_log.h"
30 #include "net/dns/dns_protocol.h"
31 #include "net/dns/dns_query.h"
32 #include "net/dns/dns_response.h"
33 #include "net/dns/dns_session.h"
34 #include "net/socket/client_socket_factory.h"
35 #include "net/udp/datagram_client_socket.h"
37 namespace net {
39 namespace {
41 // Provide a common macro to simplify code and readability. We must use a
42 // macro as the underlying HISTOGRAM macro creates static variables.
43 #define DNS_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES(name, time, \
44 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1), 100)
46 // Count labels in the fully-qualified name in DNS format.
47 int CountLabels(const std::string& name) {
48 size_t count = 0;
49 for (size_t i = 0; i < name.size() && name[i]; i += name[i] + 1)
50 ++count;
51 return count;
54 bool IsIPLiteral(const std::string& hostname) {
55 IPAddressNumber ip;
56 return ParseIPLiteralToNumber(hostname, &ip);
59 Value* NetLogStartCallback(const std::string* hostname,
60 uint16 qtype,
61 NetLog::LogLevel /* log_level */) {
62 DictionaryValue* dict = new DictionaryValue();
63 dict->SetString("hostname", *hostname);
64 dict->SetInteger("query_type", qtype);
65 return dict;
68 // ----------------------------------------------------------------------------
70 // A single asynchronous DNS exchange over UDP, which consists of sending out a
71 // DNS query, waiting for a response, and returning the response that it
72 // matches. Logging is done in the socket and in the outer DnsTransaction.
73 class DnsUDPAttempt {
74 public:
75 DnsUDPAttempt(scoped_ptr<DatagramClientSocket> socket,
76 const IPEndPoint& server,
77 scoped_ptr<DnsQuery> query,
78 const CompletionCallback& callback)
79 : next_state_(STATE_NONE),
80 received_malformed_response_(false),
81 socket_(socket.Pass()),
82 server_(server),
83 query_(query.Pass()),
84 callback_(callback) {
87 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously
88 // and calls |callback| upon completion.
89 int Start() {
90 DCHECK_EQ(STATE_NONE, next_state_);
91 int rv = socket_->Connect(server_);
92 DCHECK_NE(ERR_IO_PENDING, rv);
93 if (rv < 0)
94 return rv;
95 start_time_ = base::TimeTicks::Now();
96 next_state_ = STATE_SEND_QUERY;
97 return DoLoop(OK);
100 const DnsQuery* query() const {
101 return query_.get();
104 const DatagramClientSocket* socket() const {
105 return socket_.get();
108 // Returns the response or NULL if has not received a matching response from
109 // the server.
110 const DnsResponse* response() const {
111 const DnsResponse* resp = response_.get();
112 return (resp != NULL && resp->IsValid()) ? resp : NULL;
115 // Returns a Value representing the received response, along with a reference
116 // to the NetLog source source of the UDP socket used. The request must have
117 // completed before this is called.
118 Value* NetLogResponseCallback(NetLog::LogLevel /* log_level */) const {
119 DCHECK(response_->IsValid());
121 DictionaryValue* dict = new DictionaryValue();
122 dict->SetInteger("rcode", response_->rcode());
123 dict->SetInteger("answer_count", response_->answer_count());
124 socket_->NetLog().source().AddToEventParameters(dict);
125 return dict;
128 private:
129 enum State {
130 STATE_SEND_QUERY,
131 STATE_SEND_QUERY_COMPLETE,
132 STATE_READ_RESPONSE,
133 STATE_READ_RESPONSE_COMPLETE,
134 STATE_NONE,
137 int DoLoop(int result) {
138 CHECK_NE(STATE_NONE, next_state_);
139 int rv = result;
140 do {
141 State state = next_state_;
142 next_state_ = STATE_NONE;
143 switch (state) {
144 case STATE_SEND_QUERY:
145 rv = DoSendQuery();
146 break;
147 case STATE_SEND_QUERY_COMPLETE:
148 rv = DoSendQueryComplete(rv);
149 break;
150 case STATE_READ_RESPONSE:
151 rv = DoReadResponse();
152 break;
153 case STATE_READ_RESPONSE_COMPLETE:
154 rv = DoReadResponseComplete(rv);
155 break;
156 default:
157 NOTREACHED();
158 break;
160 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
161 // If we received a malformed response, and are now waiting for another one,
162 // indicate to the transaction that the server might be misbehaving.
163 if (rv == ERR_IO_PENDING && received_malformed_response_)
164 return ERR_DNS_MALFORMED_RESPONSE;
165 if (rv == OK) {
166 DNS_HISTOGRAM("AsyncDNS.UDPAttemptSuccess",
167 base::TimeTicks::Now() - start_time_);
168 } else if (rv != ERR_IO_PENDING) {
169 DNS_HISTOGRAM("AsyncDNS.UDPAttemptFail",
170 base::TimeTicks::Now() - start_time_);
172 return rv;
175 int DoSendQuery() {
176 next_state_ = STATE_SEND_QUERY_COMPLETE;
177 return socket_->Write(query_->io_buffer(),
178 query_->io_buffer()->size(),
179 base::Bind(&DnsUDPAttempt::OnIOComplete,
180 base::Unretained(this)));
183 int DoSendQueryComplete(int rv) {
184 DCHECK_NE(ERR_IO_PENDING, rv);
185 if (rv < 0)
186 return rv;
188 // Writing to UDP should not result in a partial datagram.
189 if (rv != query_->io_buffer()->size())
190 return ERR_MSG_TOO_BIG;
192 next_state_ = STATE_READ_RESPONSE;
193 return OK;
196 int DoReadResponse() {
197 next_state_ = STATE_READ_RESPONSE_COMPLETE;
198 response_.reset(new DnsResponse());
199 return socket_->Read(response_->io_buffer(),
200 response_->io_buffer()->size(),
201 base::Bind(&DnsUDPAttempt::OnIOComplete,
202 base::Unretained(this)));
205 int DoReadResponseComplete(int rv) {
206 DCHECK_NE(ERR_IO_PENDING, rv);
207 if (rv < 0)
208 return rv;
210 DCHECK(rv);
211 if (!response_->InitParse(rv, *query_)) {
212 // Other implementations simply ignore mismatched responses. Since each
213 // DnsUDPAttempt binds to a different port, we might find that responses
214 // to previously timed out queries lead to failures in the future.
215 // Our solution is to make another attempt, in case the query truly
216 // failed, but keep this attempt alive, in case it was a false alarm.
217 received_malformed_response_ = true;
218 next_state_ = STATE_READ_RESPONSE;
219 return OK;
221 if (response_->flags() & dns_protocol::kFlagTC)
222 return ERR_DNS_SERVER_REQUIRES_TCP;
223 // TODO(szym): Extract TTL for NXDOMAIN results. http://crbug.com/115051
224 if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
225 return ERR_NAME_NOT_RESOLVED;
226 if (response_->rcode() != dns_protocol::kRcodeNOERROR)
227 return ERR_DNS_SERVER_FAILED;
229 CHECK(response());
230 return OK;
233 void OnIOComplete(int rv) {
234 rv = DoLoop(rv);
235 if (rv != ERR_IO_PENDING)
236 callback_.Run(rv);
239 State next_state_;
240 bool received_malformed_response_;
241 base::TimeTicks start_time_;
243 scoped_ptr<DatagramClientSocket> socket_;
244 IPEndPoint server_;
245 scoped_ptr<DnsQuery> query_;
247 scoped_ptr<DnsResponse> response_;
249 CompletionCallback callback_;
251 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt);
254 // ----------------------------------------------------------------------------
256 // Implements DnsTransaction. Configuration is supplied by DnsSession.
257 // The suffix list is built according to the DnsConfig from the session.
258 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout.
259 // The first server to attempt on each query is given by
260 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards.
261 // Each server is attempted DnsConfig::attempts times.
262 class DnsTransactionImpl : public DnsTransaction,
263 public base::NonThreadSafe,
264 public base::SupportsWeakPtr<DnsTransactionImpl> {
265 public:
266 DnsTransactionImpl(DnsSession* session,
267 const std::string& hostname,
268 uint16 qtype,
269 const DnsTransactionFactory::CallbackType& callback,
270 const BoundNetLog& net_log)
271 : session_(session),
272 hostname_(hostname),
273 qtype_(qtype),
274 callback_(callback),
275 net_log_(net_log),
276 first_server_index_(0) {
277 DCHECK(session_);
278 DCHECK(!hostname_.empty());
279 DCHECK(!callback_.is_null());
280 DCHECK(!IsIPLiteral(hostname_));
283 virtual ~DnsTransactionImpl() {
284 if (!callback_.is_null()) {
285 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION,
286 ERR_ABORTED);
287 } // otherwise logged in DoCallback or Start
290 virtual const std::string& GetHostname() const OVERRIDE {
291 DCHECK(CalledOnValidThread());
292 return hostname_;
295 virtual uint16 GetType() const OVERRIDE {
296 DCHECK(CalledOnValidThread());
297 return qtype_;
300 virtual int Start() OVERRIDE {
301 DCHECK(!callback_.is_null());
302 DCHECK(attempts_.empty());
303 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION,
304 base::Bind(&NetLogStartCallback, &hostname_, qtype_));
305 int rv = PrepareSearch();
306 if (rv == OK) {
307 AttemptResult result = ProcessAttemptResult(StartQuery());
308 if (result.rv == OK) {
309 // DnsTransaction must never succeed synchronously.
310 MessageLoop::current()->PostTask(
311 FROM_HERE,
312 base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result));
313 return ERR_IO_PENDING;
315 rv = result.rv;
317 if (rv != ERR_IO_PENDING) {
318 callback_.Reset();
319 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, rv);
321 DCHECK_NE(OK, rv);
322 return rv;
325 private:
326 // Wrapper for the result of a DnsUDPAttempt.
327 struct AttemptResult {
328 AttemptResult(int rv, const DnsUDPAttempt* attempt)
329 : rv(rv), attempt(attempt) {}
331 int rv;
332 const DnsUDPAttempt* attempt;
335 // Prepares |qnames_| according to the DnsConfig.
336 int PrepareSearch() {
337 const DnsConfig& config = session_->config();
339 std::string labeled_hostname;
340 if (!DNSDomainFromDot(hostname_, &labeled_hostname))
341 return ERR_INVALID_ARGUMENT;
343 if (hostname_[hostname_.size() - 1] == '.') {
344 // It's a fully-qualified name, no suffix search.
345 qnames_.push_back(labeled_hostname);
346 return OK;
349 int ndots = CountLabels(labeled_hostname) - 1;
351 if (ndots > 0 && !config.append_to_multi_label_name) {
352 qnames_.push_back(labeled_hostname);
353 return OK;
356 // Set true when |labeled_hostname| is put on the list.
357 bool had_hostname = false;
359 if (ndots >= config.ndots) {
360 qnames_.push_back(labeled_hostname);
361 had_hostname = true;
364 std::string qname;
365 for (size_t i = 0; i < config.search.size(); ++i) {
366 // Ignore invalid (too long) combinations.
367 if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname))
368 continue;
369 if (qname.size() == labeled_hostname.size()) {
370 if (had_hostname)
371 continue;
372 had_hostname = true;
374 qnames_.push_back(qname);
377 if (ndots > 0 && !had_hostname)
378 qnames_.push_back(labeled_hostname);
380 return qnames_.empty() ? ERR_DNS_SEARCH_EMPTY : OK;
383 void DoCallback(AttemptResult result) {
384 DCHECK(!callback_.is_null());
385 DCHECK_NE(ERR_IO_PENDING, result.rv);
386 const DnsResponse* response = result.attempt ?
387 result.attempt->response() : NULL;
388 CHECK(result.rv != OK || response != NULL);
390 timer_.Stop();
392 DnsTransactionFactory::CallbackType callback = callback_;
393 callback_.Reset();
395 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, result.rv);
396 callback.Run(this, result.rv, response);
399 // Makes another attempt at the current name, |qnames_.front()|, using the
400 // next nameserver.
401 AttemptResult MakeAttempt() {
402 unsigned attempt_number = attempts_.size();
404 #if defined(OS_WIN)
405 // Avoid the Windows firewall warning about explicit UDP binding.
406 // TODO(szym): Reuse a pool of pre-bound sockets. http://crbug.com/107413
407 DatagramSocket::BindType bind_type = DatagramSocket::DEFAULT_BIND;
408 #else
409 DatagramSocket::BindType bind_type = DatagramSocket::RANDOM_BIND;
410 #endif
412 scoped_ptr<DatagramClientSocket> socket(
413 session_->socket_factory()->CreateDatagramClientSocket(
414 bind_type,
415 base::Bind(&base::RandInt),
416 net_log_.net_log(),
417 net_log_.source()));
419 uint16 id = session_->NextQueryId();
420 scoped_ptr<DnsQuery> query;
421 if (attempts_.empty()) {
422 query.reset(new DnsQuery(id, qnames_.front(), qtype_));
423 } else {
424 query.reset(attempts_[0]->query()->CloneWithNewId(id));
427 net_log_.AddEvent(NetLog::TYPE_DNS_TRANSACTION_ATTEMPT,
428 socket->NetLog().source().ToEventParametersCallback());
430 const DnsConfig& config = session_->config();
432 unsigned server_index = first_server_index_ +
433 (attempt_number % config.nameservers.size());
435 DnsUDPAttempt* attempt = new DnsUDPAttempt(
436 socket.Pass(),
437 config.nameservers[server_index],
438 query.Pass(),
439 base::Bind(&DnsTransactionImpl::OnAttemptComplete,
440 base::Unretained(this),
441 attempt_number));
443 attempts_.push_back(attempt);
445 int rv = attempt->Start();
446 if (rv == ERR_IO_PENDING) {
447 timer_.Stop();
448 base::TimeDelta timeout = session_->NextTimeout(attempt_number);
449 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
451 return AttemptResult(rv, attempt);
454 // Begins query for the current name. Makes the first attempt.
455 AttemptResult StartQuery() {
456 std::string dotted_qname = DNSDomainToString(qnames_.front());
457 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION_QUERY,
458 NetLog::StringCallback("qname", &dotted_qname));
460 first_server_index_ = session_->NextFirstServerIndex();
462 attempts_.clear();
463 return MakeAttempt();
466 void OnAttemptComplete(unsigned attempt_number, int rv) {
467 if (callback_.is_null())
468 return;
469 DCHECK_LT(attempt_number, attempts_.size());
470 const DnsUDPAttempt* attempt = attempts_[attempt_number];
471 AttemptResult result = ProcessAttemptResult(AttemptResult(rv, attempt));
472 if (result.rv != ERR_IO_PENDING)
473 DoCallback(result);
476 void LogResponse(const DnsUDPAttempt* attempt) {
477 if (attempt && attempt->response()) {
478 net_log_.AddEvent(
479 NetLog::TYPE_DNS_TRANSACTION_RESPONSE,
480 base::Bind(&DnsUDPAttempt::NetLogResponseCallback,
481 base::Unretained(attempt)));
485 bool MoreAttemptsAllowed() const {
486 const DnsConfig& config = session_->config();
487 return attempts_.size() < config.attempts * config.nameservers.size();
490 // Resolves the result of a DnsUDPAttempt until a terminal result is reached
491 // or it will complete asynchronously (ERR_IO_PENDING).
492 AttemptResult ProcessAttemptResult(AttemptResult result) {
493 while (result.rv != ERR_IO_PENDING) {
494 LogResponse(result.attempt);
496 switch (result.rv) {
497 case OK:
498 net_log_.EndEventWithNetErrorCode(
499 NetLog::TYPE_DNS_TRANSACTION_QUERY, result.rv);
500 DCHECK(result.attempt);
501 DCHECK(result.attempt->response());
502 return result;
503 case ERR_NAME_NOT_RESOLVED:
504 net_log_.EndEventWithNetErrorCode(
505 NetLog::TYPE_DNS_TRANSACTION_QUERY, result.rv);
506 // Try next suffix.
507 qnames_.pop_front();
508 if (qnames_.empty()) {
509 return AttemptResult(ERR_NAME_NOT_RESOLVED, NULL);
510 } else {
511 result = StartQuery();
513 break;
514 case ERR_DNS_TIMED_OUT:
515 if (MoreAttemptsAllowed()) {
516 result = MakeAttempt();
517 } else {
518 return result;
520 break;
521 default:
522 // Server failure.
523 DCHECK(result.attempt);
524 if (result.attempt != attempts_.back()) {
525 // This attempt already timed out. Ignore it.
526 return AttemptResult(ERR_IO_PENDING, NULL);
528 if (MoreAttemptsAllowed()) {
529 result = MakeAttempt();
530 } else if (result.rv == ERR_DNS_MALFORMED_RESPONSE) {
531 // Wait until the last attempt times out.
532 return AttemptResult(ERR_IO_PENDING, NULL);
533 } else {
534 return AttemptResult(result.rv, NULL);
536 break;
539 return result;
542 void OnTimeout() {
543 if (callback_.is_null())
544 return;
545 AttemptResult result = ProcessAttemptResult(
546 AttemptResult(ERR_DNS_TIMED_OUT, NULL));
547 if (result.rv != ERR_IO_PENDING)
548 DoCallback(result);
551 scoped_refptr<DnsSession> session_;
552 std::string hostname_;
553 uint16 qtype_;
554 // Cleared in DoCallback.
555 DnsTransactionFactory::CallbackType callback_;
557 BoundNetLog net_log_;
559 // Search list of fully-qualified DNS names to query next (in DNS format).
560 std::deque<std::string> qnames_;
562 // List of attempts for the current name.
563 ScopedVector<DnsUDPAttempt> attempts_;
565 // Index of the first server to try on each search query.
566 int first_server_index_;
568 base::OneShotTimer<DnsTransactionImpl> timer_;
570 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl);
573 // ----------------------------------------------------------------------------
575 // Implementation of DnsTransactionFactory that returns instances of
576 // DnsTransactionImpl.
577 class DnsTransactionFactoryImpl : public DnsTransactionFactory {
578 public:
579 explicit DnsTransactionFactoryImpl(DnsSession* session) {
580 session_ = session;
583 virtual scoped_ptr<DnsTransaction> CreateTransaction(
584 const std::string& hostname,
585 uint16 qtype,
586 const CallbackType& callback,
587 const BoundNetLog& net_log) OVERRIDE {
588 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(session_,
589 hostname,
590 qtype,
591 callback,
592 net_log));
595 private:
596 scoped_refptr<DnsSession> session_;
599 } // namespace
601 // static
602 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory(
603 DnsSession* session) {
604 return scoped_ptr<DnsTransactionFactory>(
605 new DnsTransactionFactoryImpl(session));
608 } // namespace net