Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(743)

Side by Side Diff: net/dns/dns_transaction.cc

Issue 992733002: Remove //net (except for Android test stuff) and sdch (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/dns/dns_transaction.h ('k') | net/dns/dns_transaction_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/big_endian.h"
12 #include "base/bind.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/histogram.h"
19 #include "base/rand_util.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_piece.h"
22 #include "base/threading/non_thread_safe.h"
23 #include "base/timer/timer.h"
24 #include "base/values.h"
25 #include "net/base/completion_callback.h"
26 #include "net/base/dns_util.h"
27 #include "net/base/io_buffer.h"
28 #include "net/base/ip_endpoint.h"
29 #include "net/base/net_errors.h"
30 #include "net/base/net_log.h"
31 #include "net/dns/dns_protocol.h"
32 #include "net/dns/dns_query.h"
33 #include "net/dns/dns_response.h"
34 #include "net/dns/dns_session.h"
35 #include "net/socket/stream_socket.h"
36 #include "net/udp/datagram_client_socket.h"
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 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 const DnsQuery* GetQuery() const override { return query_.get(); }
153
154 const DnsResponse* GetResponse() const override {
155 const DnsResponse* resp = response_.get();
156 return (resp != NULL && resp->IsValid()) ? resp : NULL;
157 }
158
159 const BoundNetLog& GetSocketNetLog() const override {
160 return socket_lease_->socket()->NetLog();
161 }
162
163 private:
164 enum State {
165 STATE_SEND_QUERY,
166 STATE_SEND_QUERY_COMPLETE,
167 STATE_READ_RESPONSE,
168 STATE_READ_RESPONSE_COMPLETE,
169 STATE_NONE,
170 };
171
172 DatagramClientSocket* socket() {
173 return socket_lease_->socket();
174 }
175
176 int DoLoop(int result) {
177 CHECK_NE(STATE_NONE, next_state_);
178 int rv = result;
179 do {
180 State state = next_state_;
181 next_state_ = STATE_NONE;
182 switch (state) {
183 case STATE_SEND_QUERY:
184 rv = DoSendQuery();
185 break;
186 case STATE_SEND_QUERY_COMPLETE:
187 rv = DoSendQueryComplete(rv);
188 break;
189 case STATE_READ_RESPONSE:
190 rv = DoReadResponse();
191 break;
192 case STATE_READ_RESPONSE_COMPLETE:
193 rv = DoReadResponseComplete(rv);
194 break;
195 default:
196 NOTREACHED();
197 break;
198 }
199 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
200
201 set_result(rv);
202 // If we received a malformed response, and are now waiting for another one,
203 // indicate to the transaction that the server might be misbehaving.
204 if (rv == ERR_IO_PENDING && received_malformed_response_)
205 return ERR_DNS_MALFORMED_RESPONSE;
206 if (rv == OK) {
207 DCHECK_EQ(STATE_NONE, next_state_);
208 DNS_HISTOGRAM("AsyncDNS.UDPAttemptSuccess",
209 base::TimeTicks::Now() - start_time_);
210 } else if (rv != ERR_IO_PENDING) {
211 DNS_HISTOGRAM("AsyncDNS.UDPAttemptFail",
212 base::TimeTicks::Now() - start_time_);
213 }
214 return rv;
215 }
216
217 int DoSendQuery() {
218 next_state_ = STATE_SEND_QUERY_COMPLETE;
219 return socket()->Write(query_->io_buffer(),
220 query_->io_buffer()->size(),
221 base::Bind(&DnsUDPAttempt::OnIOComplete,
222 base::Unretained(this)));
223 }
224
225 int DoSendQueryComplete(int rv) {
226 DCHECK_NE(ERR_IO_PENDING, rv);
227 if (rv < 0)
228 return rv;
229
230 // Writing to UDP should not result in a partial datagram.
231 if (rv != query_->io_buffer()->size())
232 return ERR_MSG_TOO_BIG;
233
234 next_state_ = STATE_READ_RESPONSE;
235 return OK;
236 }
237
238 int DoReadResponse() {
239 next_state_ = STATE_READ_RESPONSE_COMPLETE;
240 response_.reset(new DnsResponse());
241 return socket()->Read(response_->io_buffer(),
242 response_->io_buffer()->size(),
243 base::Bind(&DnsUDPAttempt::OnIOComplete,
244 base::Unretained(this)));
245 }
246
247 int DoReadResponseComplete(int rv) {
248 DCHECK_NE(ERR_IO_PENDING, rv);
249 if (rv < 0)
250 return rv;
251
252 DCHECK(rv);
253 if (!response_->InitParse(rv, *query_)) {
254 // Other implementations simply ignore mismatched responses. Since each
255 // DnsUDPAttempt binds to a different port, we might find that responses
256 // to previously timed out queries lead to failures in the future.
257 // Our solution is to make another attempt, in case the query truly
258 // failed, but keep this attempt alive, in case it was a false alarm.
259 received_malformed_response_ = true;
260 next_state_ = STATE_READ_RESPONSE;
261 return OK;
262 }
263 if (response_->flags() & dns_protocol::kFlagTC)
264 return ERR_DNS_SERVER_REQUIRES_TCP;
265 // TODO(szym): Extract TTL for NXDOMAIN results. http://crbug.com/115051
266 if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
267 return ERR_NAME_NOT_RESOLVED;
268 if (response_->rcode() != dns_protocol::kRcodeNOERROR)
269 return ERR_DNS_SERVER_FAILED;
270
271 return OK;
272 }
273
274 void OnIOComplete(int rv) {
275 rv = DoLoop(rv);
276 if (rv != ERR_IO_PENDING)
277 callback_.Run(rv);
278 }
279
280 State next_state_;
281 bool received_malformed_response_;
282 base::TimeTicks start_time_;
283
284 scoped_ptr<DnsSession::SocketLease> socket_lease_;
285 scoped_ptr<DnsQuery> query_;
286
287 scoped_ptr<DnsResponse> response_;
288
289 CompletionCallback callback_;
290
291 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt);
292 };
293
294 class DnsTCPAttempt : public DnsAttempt {
295 public:
296 DnsTCPAttempt(unsigned server_index,
297 scoped_ptr<StreamSocket> socket,
298 scoped_ptr<DnsQuery> query)
299 : DnsAttempt(server_index),
300 next_state_(STATE_NONE),
301 socket_(socket.Pass()),
302 query_(query.Pass()),
303 length_buffer_(new IOBufferWithSize(sizeof(uint16))),
304 response_length_(0) {}
305
306 // DnsAttempt:
307 int Start(const CompletionCallback& callback) override {
308 DCHECK_EQ(STATE_NONE, next_state_);
309 callback_ = callback;
310 start_time_ = base::TimeTicks::Now();
311 next_state_ = STATE_CONNECT_COMPLETE;
312 int rv = socket_->Connect(base::Bind(&DnsTCPAttempt::OnIOComplete,
313 base::Unretained(this)));
314 if (rv == ERR_IO_PENDING) {
315 set_result(rv);
316 return rv;
317 }
318 return DoLoop(rv);
319 }
320
321 const DnsQuery* GetQuery() const override { return query_.get(); }
322
323 const DnsResponse* GetResponse() const override {
324 const DnsResponse* resp = response_.get();
325 return (resp != NULL && resp->IsValid()) ? resp : NULL;
326 }
327
328 const BoundNetLog& GetSocketNetLog() const override {
329 return socket_->NetLog();
330 }
331
332 private:
333 enum State {
334 STATE_CONNECT_COMPLETE,
335 STATE_SEND_LENGTH,
336 STATE_SEND_QUERY,
337 STATE_READ_LENGTH,
338 STATE_READ_LENGTH_COMPLETE,
339 STATE_READ_RESPONSE,
340 STATE_READ_RESPONSE_COMPLETE,
341 STATE_NONE,
342 };
343
344 int DoLoop(int result) {
345 CHECK_NE(STATE_NONE, next_state_);
346 int rv = result;
347 do {
348 State state = next_state_;
349 next_state_ = STATE_NONE;
350 switch (state) {
351 case STATE_CONNECT_COMPLETE:
352 rv = DoConnectComplete(rv);
353 break;
354 case STATE_SEND_LENGTH:
355 rv = DoSendLength(rv);
356 break;
357 case STATE_SEND_QUERY:
358 rv = DoSendQuery(rv);
359 break;
360 case STATE_READ_LENGTH:
361 rv = DoReadLength(rv);
362 break;
363 case STATE_READ_LENGTH_COMPLETE:
364 rv = DoReadLengthComplete(rv);
365 break;
366 case STATE_READ_RESPONSE:
367 rv = DoReadResponse(rv);
368 break;
369 case STATE_READ_RESPONSE_COMPLETE:
370 rv = DoReadResponseComplete(rv);
371 break;
372 default:
373 NOTREACHED();
374 break;
375 }
376 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
377
378 set_result(rv);
379 if (rv == OK) {
380 DCHECK_EQ(STATE_NONE, next_state_);
381 DNS_HISTOGRAM("AsyncDNS.TCPAttemptSuccess",
382 base::TimeTicks::Now() - start_time_);
383 } else if (rv != ERR_IO_PENDING) {
384 DNS_HISTOGRAM("AsyncDNS.TCPAttemptFail",
385 base::TimeTicks::Now() - start_time_);
386 }
387 return rv;
388 }
389
390 int DoConnectComplete(int rv) {
391 DCHECK_NE(ERR_IO_PENDING, rv);
392 if (rv < 0)
393 return rv;
394
395 uint16 query_size = static_cast<uint16>(query_->io_buffer()->size());
396 if (static_cast<int>(query_size) != query_->io_buffer()->size())
397 return ERR_FAILED;
398 base::WriteBigEndian<uint16>(length_buffer_->data(), query_size);
399 buffer_ =
400 new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
401 next_state_ = STATE_SEND_LENGTH;
402 return OK;
403 }
404
405 int DoSendLength(int rv) {
406 DCHECK_NE(ERR_IO_PENDING, rv);
407 if (rv < 0)
408 return rv;
409
410 buffer_->DidConsume(rv);
411 if (buffer_->BytesRemaining() > 0) {
412 next_state_ = STATE_SEND_LENGTH;
413 return socket_->Write(
414 buffer_.get(),
415 buffer_->BytesRemaining(),
416 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
417 }
418 buffer_ = new DrainableIOBuffer(query_->io_buffer(),
419 query_->io_buffer()->size());
420 next_state_ = STATE_SEND_QUERY;
421 return OK;
422 }
423
424 int DoSendQuery(int rv) {
425 DCHECK_NE(ERR_IO_PENDING, rv);
426 if (rv < 0)
427 return rv;
428
429 buffer_->DidConsume(rv);
430 if (buffer_->BytesRemaining() > 0) {
431 next_state_ = STATE_SEND_QUERY;
432 return socket_->Write(
433 buffer_.get(),
434 buffer_->BytesRemaining(),
435 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
436 }
437 buffer_ =
438 new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
439 next_state_ = STATE_READ_LENGTH;
440 return OK;
441 }
442
443 int DoReadLength(int rv) {
444 DCHECK_EQ(OK, rv);
445
446 next_state_ = STATE_READ_LENGTH_COMPLETE;
447 return ReadIntoBuffer();
448 }
449
450 int DoReadLengthComplete(int rv) {
451 DCHECK_NE(ERR_IO_PENDING, rv);
452 if (rv < 0)
453 return rv;
454 if (rv == 0)
455 return ERR_CONNECTION_CLOSED;
456
457 buffer_->DidConsume(rv);
458 if (buffer_->BytesRemaining() > 0) {
459 next_state_ = STATE_READ_LENGTH;
460 return OK;
461 }
462
463 base::ReadBigEndian<uint16>(length_buffer_->data(), &response_length_);
464 // Check if advertised response is too short. (Optimization only.)
465 if (response_length_ < query_->io_buffer()->size())
466 return ERR_DNS_MALFORMED_RESPONSE;
467 // Allocate more space so that DnsResponse::InitParse sanity check passes.
468 response_.reset(new DnsResponse(response_length_ + 1));
469 buffer_ = new DrainableIOBuffer(response_->io_buffer(), response_length_);
470 next_state_ = STATE_READ_RESPONSE;
471 return OK;
472 }
473
474 int DoReadResponse(int rv) {
475 DCHECK_EQ(OK, rv);
476
477 next_state_ = STATE_READ_RESPONSE_COMPLETE;
478 return ReadIntoBuffer();
479 }
480
481 int DoReadResponseComplete(int rv) {
482 DCHECK_NE(ERR_IO_PENDING, rv);
483 if (rv < 0)
484 return rv;
485 if (rv == 0)
486 return ERR_CONNECTION_CLOSED;
487
488 buffer_->DidConsume(rv);
489 if (buffer_->BytesRemaining() > 0) {
490 next_state_ = STATE_READ_RESPONSE;
491 return OK;
492 }
493
494 if (!response_->InitParse(buffer_->BytesConsumed(), *query_))
495 return ERR_DNS_MALFORMED_RESPONSE;
496 if (response_->flags() & dns_protocol::kFlagTC)
497 return ERR_UNEXPECTED;
498 // TODO(szym): Frankly, none of these are expected.
499 if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
500 return ERR_NAME_NOT_RESOLVED;
501 if (response_->rcode() != dns_protocol::kRcodeNOERROR)
502 return ERR_DNS_SERVER_FAILED;
503
504 return OK;
505 }
506
507 void OnIOComplete(int rv) {
508 rv = DoLoop(rv);
509 if (rv != ERR_IO_PENDING)
510 callback_.Run(rv);
511 }
512
513 int ReadIntoBuffer() {
514 return socket_->Read(
515 buffer_.get(),
516 buffer_->BytesRemaining(),
517 base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
518 }
519
520 State next_state_;
521 base::TimeTicks start_time_;
522
523 scoped_ptr<StreamSocket> socket_;
524 scoped_ptr<DnsQuery> query_;
525 scoped_refptr<IOBufferWithSize> length_buffer_;
526 scoped_refptr<DrainableIOBuffer> buffer_;
527
528 uint16 response_length_;
529 scoped_ptr<DnsResponse> response_;
530
531 CompletionCallback callback_;
532
533 DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt);
534 };
535
536 // ----------------------------------------------------------------------------
537
538 // Implements DnsTransaction. Configuration is supplied by DnsSession.
539 // The suffix list is built according to the DnsConfig from the session.
540 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout.
541 // The first server to attempt on each query is given by
542 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards.
543 // Each server is attempted DnsConfig::attempts times.
544 class DnsTransactionImpl : public DnsTransaction,
545 public base::NonThreadSafe,
546 public base::SupportsWeakPtr<DnsTransactionImpl> {
547 public:
548 DnsTransactionImpl(DnsSession* session,
549 const std::string& hostname,
550 uint16 qtype,
551 const DnsTransactionFactory::CallbackType& callback,
552 const BoundNetLog& net_log)
553 : session_(session),
554 hostname_(hostname),
555 qtype_(qtype),
556 callback_(callback),
557 net_log_(net_log),
558 qnames_initial_size_(0),
559 attempts_count_(0),
560 had_tcp_attempt_(false),
561 first_server_index_(0) {
562 DCHECK(session_.get());
563 DCHECK(!hostname_.empty());
564 DCHECK(!callback_.is_null());
565 DCHECK(!IsIPLiteral(hostname_));
566 }
567
568 ~DnsTransactionImpl() override {
569 if (!callback_.is_null()) {
570 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION,
571 ERR_ABORTED);
572 } // otherwise logged in DoCallback or Start
573 }
574
575 const std::string& GetHostname() const override {
576 DCHECK(CalledOnValidThread());
577 return hostname_;
578 }
579
580 uint16 GetType() const override {
581 DCHECK(CalledOnValidThread());
582 return qtype_;
583 }
584
585 void Start() override {
586 DCHECK(!callback_.is_null());
587 DCHECK(attempts_.empty());
588 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION,
589 base::Bind(&NetLogStartCallback, &hostname_, qtype_));
590 AttemptResult result(PrepareSearch(), NULL);
591 if (result.rv == OK) {
592 qnames_initial_size_ = qnames_.size();
593 if (qtype_ == dns_protocol::kTypeA)
594 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchStart", qnames_.size());
595 result = ProcessAttemptResult(StartQuery());
596 }
597
598 // Must always return result asynchronously, to avoid reentrancy.
599 if (result.rv != ERR_IO_PENDING) {
600 base::MessageLoop::current()->PostTask(
601 FROM_HERE,
602 base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result));
603 }
604 }
605
606 private:
607 // Wrapper for the result of a DnsUDPAttempt.
608 struct AttemptResult {
609 AttemptResult(int rv, const DnsAttempt* attempt)
610 : rv(rv), attempt(attempt) {}
611
612 int rv;
613 const DnsAttempt* attempt;
614 };
615
616 // Prepares |qnames_| according to the DnsConfig.
617 int PrepareSearch() {
618 const DnsConfig& config = session_->config();
619
620 std::string labeled_hostname;
621 if (!DNSDomainFromDot(hostname_, &labeled_hostname))
622 return ERR_INVALID_ARGUMENT;
623
624 if (hostname_[hostname_.size() - 1] == '.') {
625 // It's a fully-qualified name, no suffix search.
626 qnames_.push_back(labeled_hostname);
627 return OK;
628 }
629
630 int ndots = CountLabels(labeled_hostname) - 1;
631
632 if (ndots > 0 && !config.append_to_multi_label_name) {
633 qnames_.push_back(labeled_hostname);
634 return OK;
635 }
636
637 // Set true when |labeled_hostname| is put on the list.
638 bool had_hostname = false;
639
640 if (ndots >= config.ndots) {
641 qnames_.push_back(labeled_hostname);
642 had_hostname = true;
643 }
644
645 std::string qname;
646 for (size_t i = 0; i < config.search.size(); ++i) {
647 // Ignore invalid (too long) combinations.
648 if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname))
649 continue;
650 if (qname.size() == labeled_hostname.size()) {
651 if (had_hostname)
652 continue;
653 had_hostname = true;
654 }
655 qnames_.push_back(qname);
656 }
657
658 if (ndots > 0 && !had_hostname)
659 qnames_.push_back(labeled_hostname);
660
661 return qnames_.empty() ? ERR_DNS_SEARCH_EMPTY : OK;
662 }
663
664 void DoCallback(AttemptResult result) {
665 DCHECK(!callback_.is_null());
666 DCHECK_NE(ERR_IO_PENDING, result.rv);
667 const DnsResponse* response = result.attempt ?
668 result.attempt->GetResponse() : NULL;
669 CHECK(result.rv != OK || response != NULL);
670
671 timer_.Stop();
672 RecordLostPacketsIfAny();
673 if (result.rv == OK)
674 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountSuccess", attempts_count_);
675 else
676 UMA_HISTOGRAM_COUNTS("AsyncDNS.AttemptCountFail", attempts_count_);
677
678 if (response && qtype_ == dns_protocol::kTypeA) {
679 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchRemain", qnames_.size());
680 UMA_HISTOGRAM_COUNTS("AsyncDNS.SuffixSearchDone",
681 qnames_initial_size_ - qnames_.size());
682 }
683
684 DnsTransactionFactory::CallbackType callback = callback_;
685 callback_.Reset();
686
687 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, result.rv);
688 callback.Run(this, result.rv, response);
689 }
690
691 // Makes another attempt at the current name, |qnames_.front()|, using the
692 // next nameserver.
693 AttemptResult MakeAttempt() {
694 unsigned attempt_number = attempts_.size();
695
696 uint16 id = session_->NextQueryId();
697 scoped_ptr<DnsQuery> query;
698 if (attempts_.empty()) {
699 query.reset(new DnsQuery(id, qnames_.front(), qtype_));
700 } else {
701 query.reset(attempts_[0]->GetQuery()->CloneWithNewId(id));
702 }
703
704 const DnsConfig& config = session_->config();
705
706 unsigned server_index =
707 (first_server_index_ + attempt_number) % config.nameservers.size();
708 // Skip over known failed servers.
709 server_index = session_->NextGoodServerIndex(server_index);
710
711 scoped_ptr<DnsSession::SocketLease> lease =
712 session_->AllocateSocket(server_index, net_log_.source());
713
714 bool got_socket = !!lease.get();
715
716 DnsUDPAttempt* attempt =
717 new DnsUDPAttempt(server_index, lease.Pass(), query.Pass());
718
719 attempts_.push_back(attempt);
720 ++attempts_count_;
721
722 if (!got_socket)
723 return AttemptResult(ERR_CONNECTION_REFUSED, NULL);
724
725 net_log_.AddEvent(
726 NetLog::TYPE_DNS_TRANSACTION_ATTEMPT,
727 attempt->GetSocketNetLog().source().ToEventParametersCallback());
728
729 int rv = attempt->Start(
730 base::Bind(&DnsTransactionImpl::OnUdpAttemptComplete,
731 base::Unretained(this), attempt_number,
732 base::TimeTicks::Now()));
733 if (rv == ERR_IO_PENDING) {
734 base::TimeDelta timeout = session_->NextTimeout(server_index,
735 attempt_number);
736 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
737 }
738 return AttemptResult(rv, attempt);
739 }
740
741 AttemptResult MakeTCPAttempt(const DnsAttempt* previous_attempt) {
742 DCHECK(previous_attempt);
743 DCHECK(!had_tcp_attempt_);
744
745 unsigned server_index = previous_attempt->server_index();
746
747 scoped_ptr<StreamSocket> socket(
748 session_->CreateTCPSocket(server_index, net_log_.source()));
749
750 // TODO(szym): Reuse the same id to help the server?
751 uint16 id = session_->NextQueryId();
752 scoped_ptr<DnsQuery> query(
753 previous_attempt->GetQuery()->CloneWithNewId(id));
754
755 RecordLostPacketsIfAny();
756 // Cancel all other attempts, no point waiting on them.
757 attempts_.clear();
758
759 unsigned attempt_number = attempts_.size();
760
761 DnsTCPAttempt* attempt = new DnsTCPAttempt(server_index, socket.Pass(),
762 query.Pass());
763
764 attempts_.push_back(attempt);
765 ++attempts_count_;
766 had_tcp_attempt_ = true;
767
768 net_log_.AddEvent(
769 NetLog::TYPE_DNS_TRANSACTION_TCP_ATTEMPT,
770 attempt->GetSocketNetLog().source().ToEventParametersCallback());
771
772 int rv = attempt->Start(base::Bind(&DnsTransactionImpl::OnAttemptComplete,
773 base::Unretained(this),
774 attempt_number));
775 if (rv == ERR_IO_PENDING) {
776 // Custom timeout for TCP attempt.
777 base::TimeDelta timeout = timer_.GetCurrentDelay() * 2;
778 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout);
779 }
780 return AttemptResult(rv, attempt);
781 }
782
783 // Begins query for the current name. Makes the first attempt.
784 AttemptResult StartQuery() {
785 std::string dotted_qname = DNSDomainToString(qnames_.front());
786 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION_QUERY,
787 NetLog::StringCallback("qname", &dotted_qname));
788
789 first_server_index_ = session_->NextFirstServerIndex();
790 RecordLostPacketsIfAny();
791 attempts_.clear();
792 had_tcp_attempt_ = false;
793 return MakeAttempt();
794 }
795
796 void OnUdpAttemptComplete(unsigned attempt_number,
797 base::TimeTicks start,
798 int rv) {
799 DCHECK_LT(attempt_number, attempts_.size());
800 const DnsAttempt* attempt = attempts_[attempt_number];
801 if (attempt->GetResponse()) {
802 session_->RecordRTT(attempt->server_index(),
803 base::TimeTicks::Now() - start);
804 }
805 OnAttemptComplete(attempt_number, rv);
806 }
807
808 void OnAttemptComplete(unsigned attempt_number, int rv) {
809 if (callback_.is_null())
810 return;
811 DCHECK_LT(attempt_number, attempts_.size());
812 const DnsAttempt* attempt = attempts_[attempt_number];
813 AttemptResult result = ProcessAttemptResult(AttemptResult(rv, attempt));
814 if (result.rv != ERR_IO_PENDING)
815 DoCallback(result);
816 }
817
818 // Record packet loss for any incomplete attempts.
819 void RecordLostPacketsIfAny() {
820 // Loop through attempts until we find first that is completed
821 size_t first_completed = 0;
822 for (first_completed = 0; first_completed < attempts_.size();
823 ++first_completed) {
824 if (attempts_[first_completed]->is_completed())
825 break;
826 }
827 // If there were no completed attempts, then we must be offline, so don't
828 // record any attempts as lost packets.
829 if (first_completed == attempts_.size())
830 return;
831
832 size_t num_servers = session_->config().nameservers.size();
833 std::vector<int> server_attempts(num_servers);
834 for (size_t i = 0; i < first_completed; ++i) {
835 unsigned server_index = attempts_[i]->server_index();
836 int server_attempt = server_attempts[server_index]++;
837 // Don't record lost packet unless attempt is in pending state.
838 if (!attempts_[i]->is_pending())
839 continue;
840 session_->RecordLostPacket(server_index, server_attempt);
841 }
842 }
843
844 void LogResponse(const DnsAttempt* attempt) {
845 if (attempt && attempt->GetResponse()) {
846 net_log_.AddEvent(
847 NetLog::TYPE_DNS_TRANSACTION_RESPONSE,
848 base::Bind(&DnsAttempt::NetLogResponseCallback,
849 base::Unretained(attempt)));
850 }
851 }
852
853 bool MoreAttemptsAllowed() const {
854 if (had_tcp_attempt_)
855 return false;
856 const DnsConfig& config = session_->config();
857 return attempts_.size() < config.attempts * config.nameservers.size();
858 }
859
860 // Resolves the result of a DnsAttempt until a terminal result is reached
861 // or it will complete asynchronously (ERR_IO_PENDING).
862 AttemptResult ProcessAttemptResult(AttemptResult result) {
863 while (result.rv != ERR_IO_PENDING) {
864 LogResponse(result.attempt);
865
866 switch (result.rv) {
867 case OK:
868 session_->RecordServerSuccess(result.attempt->server_index());
869 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
870 result.rv);
871 DCHECK(result.attempt);
872 DCHECK(result.attempt->GetResponse());
873 return result;
874 case ERR_NAME_NOT_RESOLVED:
875 session_->RecordServerSuccess(result.attempt->server_index());
876 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY,
877 result.rv);
878 // Try next suffix.
879 qnames_.pop_front();
880 if (qnames_.empty()) {
881 return AttemptResult(ERR_NAME_NOT_RESOLVED, NULL);
882 } else {
883 result = StartQuery();
884 }
885 break;
886 case ERR_CONNECTION_REFUSED:
887 case ERR_DNS_TIMED_OUT:
888 if (result.attempt)
889 session_->RecordServerFailure(result.attempt->server_index());
890 if (MoreAttemptsAllowed()) {
891 result = MakeAttempt();
892 } else {
893 return result;
894 }
895 break;
896 case ERR_DNS_SERVER_REQUIRES_TCP:
897 result = MakeTCPAttempt(result.attempt);
898 break;
899 default:
900 // Server failure.
901 DCHECK(result.attempt);
902 if (result.attempt != attempts_.back()) {
903 // This attempt already timed out. Ignore it.
904 session_->RecordServerFailure(result.attempt->server_index());
905 return AttemptResult(ERR_IO_PENDING, NULL);
906 }
907 if (MoreAttemptsAllowed()) {
908 result = MakeAttempt();
909 } else if (result.rv == ERR_DNS_MALFORMED_RESPONSE &&
910 !had_tcp_attempt_) {
911 // For UDP only, ignore the response and wait until the last attempt
912 // times out.
913 return AttemptResult(ERR_IO_PENDING, NULL);
914 } else {
915 return AttemptResult(result.rv, NULL);
916 }
917 break;
918 }
919 }
920 return result;
921 }
922
923 void OnTimeout() {
924 if (callback_.is_null())
925 return;
926 DCHECK(!attempts_.empty());
927 AttemptResult result = ProcessAttemptResult(
928 AttemptResult(ERR_DNS_TIMED_OUT, attempts_.back()));
929 if (result.rv != ERR_IO_PENDING)
930 DoCallback(result);
931 }
932
933 scoped_refptr<DnsSession> session_;
934 std::string hostname_;
935 uint16 qtype_;
936 // Cleared in DoCallback.
937 DnsTransactionFactory::CallbackType callback_;
938
939 BoundNetLog net_log_;
940
941 // Search list of fully-qualified DNS names to query next (in DNS format).
942 std::deque<std::string> qnames_;
943 size_t qnames_initial_size_;
944
945 // List of attempts for the current name.
946 ScopedVector<DnsAttempt> attempts_;
947 // Count of attempts, not reset when |attempts_| vector is cleared.
948 int attempts_count_;
949 bool had_tcp_attempt_;
950
951 // Index of the first server to try on each search query.
952 int first_server_index_;
953
954 base::OneShotTimer<DnsTransactionImpl> timer_;
955
956 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl);
957 };
958
959 // ----------------------------------------------------------------------------
960
961 // Implementation of DnsTransactionFactory that returns instances of
962 // DnsTransactionImpl.
963 class DnsTransactionFactoryImpl : public DnsTransactionFactory {
964 public:
965 explicit DnsTransactionFactoryImpl(DnsSession* session) {
966 session_ = session;
967 }
968
969 scoped_ptr<DnsTransaction> CreateTransaction(
970 const std::string& hostname,
971 uint16 qtype,
972 const CallbackType& callback,
973 const BoundNetLog& net_log) override {
974 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(
975 session_.get(), hostname, qtype, callback, net_log));
976 }
977
978 private:
979 scoped_refptr<DnsSession> session_;
980 };
981
982 } // namespace
983
984 // static
985 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory(
986 DnsSession* session) {
987 return scoped_ptr<DnsTransactionFactory>(
988 new DnsTransactionFactoryImpl(session));
989 }
990
991 } // namespace net
OLDNEW
« no previous file with comments | « net/dns/dns_transaction.h ('k') | net/dns/dns_transaction_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698