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"
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"
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
) {
49 for (size_t i
= 0; i
< name
.size() && name
[i
]; i
+= name
[i
] + 1)
54 bool IsIPLiteral(const std::string
& hostname
) {
56 return ParseIPLiteralToNumber(hostname
, &ip
);
59 Value
* NetLogStartCallback(const std::string
* hostname
,
61 NetLog::LogLevel
/* log_level */) {
62 DictionaryValue
* dict
= new DictionaryValue();
63 dict
->SetString("hostname", *hostname
);
64 dict
->SetInteger("query_type", qtype
);
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.
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()),
87 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously
88 // and calls |callback| upon completion.
90 DCHECK_EQ(STATE_NONE
, next_state_
);
91 int rv
= socket_
->Connect(server_
);
92 DCHECK_NE(ERR_IO_PENDING
, rv
);
95 start_time_
= base::TimeTicks::Now();
96 next_state_
= STATE_SEND_QUERY
;
100 const DnsQuery
* query() const {
104 const DatagramClientSocket
* socket() const {
105 return socket_
.get();
108 // Returns the response or NULL if has not received a matching response from
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
);
131 STATE_SEND_QUERY_COMPLETE
,
133 STATE_READ_RESPONSE_COMPLETE
,
137 int DoLoop(int result
) {
138 CHECK_NE(STATE_NONE
, next_state_
);
141 State state
= next_state_
;
142 next_state_
= STATE_NONE
;
144 case STATE_SEND_QUERY
:
147 case STATE_SEND_QUERY_COMPLETE
:
148 rv
= DoSendQueryComplete(rv
);
150 case STATE_READ_RESPONSE
:
151 rv
= DoReadResponse();
153 case STATE_READ_RESPONSE_COMPLETE
:
154 rv
= DoReadResponseComplete(rv
);
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
;
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_
);
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
);
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
;
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
);
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
;
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
;
233 void OnIOComplete(int rv
) {
235 if (rv
!= ERR_IO_PENDING
)
240 bool received_malformed_response_
;
241 base::TimeTicks start_time_
;
243 scoped_ptr
<DatagramClientSocket
> socket_
;
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
> {
266 DnsTransactionImpl(DnsSession
* session
,
267 const std::string
& hostname
,
269 const DnsTransactionFactory::CallbackType
& callback
,
270 const BoundNetLog
& net_log
)
276 first_server_index_(0) {
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
,
287 } // otherwise logged in DoCallback or Start
290 virtual const std::string
& GetHostname() const OVERRIDE
{
291 DCHECK(CalledOnValidThread());
295 virtual uint16
GetType() const OVERRIDE
{
296 DCHECK(CalledOnValidThread());
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();
307 AttemptResult result
= ProcessAttemptResult(StartQuery());
308 if (result
.rv
== OK
) {
309 // DnsTransaction must never succeed synchronously.
310 MessageLoop::current()->PostTask(
312 base::Bind(&DnsTransactionImpl::DoCallback
, AsWeakPtr(), result
));
313 return ERR_IO_PENDING
;
317 if (rv
!= ERR_IO_PENDING
) {
319 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION
, rv
);
326 // Wrapper for the result of a DnsUDPAttempt.
327 struct AttemptResult
{
328 AttemptResult(int rv
, const DnsUDPAttempt
* attempt
)
329 : rv(rv
), attempt(attempt
) {}
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
);
349 int ndots
= CountLabels(labeled_hostname
) - 1;
351 if (ndots
> 0 && !config
.append_to_multi_label_name
) {
352 qnames_
.push_back(labeled_hostname
);
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
);
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
))
369 if (qname
.size() == labeled_hostname
.size()) {
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
);
392 DnsTransactionFactory::CallbackType callback
= callback_
;
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
401 AttemptResult
MakeAttempt() {
402 unsigned attempt_number
= attempts_
.size();
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
;
409 DatagramSocket::BindType bind_type
= DatagramSocket::RANDOM_BIND
;
412 scoped_ptr
<DatagramClientSocket
> socket(
413 session_
->socket_factory()->CreateDatagramClientSocket(
415 base::Bind(&base::RandInt
),
419 uint16 id
= session_
->NextQueryId();
420 scoped_ptr
<DnsQuery
> query
;
421 if (attempts_
.empty()) {
422 query
.reset(new DnsQuery(id
, qnames_
.front(), qtype_
));
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(
437 config
.nameservers
[server_index
],
439 base::Bind(&DnsTransactionImpl::OnAttemptComplete
,
440 base::Unretained(this),
443 attempts_
.push_back(attempt
);
445 int rv
= attempt
->Start();
446 if (rv
== ERR_IO_PENDING
) {
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();
463 return MakeAttempt();
466 void OnAttemptComplete(unsigned attempt_number
, int rv
) {
467 if (callback_
.is_null())
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
)
476 void LogResponse(const DnsUDPAttempt
* attempt
) {
477 if (attempt
&& attempt
->response()) {
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
);
498 net_log_
.EndEventWithNetErrorCode(
499 NetLog::TYPE_DNS_TRANSACTION_QUERY
, result
.rv
);
500 DCHECK(result
.attempt
);
501 DCHECK(result
.attempt
->response());
503 case ERR_NAME_NOT_RESOLVED
:
504 net_log_
.EndEventWithNetErrorCode(
505 NetLog::TYPE_DNS_TRANSACTION_QUERY
, result
.rv
);
508 if (qnames_
.empty()) {
509 return AttemptResult(ERR_NAME_NOT_RESOLVED
, NULL
);
511 result
= StartQuery();
514 case ERR_DNS_TIMED_OUT
:
515 if (MoreAttemptsAllowed()) {
516 result
= MakeAttempt();
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
);
534 return AttemptResult(result
.rv
, NULL
);
543 if (callback_
.is_null())
545 AttemptResult result
= ProcessAttemptResult(
546 AttemptResult(ERR_DNS_TIMED_OUT
, NULL
));
547 if (result
.rv
!= ERR_IO_PENDING
)
551 scoped_refptr
<DnsSession
> session_
;
552 std::string hostname_
;
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
{
579 explicit DnsTransactionFactoryImpl(DnsSession
* session
) {
583 virtual scoped_ptr
<DnsTransaction
> CreateTransaction(
584 const std::string
& hostname
,
586 const CallbackType
& callback
,
587 const BoundNetLog
& net_log
) OVERRIDE
{
588 return scoped_ptr
<DnsTransaction
>(new DnsTransactionImpl(session_
,
596 scoped_refptr
<DnsSession
> session_
;
602 scoped_ptr
<DnsTransactionFactory
> DnsTransactionFactory::CreateFactory(
603 DnsSession
* session
) {
604 return scoped_ptr
<DnsTransactionFactory
>(
605 new DnsTransactionFactoryImpl(session
));