OLD | NEW |
| (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 | |
OLD | NEW |