| OLD | NEW |
| 1 // Copyright (c) 2012 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> | 7 #include <deque> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 64 // A single asynchronous DNS exchange over UDP, which consists of sending out a | 64 // A single asynchronous DNS exchange over UDP, which consists of sending out a |
| 65 // DNS query, waiting for a response, and returning the response that it | 65 // DNS query, waiting for a response, and returning the response that it |
| 66 // matches. Logging is done in the socket and in the outer DnsTransaction. | 66 // matches. Logging is done in the socket and in the outer DnsTransaction. |
| 67 class DnsUDPAttempt { | 67 class DnsUDPAttempt { |
| 68 public: | 68 public: |
| 69 DnsUDPAttempt(scoped_ptr<DatagramClientSocket> socket, | 69 DnsUDPAttempt(scoped_ptr<DatagramClientSocket> socket, |
| 70 const IPEndPoint& server, | 70 const IPEndPoint& server, |
| 71 scoped_ptr<DnsQuery> query, | 71 scoped_ptr<DnsQuery> query, |
| 72 const CompletionCallback& callback) | 72 const CompletionCallback& callback) |
| 73 : next_state_(STATE_NONE), | 73 : next_state_(STATE_NONE), |
| 74 received_malformed_response_(false), |
| 74 socket_(socket.Pass()), | 75 socket_(socket.Pass()), |
| 75 server_(server), | 76 server_(server), |
| 76 query_(query.Pass()), | 77 query_(query.Pass()), |
| 77 callback_(callback) { | 78 callback_(callback) { |
| 78 } | 79 } |
| 79 | 80 |
| 80 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously | 81 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously |
| 81 // and calls |callback| upon completion. | 82 // and calls |callback| upon completion. |
| 82 int Start() { | 83 int Start() { |
| 83 DCHECK_EQ(STATE_NONE, next_state_); | 84 DCHECK_EQ(STATE_NONE, next_state_); |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 143 rv = DoReadResponse(); | 144 rv = DoReadResponse(); |
| 144 break; | 145 break; |
| 145 case STATE_READ_RESPONSE_COMPLETE: | 146 case STATE_READ_RESPONSE_COMPLETE: |
| 146 rv = DoReadResponseComplete(rv); | 147 rv = DoReadResponseComplete(rv); |
| 147 break; | 148 break; |
| 148 default: | 149 default: |
| 149 NOTREACHED(); | 150 NOTREACHED(); |
| 150 break; | 151 break; |
| 151 } | 152 } |
| 152 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | 153 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); |
| 153 | 154 // If we received a malformed response, and are now waiting for another one, |
| 155 // indicate to the transaction that the server might be misbehaving. |
| 156 if (rv == ERR_IO_PENDING && received_malformed_response_) |
| 157 return ERR_DNS_MALFORMED_RESPONSE; |
| 154 return rv; | 158 return rv; |
| 155 } | 159 } |
| 156 | 160 |
| 157 int DoConnect() { | 161 int DoConnect() { |
| 158 next_state_ = STATE_SEND_QUERY; | 162 next_state_ = STATE_SEND_QUERY; |
| 159 return socket_->Connect(server_); | 163 return socket_->Connect(server_); |
| 160 } | 164 } |
| 161 | 165 |
| 162 int DoSendQuery() { | 166 int DoSendQuery() { |
| 163 next_state_ = STATE_SEND_QUERY_COMPLETE; | 167 next_state_ = STATE_SEND_QUERY_COMPLETE; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 189 base::Unretained(this))); | 193 base::Unretained(this))); |
| 190 } | 194 } |
| 191 | 195 |
| 192 int DoReadResponseComplete(int rv) { | 196 int DoReadResponseComplete(int rv) { |
| 193 DCHECK_NE(ERR_IO_PENDING, rv); | 197 DCHECK_NE(ERR_IO_PENDING, rv); |
| 194 if (rv < 0) | 198 if (rv < 0) |
| 195 return rv; | 199 return rv; |
| 196 | 200 |
| 197 DCHECK(rv); | 201 DCHECK(rv); |
| 198 if (!response_->InitParse(rv, *query_)) { | 202 if (!response_->InitParse(rv, *query_)) { |
| 199 // TODO(szym): Consider making this reaction less aggressive. | |
| 200 // Other implementations simply ignore mismatched responses. Since each | 203 // Other implementations simply ignore mismatched responses. Since each |
| 201 // DnsUDPAttempt binds to a different port, we might find that responses | 204 // DnsUDPAttempt binds to a different port, we might find that responses |
| 202 // to previously timed out queries lead to failures in the future. | 205 // to previously timed out queries lead to failures in the future. |
| 203 // http://crbug.com/107413 | 206 // Our solution is to make another attempt, in case the query truly |
| 204 return ERR_DNS_MALFORMED_RESPONSE; | 207 // failed, but keep this attempt alive, in case it was a false alarm. |
| 208 received_malformed_response_ = true; |
| 209 next_state_ = STATE_READ_RESPONSE; |
| 210 return OK; |
| 205 } | 211 } |
| 206 if (response_->flags() & dns_protocol::kFlagTC) | 212 if (response_->flags() & dns_protocol::kFlagTC) |
| 207 return ERR_DNS_SERVER_REQUIRES_TCP; | 213 return ERR_DNS_SERVER_REQUIRES_TCP; |
| 208 // TODO(szym): Extract TTL for NXDOMAIN results. http://crbug.com/115051 | 214 // TODO(szym): Extract TTL for NXDOMAIN results. http://crbug.com/115051 |
| 209 if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN) | 215 if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN) |
| 210 return ERR_NAME_NOT_RESOLVED; | 216 return ERR_NAME_NOT_RESOLVED; |
| 211 if (response_->rcode() != dns_protocol::kRcodeNOERROR) | 217 if (response_->rcode() != dns_protocol::kRcodeNOERROR) |
| 212 return ERR_DNS_SERVER_FAILED; | 218 return ERR_DNS_SERVER_FAILED; |
| 213 | 219 |
| 214 CHECK(response()); | 220 CHECK(response()); |
| 215 return OK; | 221 return OK; |
| 216 } | 222 } |
| 217 | 223 |
| 218 void OnIOComplete(int rv) { | 224 void OnIOComplete(int rv) { |
| 219 rv = DoLoop(rv); | 225 rv = DoLoop(rv); |
| 220 if (rv != ERR_IO_PENDING) | 226 if (rv != ERR_IO_PENDING) |
| 221 callback_.Run(rv); | 227 callback_.Run(rv); |
| 222 } | 228 } |
| 223 | 229 |
| 224 State next_state_; | 230 State next_state_; |
| 231 bool received_malformed_response_; |
| 225 | 232 |
| 226 scoped_ptr<DatagramClientSocket> socket_; | 233 scoped_ptr<DatagramClientSocket> socket_; |
| 227 IPEndPoint server_; | 234 IPEndPoint server_; |
| 228 scoped_ptr<DnsQuery> query_; | 235 scoped_ptr<DnsQuery> query_; |
| 229 | 236 |
| 230 scoped_ptr<DnsResponse> response_; | 237 scoped_ptr<DnsResponse> response_; |
| 231 | 238 |
| 232 CompletionCallback callback_; | 239 CompletionCallback callback_; |
| 233 | 240 |
| 234 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt); | 241 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 280 return qtype_; | 287 return qtype_; |
| 281 } | 288 } |
| 282 | 289 |
| 283 virtual int Start() OVERRIDE { | 290 virtual int Start() OVERRIDE { |
| 284 DCHECK(!callback_.is_null()); | 291 DCHECK(!callback_.is_null()); |
| 285 DCHECK(attempts_.empty()); | 292 DCHECK(attempts_.empty()); |
| 286 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION, | 293 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION, |
| 287 base::Bind(&NetLogStartCallback, &hostname_, qtype_)); | 294 base::Bind(&NetLogStartCallback, &hostname_, qtype_)); |
| 288 int rv = PrepareSearch(); | 295 int rv = PrepareSearch(); |
| 289 if (rv == OK) { | 296 if (rv == OK) { |
| 290 AttemptResult result = FinishAttempt(StartQuery()); | 297 AttemptResult result = ProcessAttemptResult(StartQuery()); |
| 291 if (result.rv == OK) { | 298 if (result.rv == OK) { |
| 292 // DnsTransaction must never succeed synchronously. | 299 // DnsTransaction must never succeed synchronously. |
| 293 MessageLoop::current()->PostTask( | 300 MessageLoop::current()->PostTask( |
| 294 FROM_HERE, | 301 FROM_HERE, |
| 295 base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result)); | 302 base::Bind(&DnsTransactionImpl::DoCallback, AsWeakPtr(), result)); |
| 296 return ERR_IO_PENDING; | 303 return ERR_IO_PENDING; |
| 297 } | 304 } |
| 298 rv = result.rv; | 305 rv = result.rv; |
| 299 } | 306 } |
| 300 if (rv != ERR_IO_PENDING) { | 307 if (rv != ERR_IO_PENDING) { |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 444 | 451 |
| 445 attempts_.clear(); | 452 attempts_.clear(); |
| 446 return MakeAttempt(); | 453 return MakeAttempt(); |
| 447 } | 454 } |
| 448 | 455 |
| 449 void OnAttemptComplete(unsigned attempt_number, int rv) { | 456 void OnAttemptComplete(unsigned attempt_number, int rv) { |
| 450 if (callback_.is_null()) | 457 if (callback_.is_null()) |
| 451 return; | 458 return; |
| 452 DCHECK_LT(attempt_number, attempts_.size()); | 459 DCHECK_LT(attempt_number, attempts_.size()); |
| 453 const DnsUDPAttempt* attempt = attempts_[attempt_number]; | 460 const DnsUDPAttempt* attempt = attempts_[attempt_number]; |
| 454 AttemptResult result = FinishAttempt(AttemptResult(rv, attempt)); | 461 AttemptResult result = ProcessAttemptResult(AttemptResult(rv, attempt)); |
| 455 if (result.rv != ERR_IO_PENDING) | 462 if (result.rv != ERR_IO_PENDING) |
| 456 DoCallback(result); | 463 DoCallback(result); |
| 457 } | 464 } |
| 458 | 465 |
| 459 void LogResponse(const DnsUDPAttempt* attempt) { | 466 void LogResponse(const DnsUDPAttempt* attempt) { |
| 460 if (attempt && attempt->response()) { | 467 if (attempt && attempt->response()) { |
| 461 net_log_.AddEvent( | 468 net_log_.AddEvent( |
| 462 NetLog::TYPE_DNS_TRANSACTION_RESPONSE, | 469 NetLog::TYPE_DNS_TRANSACTION_RESPONSE, |
| 463 base::Bind(&DnsUDPAttempt::NetLogResponseCallback, | 470 base::Bind(&DnsUDPAttempt::NetLogResponseCallback, |
| 464 base::Unretained(attempt))); | 471 base::Unretained(attempt))); |
| 465 } | 472 } |
| 466 } | 473 } |
| 467 | 474 |
| 468 bool MoreAttemptsAllowed() const { | 475 bool MoreAttemptsAllowed() const { |
| 469 const DnsConfig& config = session_->config(); | 476 const DnsConfig& config = session_->config(); |
| 470 return attempts_.size() < config.attempts * config.nameservers.size(); | 477 return attempts_.size() < config.attempts * config.nameservers.size(); |
| 471 } | 478 } |
| 472 | 479 |
| 473 // Resolves the result of a DnsUDPAttempt until a terminal result is reached | 480 // Resolves the result of a DnsUDPAttempt until a terminal result is reached |
| 474 // or it will complete asynchronously (ERR_IO_PENDING). | 481 // or it will complete asynchronously (ERR_IO_PENDING). |
| 475 AttemptResult FinishAttempt(AttemptResult result) { | 482 AttemptResult ProcessAttemptResult(AttemptResult result) { |
| 476 while (result.rv != ERR_IO_PENDING) { | 483 while (result.rv != ERR_IO_PENDING) { |
| 477 LogResponse(result.attempt); | 484 LogResponse(result.attempt); |
| 478 | 485 |
| 479 switch (result.rv) { | 486 switch (result.rv) { |
| 480 case OK: | 487 case OK: |
| 481 net_log_.EndEventWithNetErrorCode( | 488 net_log_.EndEventWithNetErrorCode( |
| 482 NetLog::TYPE_DNS_TRANSACTION_QUERY, result.rv); | 489 NetLog::TYPE_DNS_TRANSACTION_QUERY, result.rv); |
| 483 DCHECK(result.attempt); | 490 DCHECK(result.attempt); |
| 484 DCHECK(result.attempt->response()); | 491 DCHECK(result.attempt->response()); |
| 485 return result; | 492 return result; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 503 break; | 510 break; |
| 504 default: | 511 default: |
| 505 // Server failure. | 512 // Server failure. |
| 506 DCHECK(result.attempt); | 513 DCHECK(result.attempt); |
| 507 if (result.attempt != attempts_.back()) { | 514 if (result.attempt != attempts_.back()) { |
| 508 // This attempt already timed out. Ignore it. | 515 // This attempt already timed out. Ignore it. |
| 509 return AttemptResult(ERR_IO_PENDING, NULL); | 516 return AttemptResult(ERR_IO_PENDING, NULL); |
| 510 } | 517 } |
| 511 if (MoreAttemptsAllowed()) { | 518 if (MoreAttemptsAllowed()) { |
| 512 result = MakeAttempt(); | 519 result = MakeAttempt(); |
| 520 } else if (result.rv == ERR_DNS_MALFORMED_RESPONSE) { |
| 521 // Wait until the last attempt times out. |
| 522 return AttemptResult(ERR_IO_PENDING, NULL); |
| 513 } else { | 523 } else { |
| 514 return AttemptResult(result.rv, NULL); | 524 return AttemptResult(result.rv, NULL); |
| 515 } | 525 } |
| 516 break; | 526 break; |
| 517 } | 527 } |
| 518 } | 528 } |
| 519 return result; | 529 return result; |
| 520 } | 530 } |
| 521 | 531 |
| 522 void OnTimeout() { | 532 void OnTimeout() { |
| 523 if (callback_.is_null()) | 533 if (callback_.is_null()) |
| 524 return; | 534 return; |
| 525 AttemptResult result = FinishAttempt( | 535 AttemptResult result = ProcessAttemptResult( |
| 526 AttemptResult(ERR_DNS_TIMED_OUT, NULL)); | 536 AttemptResult(ERR_DNS_TIMED_OUT, NULL)); |
| 527 if (result.rv != ERR_IO_PENDING) | 537 if (result.rv != ERR_IO_PENDING) |
| 528 DoCallback(result); | 538 DoCallback(result); |
| 529 } | 539 } |
| 530 | 540 |
| 531 scoped_refptr<DnsSession> session_; | 541 scoped_refptr<DnsSession> session_; |
| 532 std::string hostname_; | 542 std::string hostname_; |
| 533 uint16 qtype_; | 543 uint16 qtype_; |
| 534 // Cleared in DoCallback. | 544 // Cleared in DoCallback. |
| 535 DnsTransactionFactory::CallbackType callback_; | 545 DnsTransactionFactory::CallbackType callback_; |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 579 } // namespace | 589 } // namespace |
| 580 | 590 |
| 581 // static | 591 // static |
| 582 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory( | 592 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory( |
| 583 DnsSession* session) { | 593 DnsSession* session) { |
| 584 return scoped_ptr<DnsTransactionFactory>( | 594 return scoped_ptr<DnsTransactionFactory>( |
| 585 new DnsTransactionFactoryImpl(session)); | 595 new DnsTransactionFactoryImpl(session)); |
| 586 } | 596 } |
| 587 | 597 |
| 588 } // namespace net | 598 } // namespace net |
| OLD | NEW |