| 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 |