OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/dns/dns_transaction.h" | 5 #include "net/dns/dns_transaction.h" |
6 | 6 |
| 7 #include <deque> |
| 8 #include <string> |
| 9 #include <vector> |
| 10 |
7 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/memory/ref_counted.h" |
| 13 #include "base/memory/scoped_ptr.h" |
| 14 #include "base/memory/weak_ptr.h" |
| 15 #include "base/message_loop.h" |
8 #include "base/rand_util.h" | 16 #include "base/rand_util.h" |
9 #include "base/values.h" | 17 #include "base/stl_util.h" |
| 18 #include "base/string_piece.h" |
| 19 #include "base/threading/non_thread_safe.h" |
| 20 #include "base/timer.h" |
| 21 #include "net/base/completion_callback.h" |
| 22 #include "net/base/dns_util.h" |
10 #include "net/base/io_buffer.h" | 23 #include "net/base/io_buffer.h" |
| 24 #include "net/base/ip_endpoint.h" |
11 #include "net/base/net_errors.h" | 25 #include "net/base/net_errors.h" |
| 26 #include "net/base/net_log.h" |
12 #include "net/dns/dns_protocol.h" | 27 #include "net/dns/dns_protocol.h" |
13 #include "net/dns/dns_query.h" | 28 #include "net/dns/dns_query.h" |
14 #include "net/dns/dns_response.h" | 29 #include "net/dns/dns_response.h" |
15 #include "net/dns/dns_session.h" | 30 #include "net/dns/dns_session.h" |
16 #include "net/socket/client_socket_factory.h" | 31 #include "net/socket/client_socket_factory.h" |
17 #include "net/udp/datagram_client_socket.h" | 32 #include "net/udp/datagram_client_socket.h" |
18 | 33 |
19 namespace net { | 34 namespace net { |
20 | 35 |
21 namespace { | 36 namespace { |
22 | 37 |
23 class DnsTransactionStartParameters : public NetLog::EventParameters { | 38 // Count labels in the fully-qualified name in DNS format. |
| 39 int CountLabels(const std::string& name) { |
| 40 size_t count = 0; |
| 41 for (size_t i = 0; i < name.size() && name[i]; i += name[i] + 1) |
| 42 ++count; |
| 43 return count; |
| 44 } |
| 45 |
| 46 bool IsIPLiteral(const std::string& hostname) { |
| 47 IPAddressNumber ip; |
| 48 return ParseIPLiteralToNumber(hostname, &ip); |
| 49 } |
| 50 |
| 51 class StartParameters : public NetLog::EventParameters { |
24 public: | 52 public: |
25 DnsTransactionStartParameters(const IPEndPoint& dns_server, | 53 StartParameters(const std::string& hostname, |
26 const base::StringPiece& qname, | 54 uint16 qtype, |
27 uint16 qtype, | 55 const NetLog::Source& source) |
28 const NetLog::Source& source) | 56 : hostname_(hostname), qtype_(qtype), source_(source) {} |
29 : dns_server_(dns_server), | 57 |
30 qname_(qname.data(), qname.length()), | 58 virtual Value* ToValue() const OVERRIDE { |
31 qtype_(qtype), | |
32 source_(source) {} | |
33 | |
34 virtual Value* ToValue() const { | |
35 DictionaryValue* dict = new DictionaryValue(); | 59 DictionaryValue* dict = new DictionaryValue(); |
36 dict->SetString("dns_server", dns_server_.ToString()); | 60 dict->SetString("hostname", hostname_); |
37 dict->SetString("hostname", qname_); | |
38 dict->SetInteger("query_type", qtype_); | 61 dict->SetInteger("query_type", qtype_); |
39 if (source_.is_valid()) | 62 dict->Set("source_dependency", source_.ToValue()); |
40 dict->Set("source_dependency", source_.ToValue()); | |
41 return dict; | 63 return dict; |
42 } | 64 } |
43 | 65 |
44 private: | 66 private: |
45 IPEndPoint dns_server_; | 67 const std::string hostname_; |
46 std::string qname_; | 68 const uint16 qtype_; |
47 uint16 qtype_; | |
48 const NetLog::Source source_; | 69 const NetLog::Source source_; |
49 }; | 70 }; |
50 | 71 |
51 class DnsTransactionFinishParameters : public NetLog::EventParameters { | 72 class ResponseParameters : public NetLog::EventParameters { |
52 public: | 73 public: |
53 // TODO(szym): add rcode ? | 74 ResponseParameters(int rcode, int answer_count, const NetLog::Source& source) |
54 DnsTransactionFinishParameters(int net_error, int answer_count) | 75 : rcode_(rcode), answer_count_(answer_count), source_(source) {} |
55 : net_error_(net_error), answer_count_(answer_count) {} | 76 |
56 | 77 virtual Value* ToValue() const OVERRIDE { |
57 virtual Value* ToValue() const { | |
58 DictionaryValue* dict = new DictionaryValue(); | 78 DictionaryValue* dict = new DictionaryValue(); |
59 if (net_error_) | 79 dict->SetInteger("rcode", rcode_); |
60 dict->SetInteger("net_error", net_error_); | |
61 dict->SetInteger("answer_count", answer_count_); | 80 dict->SetInteger("answer_count", answer_count_); |
| 81 dict->Set("socket_source", source_.ToValue()); |
62 return dict; | 82 return dict; |
63 } | 83 } |
64 | 84 |
65 private: | 85 private: |
66 const int net_error_; | 86 const int rcode_; |
67 const int answer_count_; | 87 const int answer_count_; |
68 }; | |
69 | |
70 class DnsTransactionRetryParameters : public NetLog::EventParameters { | |
71 public: | |
72 DnsTransactionRetryParameters(int attempt_number, | |
73 const NetLog::Source& source) | |
74 : attempt_number_(attempt_number), source_(source) {} | |
75 | |
76 virtual Value* ToValue() const { | |
77 DictionaryValue* dict = new DictionaryValue(); | |
78 dict->SetInteger("attempt_number", attempt_number_); | |
79 dict->Set("source_dependency", source_.ToValue()); | |
80 return dict; | |
81 } | |
82 | |
83 private: | |
84 const int attempt_number_; | |
85 const NetLog::Source source_; | 88 const NetLog::Source source_; |
86 }; | 89 }; |
87 | 90 |
| 91 // ---------------------------------------------------------------------------- |
| 92 |
| 93 // A single asynchronous DNS exchange over UDP, which consists of sending out a |
| 94 // DNS query, waiting for a response, and returning the response that it |
| 95 // matches. Logging is done in the socket and in the outer DnsTransaction. |
| 96 class DnsUDPAttempt { |
| 97 public: |
| 98 DnsUDPAttempt(scoped_ptr<DatagramClientSocket> socket, |
| 99 const IPEndPoint& server, |
| 100 scoped_ptr<DnsQuery> query, |
| 101 const CompletionCallback& callback) |
| 102 : next_state_(STATE_NONE), |
| 103 socket_(socket.Pass()), |
| 104 server_(server), |
| 105 query_(query.Pass()), |
| 106 callback_(callback) { |
| 107 } |
| 108 |
| 109 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously |
| 110 // and calls |callback| upon completion. |
| 111 int Start() { |
| 112 DCHECK_EQ(STATE_NONE, next_state_); |
| 113 next_state_ = STATE_CONNECT; |
| 114 return DoLoop(OK); |
| 115 } |
| 116 |
| 117 const DnsQuery* query() const { |
| 118 return query_.get(); |
| 119 } |
| 120 |
| 121 const DatagramClientSocket* socket() const { |
| 122 return socket_.get(); |
| 123 } |
| 124 |
| 125 // Returns the response or NULL if has not received a matching response from |
| 126 // the server. |
| 127 const DnsResponse* response() const { |
| 128 const DnsResponse* resp = response_.get(); |
| 129 return (resp != NULL && resp->IsValid()) ? resp : NULL; |
| 130 } |
| 131 |
| 132 private: |
| 133 enum State { |
| 134 STATE_CONNECT, |
| 135 STATE_SEND_QUERY, |
| 136 STATE_SEND_QUERY_COMPLETE, |
| 137 STATE_READ_RESPONSE, |
| 138 STATE_READ_RESPONSE_COMPLETE, |
| 139 STATE_NONE, |
| 140 }; |
| 141 |
| 142 int DoLoop(int result) { |
| 143 DCHECK_NE(STATE_NONE, next_state_); |
| 144 int rv = result; |
| 145 do { |
| 146 State state = next_state_; |
| 147 next_state_ = STATE_NONE; |
| 148 switch (state) { |
| 149 case STATE_CONNECT: |
| 150 rv = DoConnect(); |
| 151 break; |
| 152 case STATE_SEND_QUERY: |
| 153 rv = DoSendQuery(); |
| 154 break; |
| 155 case STATE_SEND_QUERY_COMPLETE: |
| 156 rv = DoSendQueryComplete(rv); |
| 157 break; |
| 158 case STATE_READ_RESPONSE: |
| 159 rv = DoReadResponse(); |
| 160 break; |
| 161 case STATE_READ_RESPONSE_COMPLETE: |
| 162 rv = DoReadResponseComplete(rv); |
| 163 break; |
| 164 default: |
| 165 NOTREACHED(); |
| 166 break; |
| 167 } |
| 168 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); |
| 169 |
| 170 return rv; |
| 171 } |
| 172 |
| 173 int DoConnect() { |
| 174 next_state_ = STATE_SEND_QUERY; |
| 175 return socket_->Connect(server_); |
| 176 } |
| 177 |
| 178 int DoSendQuery() { |
| 179 next_state_ = STATE_SEND_QUERY_COMPLETE; |
| 180 return socket_->Write(query_->io_buffer(), |
| 181 query_->io_buffer()->size(), |
| 182 base::Bind(&DnsUDPAttempt::OnIOComplete, |
| 183 base::Unretained(this))); |
| 184 } |
| 185 |
| 186 int DoSendQueryComplete(int rv) { |
| 187 DCHECK_NE(ERR_IO_PENDING, rv); |
| 188 if (rv < 0) |
| 189 return rv; |
| 190 |
| 191 // Writing to UDP should not result in a partial datagram. |
| 192 if (rv != query_->io_buffer()->size()) |
| 193 return ERR_MSG_TOO_BIG; |
| 194 |
| 195 next_state_ = STATE_READ_RESPONSE; |
| 196 return OK; |
| 197 } |
| 198 |
| 199 int DoReadResponse() { |
| 200 next_state_ = STATE_READ_RESPONSE_COMPLETE; |
| 201 response_.reset(new DnsResponse()); |
| 202 return socket_->Read(response_->io_buffer(), |
| 203 response_->io_buffer()->size(), |
| 204 base::Bind(&DnsUDPAttempt::OnIOComplete, |
| 205 base::Unretained(this))); |
| 206 } |
| 207 |
| 208 int DoReadResponseComplete(int rv) { |
| 209 DCHECK_NE(ERR_IO_PENDING, rv); |
| 210 if (rv < 0) |
| 211 return rv; |
| 212 |
| 213 DCHECK(rv); |
| 214 if (!response_->InitParse(rv, *query_)) |
| 215 return ERR_DNS_MALFORMED_RESPONSE; |
| 216 if (response_->flags() & dns_protocol::kFlagTC) |
| 217 return ERR_DNS_SERVER_REQUIRES_TCP; |
| 218 if (response_->rcode() != dns_protocol::kRcodeNOERROR && |
| 219 response_->rcode() != dns_protocol::kRcodeNXDOMAIN) { |
| 220 return ERR_DNS_SERVER_FAILED; |
| 221 } |
| 222 if (response_->answer_count() == 0) |
| 223 return ERR_NAME_NOT_RESOLVED; |
| 224 |
| 225 return OK; |
| 226 } |
| 227 |
| 228 void OnIOComplete(int rv) { |
| 229 rv = DoLoop(rv); |
| 230 if (rv != ERR_IO_PENDING) |
| 231 callback_.Run(rv); |
| 232 } |
| 233 |
| 234 State next_state_; |
| 235 |
| 236 scoped_ptr<DatagramClientSocket> socket_; |
| 237 IPEndPoint server_; |
| 238 scoped_ptr<DnsQuery> query_; |
| 239 |
| 240 scoped_ptr<DnsResponse> response_; |
| 241 |
| 242 CompletionCallback callback_; |
| 243 |
| 244 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt); |
| 245 }; |
| 246 |
| 247 // ---------------------------------------------------------------------------- |
| 248 |
| 249 // Implements DnsTransaction. Configuration is supplied by DnsSession. |
| 250 // The suffix list is built according to the DnsConfig from the session. |
| 251 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout. |
| 252 // The first server to attempt on each query is given by |
| 253 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards. |
| 254 // Each server is attempted DnsConfig::attempts times. |
| 255 class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe { |
| 256 public: |
| 257 DnsTransactionImpl(DnsSession* session, |
| 258 const std::string& hostname, |
| 259 uint16 qtype, |
| 260 const DnsTransactionFactory::CallbackType& callback, |
| 261 const BoundNetLog& source_net_log) |
| 262 : session_(session), |
| 263 hostname_(hostname), |
| 264 qtype_(qtype), |
| 265 callback_(callback), |
| 266 net_log_(BoundNetLog::Make(session->net_log(), |
| 267 NetLog::SOURCE_DNS_TRANSACTION)), |
| 268 first_server_index_(0) { |
| 269 DCHECK(session_); |
| 270 DCHECK(!hostname_.empty()); |
| 271 DCHECK(!callback_.is_null()); |
| 272 |
| 273 DCHECK(!IsIPLiteral(hostname_)); |
| 274 |
| 275 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION, make_scoped_refptr( |
| 276 new StartParameters(hostname_, qtype_, source_net_log.source()))); |
| 277 } |
| 278 |
| 279 virtual ~DnsTransactionImpl() { |
| 280 STLDeleteElements(&attempts_); |
| 281 if (!callback_.is_null()) { |
| 282 net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL); |
| 283 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, |
| 284 ERR_ABORTED); |
| 285 } |
| 286 } |
| 287 |
| 288 virtual const std::string& GetHostname() const OVERRIDE { |
| 289 DCHECK(CalledOnValidThread()); |
| 290 return hostname_; |
| 291 } |
| 292 |
| 293 virtual uint16 GetType() const OVERRIDE { |
| 294 DCHECK(CalledOnValidThread()); |
| 295 return qtype_; |
| 296 } |
| 297 |
| 298 virtual int Start() OVERRIDE { |
| 299 int rv = PrepareSearch(); |
| 300 if (rv == OK) |
| 301 rv = StartQuery(); |
| 302 DCHECK_NE(OK, rv); |
| 303 return rv; |
| 304 } |
| 305 |
| 306 private: |
| 307 // Prepares |qnames_| according to the DnsConfig. |
| 308 int PrepareSearch() { |
| 309 const DnsConfig& config = session_->config(); |
| 310 |
| 311 std::string labelled_hostname; |
| 312 if (!DNSDomainFromDot(hostname_, &labelled_hostname)) |
| 313 return ERR_INVALID_ARGUMENT; |
| 314 |
| 315 if (hostname_[hostname_.size() - 1] == '.') { |
| 316 // It's a fully-qualified name, no suffix search. |
| 317 qnames_.push_back(labelled_hostname); |
| 318 return OK; |
| 319 } |
| 320 |
| 321 // Set true when |labelled_hostname| is put on the list. |
| 322 bool had_hostname = false; |
| 323 |
| 324 int ndots = CountLabels(labelled_hostname) - 1; |
| 325 if (ndots >= config.ndots) { |
| 326 qnames_.push_back(labelled_hostname); |
| 327 had_hostname = true; |
| 328 } |
| 329 |
| 330 std::string qname; |
| 331 for (size_t i = 0; i < config.search.size(); ++i) { |
| 332 // Ignore invalid (too long) combinations. |
| 333 if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname)) |
| 334 continue; |
| 335 if (qname.size() == labelled_hostname.size()) { |
| 336 if (had_hostname) |
| 337 continue; |
| 338 had_hostname = true; |
| 339 } |
| 340 qnames_.push_back(qname); |
| 341 } |
| 342 |
| 343 if (!had_hostname) |
| 344 qnames_.push_back(labelled_hostname); |
| 345 |
| 346 return OK; |
| 347 } |
| 348 |
| 349 void DoCallback(int rv, const DnsUDPAttempt* successful_attempt) { |
| 350 if (callback_.is_null()) |
| 351 return; |
| 352 DCHECK_NE(ERR_IO_PENDING, rv); |
| 353 DCHECK(rv != OK || successful_attempt != NULL); |
| 354 |
| 355 DnsTransactionFactory::CallbackType callback = callback_; |
| 356 callback_.Reset(); |
| 357 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, rv); |
| 358 callback.Run(this, |
| 359 rv, |
| 360 successful_attempt ? successful_attempt->response() : NULL); |
| 361 } |
| 362 |
| 363 // Makes another attempt at the current name, |qnames_.front()|, using the |
| 364 // next nameserver. |
| 365 int MakeAttempt() { |
| 366 unsigned attempt_number = attempts_.size(); |
| 367 |
| 368 scoped_ptr<DatagramClientSocket> socket( |
| 369 session_->socket_factory()->CreateDatagramClientSocket( |
| 370 DatagramSocket::RANDOM_BIND, |
| 371 base::Bind(&base::RandInt), |
| 372 net_log_.net_log(), |
| 373 net_log_.source())); |
| 374 |
| 375 uint16 id = session_->NextQueryId(); |
| 376 scoped_ptr<DnsQuery> query; |
| 377 if (attempts_.empty()) { |
| 378 query.reset(new DnsQuery(id, qnames_.front(), qtype_)); |
| 379 } else { |
| 380 query.reset(attempts_[0]->query()->CloneWithNewId(id)); |
| 381 } |
| 382 |
| 383 net_log_.AddEvent(NetLog::TYPE_DNS_TRANSACTION_ATTEMPT, make_scoped_refptr( |
| 384 new NetLogSourceParameter("socket_source", socket->NetLog().source()))); |
| 385 |
| 386 const DnsConfig& config = session_->config(); |
| 387 |
| 388 unsigned server_index = first_server_index_ + |
| 389 (attempt_number % config.nameservers.size()); |
| 390 |
| 391 DnsUDPAttempt* attempt = new DnsUDPAttempt( |
| 392 socket.Pass(), |
| 393 config.nameservers[server_index], |
| 394 query.Pass(), |
| 395 base::Bind(&DnsTransactionImpl::OnAttemptComplete, |
| 396 base::Unretained(this), |
| 397 attempt_number)); |
| 398 |
| 399 base::TimeDelta timeout = session_->NextTimeout(attempt_number); |
| 400 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout); |
| 401 attempts_.push_back(attempt); |
| 402 return attempt->Start(); |
| 403 } |
| 404 |
| 405 // Begins query for the current name. Makes the first attempt. |
| 406 int StartQuery() { |
| 407 std::string dotted_qname = DNSDomainToString(qnames_.front()); |
| 408 net_log_.BeginEvent( |
| 409 NetLog::TYPE_DNS_TRANSACTION_QUERY, |
| 410 make_scoped_refptr(new NetLogStringParameter("qname", dotted_qname))); |
| 411 |
| 412 first_server_index_ = session_->NextFirstServerIndex(); |
| 413 |
| 414 STLDeleteElements(&attempts_); |
| 415 return MakeAttempt(); |
| 416 } |
| 417 |
| 418 void OnAttemptComplete(unsigned attempt_number, int rv) { |
| 419 DCHECK_LT(attempt_number, attempts_.size()); |
| 420 timer_.Stop(); |
| 421 |
| 422 const DnsUDPAttempt* attempt = attempts_[attempt_number]; |
| 423 |
| 424 if (attempt->response()) { |
| 425 net_log_.AddEvent( |
| 426 NetLog::TYPE_DNS_TRANSACTION_RESPONSE, |
| 427 make_scoped_refptr( |
| 428 new ResponseParameters(attempt->response()->rcode(), |
| 429 attempt->response()->answer_count(), |
| 430 attempt->socket()->NetLog().source()))); |
| 431 } |
| 432 |
| 433 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY, rv); |
| 434 |
| 435 switch (rv) { |
| 436 case ERR_NAME_NOT_RESOLVED: |
| 437 // Try next suffix. |
| 438 qnames_.pop_front(); |
| 439 if (qnames_.empty()) |
| 440 rv = ERR_NAME_NOT_RESOLVED; |
| 441 else |
| 442 rv = StartQuery(); |
| 443 break; |
| 444 case OK: |
| 445 DoCallback(rv, attempt); |
| 446 return; |
| 447 default: |
| 448 // TODO(szym): Some nameservers could fail so try the next one. |
| 449 const DnsConfig& config = session_->config(); |
| 450 if (attempts_.size() < config.attempts * config.nameservers.size()) { |
| 451 rv = MakeAttempt(); |
| 452 } else { |
| 453 // TODO(szym): Should this be different than the timeout case? |
| 454 rv = ERR_DNS_SERVER_FAILED; |
| 455 } |
| 456 break; |
| 457 } |
| 458 if (rv != ERR_IO_PENDING) |
| 459 DoCallback(rv, NULL); |
| 460 } |
| 461 |
| 462 void OnTimeout() { |
| 463 const DnsConfig& config = session_->config(); |
| 464 if (attempts_.size() == config.attempts * config.nameservers.size()) { |
| 465 DoCallback(ERR_DNS_TIMED_OUT, NULL); |
| 466 return; |
| 467 } |
| 468 int rv = MakeAttempt(); |
| 469 if (rv != ERR_IO_PENDING) |
| 470 DoCallback(rv, NULL); |
| 471 } |
| 472 |
| 473 scoped_refptr<DnsSession> session_; |
| 474 std::string hostname_; |
| 475 uint16 qtype_; |
| 476 // Set to null once the transaction completes. |
| 477 DnsTransactionFactory::CallbackType callback_; |
| 478 |
| 479 BoundNetLog net_log_; |
| 480 |
| 481 // Search list of fully-qualified DNS names to query next (in DNS format). |
| 482 std::deque<std::string> qnames_; |
| 483 |
| 484 // List of attempts for the current name. |
| 485 std::vector<DnsUDPAttempt*> attempts_; |
| 486 |
| 487 // Index of the first server to try on each search query. |
| 488 int first_server_index_; |
| 489 |
| 490 base::OneShotTimer<DnsTransactionImpl> timer_; |
| 491 |
| 492 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl); |
| 493 }; |
| 494 |
| 495 // ---------------------------------------------------------------------------- |
| 496 |
| 497 // Implementation of DnsTransactionFactory that returns instances of |
| 498 // DnsTransactionImpl. |
| 499 class DnsTransactionFactoryImpl : public DnsTransactionFactory { |
| 500 public: |
| 501 explicit DnsTransactionFactoryImpl(DnsSession* session) { |
| 502 session_ = session; |
| 503 } |
| 504 |
| 505 virtual scoped_ptr<DnsTransaction> CreateTransaction( |
| 506 const std::string& hostname, |
| 507 uint16 qtype, |
| 508 const CallbackType& callback, |
| 509 const BoundNetLog& source_net_log) OVERRIDE { |
| 510 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(session_, |
| 511 hostname, |
| 512 qtype, |
| 513 callback, |
| 514 source_net_log)); |
| 515 } |
| 516 |
| 517 private: |
| 518 scoped_refptr<DnsSession> session_; |
| 519 }; |
| 520 |
88 } // namespace | 521 } // namespace |
89 | 522 |
90 | 523 // static |
91 DnsTransaction::DnsTransaction(DnsSession* session, | 524 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory( |
92 const base::StringPiece& qname, | 525 DnsSession* session) { |
93 uint16 qtype, | 526 return scoped_ptr<DnsTransactionFactory>( |
94 const ResultCallback& callback, | 527 new DnsTransactionFactoryImpl(session)); |
95 const BoundNetLog& source_net_log) | |
96 : session_(session), | |
97 dns_server_(session->NextServer()), | |
98 query_(new DnsQuery(session->NextId(), qname, qtype)), | |
99 callback_(callback), | |
100 attempts_(0), | |
101 next_state_(STATE_NONE), | |
102 net_log_(BoundNetLog::Make(session->net_log(), | |
103 NetLog::SOURCE_DNS_TRANSACTION)) { | |
104 net_log_.BeginEvent( | |
105 NetLog::TYPE_DNS_TRANSACTION, | |
106 make_scoped_refptr( | |
107 new DnsTransactionStartParameters(dns_server_, | |
108 qname, | |
109 qtype, | |
110 source_net_log.source()))); | |
111 } | 528 } |
112 | 529 |
113 DnsTransaction::~DnsTransaction() {} | |
114 | |
115 int DnsTransaction::Start() { | |
116 DCHECK_EQ(STATE_NONE, next_state_); | |
117 next_state_ = STATE_CONNECT; | |
118 return DoLoop(OK); | |
119 } | |
120 | |
121 int DnsTransaction::DoLoop(int result) { | |
122 DCHECK_NE(STATE_NONE, next_state_); | |
123 int rv = result; | |
124 do { | |
125 State state = next_state_; | |
126 next_state_ = STATE_NONE; | |
127 switch (state) { | |
128 case STATE_CONNECT: | |
129 rv = DoConnect(); | |
130 break; | |
131 case STATE_CONNECT_COMPLETE: | |
132 rv = DoConnectComplete(rv); | |
133 break; | |
134 case STATE_SEND_QUERY: | |
135 rv = DoSendQuery(); | |
136 break; | |
137 case STATE_SEND_QUERY_COMPLETE: | |
138 rv = DoSendQueryComplete(rv); | |
139 break; | |
140 case STATE_READ_RESPONSE: | |
141 rv = DoReadResponse(); | |
142 break; | |
143 case STATE_READ_RESPONSE_COMPLETE: | |
144 rv = DoReadResponseComplete(rv); | |
145 break; | |
146 default: | |
147 NOTREACHED(); | |
148 break; | |
149 } | |
150 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
151 | |
152 return rv; | |
153 } | |
154 | |
155 void DnsTransaction::DoCallback(int result) { | |
156 DCHECK_NE(result, ERR_IO_PENDING); | |
157 int answer_count = (result == OK) ? response()->answer_count() : 0; | |
158 net_log_.EndEvent( | |
159 NetLog::TYPE_DNS_TRANSACTION, | |
160 make_scoped_refptr( | |
161 new DnsTransactionFinishParameters(result, answer_count))); | |
162 callback_.Run(this, result); | |
163 } | |
164 | |
165 void DnsTransaction::OnIOComplete(int result) { | |
166 int rv = DoLoop(result); | |
167 if (rv != ERR_IO_PENDING) | |
168 DoCallback(rv); | |
169 } | |
170 | |
171 int DnsTransaction::DoConnect() { | |
172 next_state_ = STATE_CONNECT_COMPLETE; | |
173 | |
174 StartTimer(session_->NextTimeout(attempts_)); | |
175 ++attempts_; | |
176 | |
177 // TODO(szym): keep all sockets around in case the server responds | |
178 // after its timeout; state machine will need to change to handle that. | |
179 // The current plan is to move socket management out to DnsSession. | |
180 // Hence also move retransmissions to DnsClient::Request. | |
181 socket_.reset(session_->socket_factory()->CreateDatagramClientSocket( | |
182 DatagramSocket::RANDOM_BIND, | |
183 base::Bind(&base::RandInt), | |
184 net_log_.net_log(), | |
185 net_log_.source())); | |
186 | |
187 net_log_.AddEvent( | |
188 NetLog::TYPE_DNS_TRANSACTION_ATTEMPT_STARTED, | |
189 make_scoped_refptr( | |
190 new DnsTransactionRetryParameters(attempts_, | |
191 socket_->NetLog().source()))); | |
192 | |
193 return socket_->Connect(dns_server_); | |
194 } | |
195 | |
196 int DnsTransaction::DoConnectComplete(int rv) { | |
197 if (rv < 0) | |
198 return rv; | |
199 next_state_ = STATE_SEND_QUERY; | |
200 return OK; | |
201 } | |
202 | |
203 int DnsTransaction::DoSendQuery() { | |
204 next_state_ = STATE_SEND_QUERY_COMPLETE; | |
205 return socket_->Write(query_->io_buffer(), | |
206 query_->io_buffer()->size(), | |
207 base::Bind(&DnsTransaction::OnIOComplete, | |
208 base::Unretained(this))); | |
209 } | |
210 | |
211 int DnsTransaction::DoSendQueryComplete(int rv) { | |
212 if (rv < 0) | |
213 return rv; | |
214 | |
215 // Writing to UDP should not result in a partial datagram. | |
216 if (rv != query_->io_buffer()->size()) | |
217 return ERR_MSG_TOO_BIG; | |
218 | |
219 next_state_ = STATE_READ_RESPONSE; | |
220 return OK; | |
221 } | |
222 | |
223 int DnsTransaction::DoReadResponse() { | |
224 next_state_ = STATE_READ_RESPONSE_COMPLETE; | |
225 response_.reset(new DnsResponse()); | |
226 return socket_->Read(response_->io_buffer(), | |
227 response_->io_buffer()->size(), | |
228 base::Bind(&DnsTransaction::OnIOComplete, | |
229 base::Unretained(this))); | |
230 } | |
231 | |
232 int DnsTransaction::DoReadResponseComplete(int rv) { | |
233 DCHECK_NE(ERR_IO_PENDING, rv); | |
234 RevokeTimer(); | |
235 if (rv < 0) | |
236 return rv; | |
237 | |
238 DCHECK(rv); | |
239 if (!response_->InitParse(rv, *query_)) | |
240 return ERR_DNS_MALFORMED_RESPONSE; | |
241 // TODO(szym): define this flag value in dns_protocol | |
242 if (response_->flags1() & 2) | |
243 return ERR_DNS_SERVER_REQUIRES_TCP; | |
244 // TODO(szym): move this handling out of DnsTransaction? | |
245 if (response_->rcode() != dns_protocol::kRcodeNOERROR && | |
246 response_->rcode() != dns_protocol::kRcodeNXDOMAIN) { | |
247 return ERR_DNS_SERVER_FAILED; | |
248 } | |
249 // TODO(szym): add ERR_DNS_RR_NOT_FOUND? | |
250 if (response_->answer_count() == 0) | |
251 return ERR_NAME_NOT_RESOLVED; | |
252 | |
253 return OK; | |
254 } | |
255 | |
256 void DnsTransaction::StartTimer(base::TimeDelta delay) { | |
257 timer_.Start(FROM_HERE, delay, this, &DnsTransaction::OnTimeout); | |
258 } | |
259 | |
260 void DnsTransaction::RevokeTimer() { | |
261 timer_.Stop(); | |
262 } | |
263 | |
264 void DnsTransaction::OnTimeout() { | |
265 DCHECK(next_state_ == STATE_SEND_QUERY_COMPLETE || | |
266 next_state_ == STATE_READ_RESPONSE_COMPLETE); | |
267 if (attempts_ == session_->config().attempts) { | |
268 DoCallback(ERR_DNS_TIMED_OUT); | |
269 return; | |
270 } | |
271 next_state_ = STATE_CONNECT; | |
272 query_.reset(query_->CloneWithNewId(session_->NextId())); | |
273 int rv = DoLoop(OK); | |
274 if (rv != ERR_IO_PENDING) | |
275 DoCallback(rv); | |
276 } | |
277 | |
278 } // namespace net | 530 } // namespace net |
| 531 |
OLD | NEW |