Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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; | |
|
mmenke
2012/01/19 17:24:47
nit: "labeled" is preferred in the US, technicall
| |
| 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 |