ftp_network_transaction.cc revision 5e3f23d412006dc4db4e659864679f29341e113f
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/ftp/ftp_network_transaction.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/compiler_specific.h" 10#include "base/metrics/histogram.h" 11#include "base/strings/string_number_conversions.h" 12#include "base/strings/string_util.h" 13#include "base/strings/string_split.h" 14#include "base/strings/utf_string_conversions.h" 15#include "base/values.h" 16#include "net/base/address_list.h" 17#include "net/base/connection_type_histograms.h" 18#include "net/base/escape.h" 19#include "net/base/net_errors.h" 20#include "net/base/net_log.h" 21#include "net/base/net_util.h" 22#include "net/ftp/ftp_network_session.h" 23#include "net/ftp/ftp_request_info.h" 24#include "net/ftp/ftp_util.h" 25#include "net/socket/client_socket_factory.h" 26#include "net/socket/stream_socket.h" 27 28const char kCRLF[] = "\r\n"; 29 30const int kCtrlBufLen = 1024; 31 32namespace { 33 34// Returns true if |input| can be safely used as a part of FTP command. 35bool IsValidFTPCommandString(const std::string& input) { 36 // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII 37 // characters in the command if the request path contains them. To be 38 // compatible, we do the same and allow non-ASCII characters in a command. 39 40 // Protect agains newline injection attack. 41 if (input.find_first_of("\r\n") != std::string::npos) 42 return false; 43 44 return true; 45} 46 47enum ErrorClass { 48 // The requested action was initiated. The client should expect another 49 // reply before issuing the next command. 50 ERROR_CLASS_INITIATED, 51 52 // The requested action has been successfully completed. 53 ERROR_CLASS_OK, 54 55 // The command has been accepted, but to complete the operation, more 56 // information must be sent by the client. 57 ERROR_CLASS_INFO_NEEDED, 58 59 // The command was not accepted and the requested action did not take place. 60 // This condition is temporary, and the client is encouraged to restart the 61 // command sequence. 62 ERROR_CLASS_TRANSIENT_ERROR, 63 64 // The command was not accepted and the requested action did not take place. 65 // This condition is rather permanent, and the client is discouraged from 66 // repeating the exact request. 67 ERROR_CLASS_PERMANENT_ERROR, 68}; 69 70// Returns the error class for given response code. Caller should ensure 71// that |response_code| is in range 100-599. 72ErrorClass GetErrorClass(int response_code) { 73 if (response_code >= 100 && response_code <= 199) 74 return ERROR_CLASS_INITIATED; 75 76 if (response_code >= 200 && response_code <= 299) 77 return ERROR_CLASS_OK; 78 79 if (response_code >= 300 && response_code <= 399) 80 return ERROR_CLASS_INFO_NEEDED; 81 82 if (response_code >= 400 && response_code <= 499) 83 return ERROR_CLASS_TRANSIENT_ERROR; 84 85 if (response_code >= 500 && response_code <= 599) 86 return ERROR_CLASS_PERMANENT_ERROR; 87 88 // We should not be called on invalid error codes. 89 NOTREACHED() << response_code; 90 return ERROR_CLASS_PERMANENT_ERROR; 91} 92 93// Returns network error code for received FTP |response_code|. 94int GetNetErrorCodeForFtpResponseCode(int response_code) { 95 switch (response_code) { 96 case 421: 97 return net::ERR_FTP_SERVICE_UNAVAILABLE; 98 case 426: 99 return net::ERR_FTP_TRANSFER_ABORTED; 100 case 450: 101 return net::ERR_FTP_FILE_BUSY; 102 case 500: 103 case 501: 104 return net::ERR_FTP_SYNTAX_ERROR; 105 case 502: 106 case 504: 107 return net::ERR_FTP_COMMAND_NOT_SUPPORTED; 108 case 503: 109 return net::ERR_FTP_BAD_COMMAND_SEQUENCE; 110 default: 111 return net::ERR_FTP_FAILED; 112 } 113} 114 115// From RFC 2428 Section 3: 116// The text returned in response to the EPSV command MUST be: 117// <some text> (<d><d><d><tcp-port><d>) 118// <d> is a delimiter character, ideally to be | 119bool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response, 120 int* port) { 121 if (response.lines.size() != 1) 122 return false; 123 const char* ptr = response.lines[0].c_str(); 124 while (*ptr && *ptr != '(') 125 ++ptr; 126 if (!*ptr) 127 return false; 128 char sep = *(++ptr); 129 if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep) 130 return false; 131 if (!isdigit(*(++ptr))) 132 return false; 133 *port = *ptr - '0'; 134 while (isdigit(*(++ptr))) { 135 *port *= 10; 136 *port += *ptr - '0'; 137 } 138 if (*ptr != sep) 139 return false; 140 141 return true; 142} 143 144// There are two way we can receive IP address and port. 145// (127,0,0,1,23,21) IP address and port encapsulated in (). 146// 127,0,0,1,23,21 IP address and port without (). 147// 148// See RFC 959, Section 4.1.2 149bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response, 150 int* port) { 151 if (response.lines.size() != 1) 152 return false; 153 154 std::string line(response.lines[0]); 155 if (!IsStringASCII(line)) 156 return false; 157 if (line.length() < 2) 158 return false; 159 160 size_t paren_pos = line.find('('); 161 if (paren_pos == std::string::npos) { 162 // Find the first comma and use it to locate the beginning 163 // of the response data. 164 size_t comma_pos = line.find(','); 165 if (comma_pos == std::string::npos) 166 return false; 167 168 size_t space_pos = line.rfind(' ', comma_pos); 169 if (space_pos != std::string::npos) 170 line = line.substr(space_pos + 1); 171 } else { 172 // Remove the parentheses and use the text inside them. 173 size_t closing_paren_pos = line.rfind(')'); 174 if (closing_paren_pos == std::string::npos) 175 return false; 176 if (closing_paren_pos <= paren_pos) 177 return false; 178 179 line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1); 180 } 181 182 // Split the line into comma-separated pieces and extract 183 // the last two. 184 std::vector<std::string> pieces; 185 base::SplitString(line, ',', &pieces); 186 if (pieces.size() != 6) 187 return false; 188 189 // Ignore the IP address supplied in the response. We are always going 190 // to connect back to the same server to prevent FTP PASV port scanning. 191 int p0, p1; 192 if (!base::StringToInt(pieces[4], &p0)) 193 return false; 194 if (!base::StringToInt(pieces[5], &p1)) 195 return false; 196 *port = (p0 << 8) + p1; 197 198 return true; 199} 200 201} // namespace 202 203namespace net { 204 205FtpNetworkTransaction::FtpNetworkTransaction( 206 FtpNetworkSession* session, 207 ClientSocketFactory* socket_factory) 208 : command_sent_(COMMAND_NONE), 209 io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete, 210 base::Unretained(this))), 211 session_(session), 212 request_(NULL), 213 resolver_(session->host_resolver()), 214 read_ctrl_buf_(new IOBuffer(kCtrlBufLen)), 215 ctrl_response_buffer_(NULL), 216 read_data_buf_len_(0), 217 last_error_(OK), 218 system_type_(SYSTEM_TYPE_UNKNOWN), 219 // Use image (binary) transfer by default. It should always work, 220 // whereas the ascii transfer may damage binary data. 221 data_type_(DATA_TYPE_IMAGE), 222 resource_type_(RESOURCE_TYPE_UNKNOWN), 223 use_epsv_(true), 224 data_connection_port_(0), 225 socket_factory_(socket_factory), 226 next_state_(STATE_NONE), 227 state_after_data_connect_complete_(STATE_CTRL_WRITE_SIZE) { 228} 229 230FtpNetworkTransaction::~FtpNetworkTransaction() { 231} 232 233int FtpNetworkTransaction::Stop(int error) { 234 if (command_sent_ == COMMAND_QUIT) 235 return error; 236 237 next_state_ = STATE_CTRL_WRITE_QUIT; 238 last_error_ = error; 239 return OK; 240} 241 242int FtpNetworkTransaction::RestartIgnoringLastError( 243 const CompletionCallback& callback) { 244 return ERR_NOT_IMPLEMENTED; 245} 246 247int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info, 248 const CompletionCallback& callback, 249 const BoundNetLog& net_log) { 250 net_log_ = net_log; 251 request_ = request_info; 252 253 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_)); 254 255 if (request_->url.has_username()) { 256 base::string16 username; 257 base::string16 password; 258 GetIdentityFromURL(request_->url, &username, &password); 259 credentials_.Set(username, password); 260 } else { 261 credentials_.Set(ASCIIToUTF16("anonymous"), 262 ASCIIToUTF16("chrome@example.com")); 263 } 264 265 DetectTypecode(); 266 267 next_state_ = STATE_CTRL_RESOLVE_HOST; 268 int rv = DoLoop(OK); 269 if (rv == ERR_IO_PENDING) 270 user_callback_ = callback; 271 return rv; 272} 273 274int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials, 275 const CompletionCallback& callback) { 276 ResetStateForRestart(); 277 278 credentials_ = credentials; 279 280 next_state_ = STATE_CTRL_RESOLVE_HOST; 281 int rv = DoLoop(OK); 282 if (rv == ERR_IO_PENDING) 283 user_callback_ = callback; 284 return rv; 285} 286 287int FtpNetworkTransaction::Read(IOBuffer* buf, 288 int buf_len, 289 const CompletionCallback& callback) { 290 DCHECK(buf); 291 DCHECK_GT(buf_len, 0); 292 293 read_data_buf_ = buf; 294 read_data_buf_len_ = buf_len; 295 296 next_state_ = STATE_DATA_READ; 297 int rv = DoLoop(OK); 298 if (rv == ERR_IO_PENDING) 299 user_callback_ = callback; 300 return rv; 301} 302 303const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const { 304 return &response_; 305} 306 307LoadState FtpNetworkTransaction::GetLoadState() const { 308 if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE) 309 return LOAD_STATE_RESOLVING_HOST; 310 311 if (next_state_ == STATE_CTRL_CONNECT_COMPLETE || 312 next_state_ == STATE_DATA_CONNECT_COMPLETE) 313 return LOAD_STATE_CONNECTING; 314 315 if (next_state_ == STATE_DATA_READ_COMPLETE) 316 return LOAD_STATE_READING_RESPONSE; 317 318 if (command_sent_ == COMMAND_RETR && read_data_buf_.get()) 319 return LOAD_STATE_READING_RESPONSE; 320 321 if (command_sent_ == COMMAND_QUIT) 322 return LOAD_STATE_IDLE; 323 324 if (command_sent_ != COMMAND_NONE) 325 return LOAD_STATE_SENDING_REQUEST; 326 327 return LOAD_STATE_IDLE; 328} 329 330uint64 FtpNetworkTransaction::GetUploadProgress() const { 331 return 0; 332} 333 334void FtpNetworkTransaction::ResetStateForRestart() { 335 command_sent_ = COMMAND_NONE; 336 user_callback_.Reset(); 337 response_ = FtpResponseInfo(); 338 read_ctrl_buf_ = new IOBuffer(kCtrlBufLen); 339 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_)); 340 read_data_buf_ = NULL; 341 read_data_buf_len_ = 0; 342 if (write_buf_.get()) 343 write_buf_->SetOffset(0); 344 last_error_ = OK; 345 data_connection_port_ = 0; 346 ctrl_socket_.reset(); 347 data_socket_.reset(); 348 next_state_ = STATE_NONE; 349 state_after_data_connect_complete_ = STATE_CTRL_WRITE_SIZE; 350} 351 352void FtpNetworkTransaction::ResetDataConnectionAfterError(State next_state) { 353 // The server _might_ have reset the data connection 354 // (see RFC 959 3.2. ESTABLISHING DATA CONNECTIONS: 355 // "The server MUST close the data connection under the following 356 // conditions: 357 // ... 358 // 5. An irrecoverable error condition occurs.") 359 // 360 // It is ambiguous what an irrecoverable error condition is, 361 // so we take no chances. 362 state_after_data_connect_complete_ = next_state; 363 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV; 364} 365 366void FtpNetworkTransaction::DoCallback(int rv) { 367 DCHECK(rv != ERR_IO_PENDING); 368 DCHECK(!user_callback_.is_null()); 369 370 // Since Run may result in Read being called, clear callback_ up front. 371 CompletionCallback c = user_callback_; 372 user_callback_.Reset(); 373 c.Run(rv); 374} 375 376void FtpNetworkTransaction::OnIOComplete(int result) { 377 int rv = DoLoop(result); 378 if (rv != ERR_IO_PENDING) 379 DoCallback(rv); 380} 381 382int FtpNetworkTransaction::ProcessCtrlResponse() { 383 FtpCtrlResponse response = ctrl_response_buffer_->PopResponse(); 384 385 int rv = OK; 386 switch (command_sent_) { 387 case COMMAND_NONE: 388 // TODO(phajdan.jr): Check for errors in the welcome message. 389 next_state_ = STATE_CTRL_WRITE_USER; 390 break; 391 case COMMAND_USER: 392 rv = ProcessResponseUSER(response); 393 break; 394 case COMMAND_PASS: 395 rv = ProcessResponsePASS(response); 396 break; 397 case COMMAND_SYST: 398 rv = ProcessResponseSYST(response); 399 break; 400 case COMMAND_PWD: 401 rv = ProcessResponsePWD(response); 402 break; 403 case COMMAND_TYPE: 404 rv = ProcessResponseTYPE(response); 405 break; 406 case COMMAND_EPSV: 407 rv = ProcessResponseEPSV(response); 408 break; 409 case COMMAND_PASV: 410 rv = ProcessResponsePASV(response); 411 break; 412 case COMMAND_SIZE: 413 rv = ProcessResponseSIZE(response); 414 break; 415 case COMMAND_RETR: 416 rv = ProcessResponseRETR(response); 417 break; 418 case COMMAND_CWD: 419 rv = ProcessResponseCWD(response); 420 break; 421 case COMMAND_LIST: 422 rv = ProcessResponseLIST(response); 423 break; 424 case COMMAND_QUIT: 425 rv = ProcessResponseQUIT(response); 426 break; 427 default: 428 LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_; 429 return ERR_UNEXPECTED; 430 } 431 432 // We may get multiple responses for some commands, 433 // see http://crbug.com/18036. 434 while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) { 435 response = ctrl_response_buffer_->PopResponse(); 436 437 switch (command_sent_) { 438 case COMMAND_RETR: 439 rv = ProcessResponseRETR(response); 440 break; 441 case COMMAND_LIST: 442 rv = ProcessResponseLIST(response); 443 break; 444 default: 445 // Multiple responses for other commands are invalid. 446 return Stop(ERR_INVALID_RESPONSE); 447 } 448 } 449 450 return rv; 451} 452 453// Used to prepare and send FTP command. 454int FtpNetworkTransaction::SendFtpCommand(const std::string& command, 455 const std::string& command_for_log, 456 Command cmd) { 457 // If we send a new command when we still have unprocessed responses 458 // for previous commands, the response receiving code will have no way to know 459 // which responses are for which command. 460 DCHECK(!ctrl_response_buffer_->ResponseAvailable()); 461 462 DCHECK(!write_command_buf_.get()); 463 DCHECK(!write_buf_.get()); 464 465 if (!IsValidFTPCommandString(command)) { 466 // Callers should validate the command themselves and return a more specific 467 // error code. 468 NOTREACHED(); 469 return Stop(ERR_UNEXPECTED); 470 } 471 472 command_sent_ = cmd; 473 474 write_command_buf_ = new IOBufferWithSize(command.length() + 2); 475 write_buf_ = new DrainableIOBuffer(write_command_buf_.get(), 476 write_command_buf_->size()); 477 memcpy(write_command_buf_->data(), command.data(), command.length()); 478 memcpy(write_command_buf_->data() + command.length(), kCRLF, 2); 479 480 net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT, 481 NetLog::StringCallback("command", &command_for_log)); 482 483 next_state_ = STATE_CTRL_WRITE; 484 return OK; 485} 486 487std::string FtpNetworkTransaction::GetRequestPathForFtpCommand( 488 bool is_directory) const { 489 std::string path(current_remote_directory_); 490 if (request_->url.has_path()) { 491 std::string gurl_path(request_->url.path()); 492 493 // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path. 494 std::string::size_type pos = gurl_path.rfind(';'); 495 if (pos != std::string::npos) 496 gurl_path.resize(pos); 497 498 path.append(gurl_path); 499 } 500 // Make sure that if the path is expected to be a file, it won't end 501 // with a trailing slash. 502 if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/') 503 path.erase(path.length() - 1); 504 UnescapeRule::Type unescape_rules = UnescapeRule::SPACES | 505 UnescapeRule::URL_SPECIAL_CHARS; 506 // This may unescape to non-ASCII characters, but we allow that. See the 507 // comment for IsValidFTPCommandString. 508 path = net::UnescapeURLComponent(path, unescape_rules); 509 510 if (system_type_ == SYSTEM_TYPE_VMS) { 511 if (is_directory) 512 path = FtpUtil::UnixDirectoryPathToVMS(path); 513 else 514 path = FtpUtil::UnixFilePathToVMS(path); 515 } 516 517 DCHECK(IsValidFTPCommandString(path)); 518 return path; 519} 520 521void FtpNetworkTransaction::DetectTypecode() { 522 if (!request_->url.has_path()) 523 return; 524 std::string gurl_path(request_->url.path()); 525 526 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path. 527 std::string::size_type pos = gurl_path.rfind(';'); 528 if (pos == std::string::npos) 529 return; 530 std::string typecode_string(gurl_path.substr(pos)); 531 if (typecode_string == ";type=a") { 532 data_type_ = DATA_TYPE_ASCII; 533 resource_type_ = RESOURCE_TYPE_FILE; 534 } else if (typecode_string == ";type=i") { 535 data_type_ = DATA_TYPE_IMAGE; 536 resource_type_ = RESOURCE_TYPE_FILE; 537 } else if (typecode_string == ";type=d") { 538 resource_type_ = RESOURCE_TYPE_DIRECTORY; 539 } 540} 541 542int FtpNetworkTransaction::DoLoop(int result) { 543 DCHECK(next_state_ != STATE_NONE); 544 545 int rv = result; 546 do { 547 State state = next_state_; 548 next_state_ = STATE_NONE; 549 switch (state) { 550 case STATE_CTRL_RESOLVE_HOST: 551 DCHECK(rv == OK); 552 rv = DoCtrlResolveHost(); 553 break; 554 case STATE_CTRL_RESOLVE_HOST_COMPLETE: 555 rv = DoCtrlResolveHostComplete(rv); 556 break; 557 case STATE_CTRL_CONNECT: 558 DCHECK(rv == OK); 559 rv = DoCtrlConnect(); 560 break; 561 case STATE_CTRL_CONNECT_COMPLETE: 562 rv = DoCtrlConnectComplete(rv); 563 break; 564 case STATE_CTRL_READ: 565 DCHECK(rv == OK); 566 rv = DoCtrlRead(); 567 break; 568 case STATE_CTRL_READ_COMPLETE: 569 rv = DoCtrlReadComplete(rv); 570 break; 571 case STATE_CTRL_WRITE: 572 DCHECK(rv == OK); 573 rv = DoCtrlWrite(); 574 break; 575 case STATE_CTRL_WRITE_COMPLETE: 576 rv = DoCtrlWriteComplete(rv); 577 break; 578 case STATE_CTRL_WRITE_USER: 579 DCHECK(rv == OK); 580 rv = DoCtrlWriteUSER(); 581 break; 582 case STATE_CTRL_WRITE_PASS: 583 DCHECK(rv == OK); 584 rv = DoCtrlWritePASS(); 585 break; 586 case STATE_CTRL_WRITE_SYST: 587 DCHECK(rv == OK); 588 rv = DoCtrlWriteSYST(); 589 break; 590 case STATE_CTRL_WRITE_PWD: 591 DCHECK(rv == OK); 592 rv = DoCtrlWritePWD(); 593 break; 594 case STATE_CTRL_WRITE_TYPE: 595 DCHECK(rv == OK); 596 rv = DoCtrlWriteTYPE(); 597 break; 598 case STATE_CTRL_WRITE_EPSV: 599 DCHECK(rv == OK); 600 rv = DoCtrlWriteEPSV(); 601 break; 602 case STATE_CTRL_WRITE_PASV: 603 DCHECK(rv == OK); 604 rv = DoCtrlWritePASV(); 605 break; 606 case STATE_CTRL_WRITE_RETR: 607 DCHECK(rv == OK); 608 rv = DoCtrlWriteRETR(); 609 break; 610 case STATE_CTRL_WRITE_SIZE: 611 DCHECK(rv == OK); 612 rv = DoCtrlWriteSIZE(); 613 break; 614 case STATE_CTRL_WRITE_CWD: 615 DCHECK(rv == OK); 616 rv = DoCtrlWriteCWD(); 617 break; 618 case STATE_CTRL_WRITE_LIST: 619 DCHECK(rv == OK); 620 rv = DoCtrlWriteLIST(); 621 break; 622 case STATE_CTRL_WRITE_QUIT: 623 DCHECK(rv == OK); 624 rv = DoCtrlWriteQUIT(); 625 break; 626 case STATE_DATA_CONNECT: 627 DCHECK(rv == OK); 628 rv = DoDataConnect(); 629 break; 630 case STATE_DATA_CONNECT_COMPLETE: 631 rv = DoDataConnectComplete(rv); 632 break; 633 case STATE_DATA_READ: 634 DCHECK(rv == OK); 635 rv = DoDataRead(); 636 break; 637 case STATE_DATA_READ_COMPLETE: 638 rv = DoDataReadComplete(rv); 639 break; 640 default: 641 NOTREACHED() << "bad state"; 642 rv = ERR_UNEXPECTED; 643 break; 644 } 645 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); 646 return rv; 647} 648 649int FtpNetworkTransaction::DoCtrlResolveHost() { 650 next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE; 651 652 HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url)); 653 // No known referrer. 654 return resolver_.Resolve( 655 info, &addresses_, 656 base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)), 657 net_log_); 658} 659 660int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) { 661 if (result == OK) 662 next_state_ = STATE_CTRL_CONNECT; 663 return result; 664} 665 666int FtpNetworkTransaction::DoCtrlConnect() { 667 next_state_ = STATE_CTRL_CONNECT_COMPLETE; 668 ctrl_socket_.reset(socket_factory_->CreateTransportClientSocket( 669 addresses_, net_log_.net_log(), net_log_.source())); 670 net_log_.AddEvent( 671 NetLog::TYPE_FTP_CONTROL_CONNECTION, 672 ctrl_socket_->NetLog().source().ToEventParametersCallback()); 673 return ctrl_socket_->Connect(io_callback_); 674} 675 676int FtpNetworkTransaction::DoCtrlConnectComplete(int result) { 677 if (result == OK) { 678 // Put the peer's IP address and port into the response. 679 IPEndPoint ip_endpoint; 680 result = ctrl_socket_->GetPeerAddress(&ip_endpoint); 681 if (result == OK) { 682 response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint); 683 next_state_ = STATE_CTRL_READ; 684 685 if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) { 686 // Do not use EPSV for IPv4 connections. Some servers become confused 687 // and we time out while waiting to connect. PASV is perfectly fine for 688 // IPv4. Note that this blacklists IPv4 not to use EPSV instead of 689 // whitelisting IPv6 to use it, to make the code more future-proof: 690 // all future protocols should just use EPSV. 691 use_epsv_ = false; 692 } 693 } 694 } 695 return result; 696} 697 698int FtpNetworkTransaction::DoCtrlRead() { 699 next_state_ = STATE_CTRL_READ_COMPLETE; 700 return ctrl_socket_->Read(read_ctrl_buf_.get(), kCtrlBufLen, io_callback_); 701} 702 703int FtpNetworkTransaction::DoCtrlReadComplete(int result) { 704 if (result == 0) { 705 // Some servers (for example Pure-FTPd) apparently close the control 706 // connection when anonymous login is not permitted. For more details 707 // see http://crbug.com/25023. 708 if (command_sent_ == COMMAND_USER && 709 credentials_.username() == ASCIIToUTF16("anonymous")) { 710 response_.needs_auth = true; 711 } 712 return Stop(ERR_EMPTY_RESPONSE); 713 } 714 if (result < 0) 715 return Stop(result); 716 717 ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result); 718 719 if (!ctrl_response_buffer_->ResponseAvailable()) { 720 // Read more data from the control socket. 721 next_state_ = STATE_CTRL_READ; 722 return OK; 723 } 724 725 return ProcessCtrlResponse(); 726} 727 728int FtpNetworkTransaction::DoCtrlWrite() { 729 next_state_ = STATE_CTRL_WRITE_COMPLETE; 730 731 return ctrl_socket_->Write( 732 write_buf_.get(), write_buf_->BytesRemaining(), io_callback_); 733} 734 735int FtpNetworkTransaction::DoCtrlWriteComplete(int result) { 736 if (result < 0) 737 return result; 738 739 write_buf_->DidConsume(result); 740 if (write_buf_->BytesRemaining() == 0) { 741 // Clear the write buffer. 742 write_buf_ = NULL; 743 write_command_buf_ = NULL; 744 745 next_state_ = STATE_CTRL_READ; 746 } else { 747 next_state_ = STATE_CTRL_WRITE; 748 } 749 return OK; 750} 751 752// FTP Commands and responses 753 754// USER Command. 755int FtpNetworkTransaction::DoCtrlWriteUSER() { 756 std::string command = "USER " + UTF16ToUTF8(credentials_.username()); 757 758 if (!IsValidFTPCommandString(command)) 759 return Stop(ERR_MALFORMED_IDENTITY); 760 761 next_state_ = STATE_CTRL_READ; 762 return SendFtpCommand(command, "USER ***", COMMAND_USER); 763} 764 765int FtpNetworkTransaction::ProcessResponseUSER( 766 const FtpCtrlResponse& response) { 767 switch (GetErrorClass(response.status_code)) { 768 case ERROR_CLASS_OK: 769 next_state_ = STATE_CTRL_WRITE_SYST; 770 break; 771 case ERROR_CLASS_INFO_NEEDED: 772 next_state_ = STATE_CTRL_WRITE_PASS; 773 break; 774 case ERROR_CLASS_TRANSIENT_ERROR: 775 case ERROR_CLASS_PERMANENT_ERROR: 776 response_.needs_auth = true; 777 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 778 default: 779 NOTREACHED(); 780 return Stop(ERR_UNEXPECTED); 781 } 782 return OK; 783} 784 785// PASS command. 786int FtpNetworkTransaction::DoCtrlWritePASS() { 787 std::string command = "PASS " + UTF16ToUTF8(credentials_.password()); 788 789 if (!IsValidFTPCommandString(command)) 790 return Stop(ERR_MALFORMED_IDENTITY); 791 792 next_state_ = STATE_CTRL_READ; 793 return SendFtpCommand(command, "PASS ***", COMMAND_PASS); 794} 795 796int FtpNetworkTransaction::ProcessResponsePASS( 797 const FtpCtrlResponse& response) { 798 switch (GetErrorClass(response.status_code)) { 799 case ERROR_CLASS_OK: 800 next_state_ = STATE_CTRL_WRITE_SYST; 801 break; 802 case ERROR_CLASS_INFO_NEEDED: 803 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 804 case ERROR_CLASS_TRANSIENT_ERROR: 805 case ERROR_CLASS_PERMANENT_ERROR: 806 response_.needs_auth = true; 807 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 808 default: 809 NOTREACHED(); 810 return Stop(ERR_UNEXPECTED); 811 } 812 return OK; 813} 814 815// SYST command. 816int FtpNetworkTransaction::DoCtrlWriteSYST() { 817 std::string command = "SYST"; 818 next_state_ = STATE_CTRL_READ; 819 return SendFtpCommand(command, command, COMMAND_SYST); 820} 821 822int FtpNetworkTransaction::ProcessResponseSYST( 823 const FtpCtrlResponse& response) { 824 switch (GetErrorClass(response.status_code)) { 825 case ERROR_CLASS_INITIATED: 826 return Stop(ERR_INVALID_RESPONSE); 827 case ERROR_CLASS_OK: { 828 // All important info should be on the first line. 829 std::string line = response.lines[0]; 830 // The response should be ASCII, which allows us to do case-insensitive 831 // comparisons easily. If it is not ASCII, we leave the system type 832 // as unknown. 833 if (IsStringASCII(line)) { 834 line = StringToLowerASCII(line); 835 836 // Remove all whitespace, to correctly handle cases like fancy "V M S" 837 // response instead of "VMS". 838 RemoveChars(line, kWhitespaceASCII, &line); 839 840 // The "magic" strings we test for below have been gathered by an 841 // empirical study. VMS needs to come first because some VMS systems 842 // also respond with "UNIX emulation", which is not perfect. It is much 843 // more reliable to talk to these servers in their native language. 844 if (line.find("vms") != std::string::npos) { 845 system_type_ = SYSTEM_TYPE_VMS; 846 } else if (line.find("l8") != std::string::npos || 847 line.find("unix") != std::string::npos || 848 line.find("bsd") != std::string::npos) { 849 system_type_ = SYSTEM_TYPE_UNIX; 850 } else if (line.find("win32") != std::string::npos || 851 line.find("windows") != std::string::npos) { 852 system_type_ = SYSTEM_TYPE_WINDOWS; 853 } else if (line.find("os/2") != std::string::npos) { 854 system_type_ = SYSTEM_TYPE_OS2; 855 } 856 } 857 next_state_ = STATE_CTRL_WRITE_PWD; 858 break; 859 } 860 case ERROR_CLASS_INFO_NEEDED: 861 return Stop(ERR_INVALID_RESPONSE); 862 case ERROR_CLASS_TRANSIENT_ERROR: 863 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 864 case ERROR_CLASS_PERMANENT_ERROR: 865 // Server does not recognize the SYST command so proceed. 866 next_state_ = STATE_CTRL_WRITE_PWD; 867 break; 868 default: 869 NOTREACHED(); 870 return Stop(ERR_UNEXPECTED); 871 } 872 return OK; 873} 874 875// PWD command. 876int FtpNetworkTransaction::DoCtrlWritePWD() { 877 std::string command = "PWD"; 878 next_state_ = STATE_CTRL_READ; 879 return SendFtpCommand(command, command, COMMAND_PWD); 880} 881 882int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) { 883 switch (GetErrorClass(response.status_code)) { 884 case ERROR_CLASS_INITIATED: 885 return Stop(ERR_INVALID_RESPONSE); 886 case ERROR_CLASS_OK: { 887 // The info we look for should be on the first line. 888 std::string line = response.lines[0]; 889 if (line.empty()) 890 return Stop(ERR_INVALID_RESPONSE); 891 std::string::size_type quote_pos = line.find('"'); 892 if (quote_pos != std::string::npos) { 893 line = line.substr(quote_pos + 1); 894 quote_pos = line.find('"'); 895 if (quote_pos == std::string::npos) 896 return Stop(ERR_INVALID_RESPONSE); 897 line = line.substr(0, quote_pos); 898 } 899 if (system_type_ == SYSTEM_TYPE_VMS) 900 line = FtpUtil::VMSPathToUnix(line); 901 if (line.length() && line[line.length() - 1] == '/') 902 line.erase(line.length() - 1); 903 current_remote_directory_ = line; 904 next_state_ = STATE_CTRL_WRITE_TYPE; 905 break; 906 } 907 case ERROR_CLASS_INFO_NEEDED: 908 return Stop(ERR_INVALID_RESPONSE); 909 case ERROR_CLASS_TRANSIENT_ERROR: 910 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 911 case ERROR_CLASS_PERMANENT_ERROR: 912 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 913 default: 914 NOTREACHED(); 915 return Stop(ERR_UNEXPECTED); 916 } 917 return OK; 918} 919 920// TYPE command. 921int FtpNetworkTransaction::DoCtrlWriteTYPE() { 922 std::string command = "TYPE "; 923 if (data_type_ == DATA_TYPE_ASCII) { 924 command += "A"; 925 } else if (data_type_ == DATA_TYPE_IMAGE) { 926 command += "I"; 927 } else { 928 NOTREACHED(); 929 return Stop(ERR_UNEXPECTED); 930 } 931 next_state_ = STATE_CTRL_READ; 932 return SendFtpCommand(command, command, COMMAND_TYPE); 933} 934 935int FtpNetworkTransaction::ProcessResponseTYPE( 936 const FtpCtrlResponse& response) { 937 switch (GetErrorClass(response.status_code)) { 938 case ERROR_CLASS_INITIATED: 939 return Stop(ERR_INVALID_RESPONSE); 940 case ERROR_CLASS_OK: 941 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV; 942 break; 943 case ERROR_CLASS_INFO_NEEDED: 944 return Stop(ERR_INVALID_RESPONSE); 945 case ERROR_CLASS_TRANSIENT_ERROR: 946 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 947 case ERROR_CLASS_PERMANENT_ERROR: 948 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 949 default: 950 NOTREACHED(); 951 return Stop(ERR_UNEXPECTED); 952 } 953 return OK; 954} 955 956// EPSV command 957int FtpNetworkTransaction::DoCtrlWriteEPSV() { 958 const std::string command = "EPSV"; 959 next_state_ = STATE_CTRL_READ; 960 return SendFtpCommand(command, command, COMMAND_EPSV); 961} 962 963int FtpNetworkTransaction::ProcessResponseEPSV( 964 const FtpCtrlResponse& response) { 965 switch (GetErrorClass(response.status_code)) { 966 case ERROR_CLASS_INITIATED: 967 return Stop(ERR_INVALID_RESPONSE); 968 case ERROR_CLASS_OK: 969 if (!ExtractPortFromEPSVResponse( response, &data_connection_port_)) 970 return Stop(ERR_INVALID_RESPONSE); 971 if (data_connection_port_ < 1024 || 972 !IsPortAllowedByFtp(data_connection_port_)) 973 return Stop(ERR_UNSAFE_PORT); 974 next_state_ = STATE_DATA_CONNECT; 975 break; 976 case ERROR_CLASS_INFO_NEEDED: 977 return Stop(ERR_INVALID_RESPONSE); 978 case ERROR_CLASS_TRANSIENT_ERROR: 979 case ERROR_CLASS_PERMANENT_ERROR: 980 use_epsv_ = false; 981 next_state_ = STATE_CTRL_WRITE_PASV; 982 return OK; 983 default: 984 NOTREACHED(); 985 return Stop(ERR_UNEXPECTED); 986 } 987 return OK; 988} 989 990// PASV command 991int FtpNetworkTransaction::DoCtrlWritePASV() { 992 std::string command = "PASV"; 993 next_state_ = STATE_CTRL_READ; 994 return SendFtpCommand(command, command, COMMAND_PASV); 995} 996 997int FtpNetworkTransaction::ProcessResponsePASV( 998 const FtpCtrlResponse& response) { 999 switch (GetErrorClass(response.status_code)) { 1000 case ERROR_CLASS_INITIATED: 1001 return Stop(ERR_INVALID_RESPONSE); 1002 case ERROR_CLASS_OK: 1003 if (!ExtractPortFromPASVResponse(response, &data_connection_port_)) 1004 return Stop(ERR_INVALID_RESPONSE); 1005 if (data_connection_port_ < 1024 || 1006 !IsPortAllowedByFtp(data_connection_port_)) 1007 return Stop(ERR_UNSAFE_PORT); 1008 next_state_ = STATE_DATA_CONNECT; 1009 break; 1010 case ERROR_CLASS_INFO_NEEDED: 1011 return Stop(ERR_INVALID_RESPONSE); 1012 case ERROR_CLASS_TRANSIENT_ERROR: 1013 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1014 case ERROR_CLASS_PERMANENT_ERROR: 1015 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1016 default: 1017 NOTREACHED(); 1018 return Stop(ERR_UNEXPECTED); 1019 } 1020 return OK; 1021} 1022 1023// RETR command 1024int FtpNetworkTransaction::DoCtrlWriteRETR() { 1025 std::string command = "RETR " + GetRequestPathForFtpCommand(false); 1026 next_state_ = STATE_CTRL_READ; 1027 return SendFtpCommand(command, command, COMMAND_RETR); 1028} 1029 1030int FtpNetworkTransaction::ProcessResponseRETR( 1031 const FtpCtrlResponse& response) { 1032 switch (GetErrorClass(response.status_code)) { 1033 case ERROR_CLASS_INITIATED: 1034 // We want the client to start reading the response at this point. 1035 // It got here either through Start or RestartWithAuth. We want that 1036 // method to complete. Not setting next state here will make DoLoop exit 1037 // and in turn make Start/RestartWithAuth complete. 1038 resource_type_ = RESOURCE_TYPE_FILE; 1039 break; 1040 case ERROR_CLASS_OK: 1041 resource_type_ = RESOURCE_TYPE_FILE; 1042 next_state_ = STATE_CTRL_WRITE_QUIT; 1043 break; 1044 case ERROR_CLASS_INFO_NEEDED: 1045 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1046 case ERROR_CLASS_TRANSIENT_ERROR: 1047 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1048 case ERROR_CLASS_PERMANENT_ERROR: 1049 // Code 550 means "Failed to open file". Other codes are unrelated, 1050 // like "Not logged in" etc. 1051 if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE) 1052 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1053 1054 // It's possible that RETR failed because the path is a directory. 1055 resource_type_ = RESOURCE_TYPE_DIRECTORY; 1056 1057 // We're going to try CWD next, but first send a PASV one more time, 1058 // because some FTP servers, including FileZilla, require that. 1059 // See http://crbug.com/25316. 1060 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV; 1061 break; 1062 default: 1063 NOTREACHED(); 1064 return Stop(ERR_UNEXPECTED); 1065 } 1066 1067 // We should be sure about our resource type now. Otherwise we risk 1068 // an infinite loop (RETR can later send CWD, and CWD can later send RETR). 1069 DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_); 1070 1071 return OK; 1072} 1073 1074// SIZE command 1075int FtpNetworkTransaction::DoCtrlWriteSIZE() { 1076 std::string command = "SIZE " + GetRequestPathForFtpCommand(false); 1077 next_state_ = STATE_CTRL_READ; 1078 return SendFtpCommand(command, command, COMMAND_SIZE); 1079} 1080 1081int FtpNetworkTransaction::ProcessResponseSIZE( 1082 const FtpCtrlResponse& response) { 1083 State state_after_size; 1084 if (resource_type_ == RESOURCE_TYPE_FILE) 1085 state_after_size = STATE_CTRL_WRITE_RETR; 1086 else 1087 state_after_size = STATE_CTRL_WRITE_CWD; 1088 1089 switch (GetErrorClass(response.status_code)) { 1090 case ERROR_CLASS_INITIATED: 1091 next_state_ = state_after_size; 1092 break; 1093 case ERROR_CLASS_OK: 1094 if (response.lines.size() != 1) 1095 return Stop(ERR_INVALID_RESPONSE); 1096 int64 size; 1097 if (!base::StringToInt64(response.lines[0], &size)) 1098 return Stop(ERR_INVALID_RESPONSE); 1099 if (size < 0) 1100 return Stop(ERR_INVALID_RESPONSE); 1101 1102 // A successful response to SIZE does not mean the resource is a file. 1103 // Some FTP servers (for example, the qnx one) send a SIZE even for 1104 // directories. 1105 response_.expected_content_size = size; 1106 1107 next_state_ = state_after_size; 1108 break; 1109 case ERROR_CLASS_INFO_NEEDED: 1110 next_state_ = state_after_size; 1111 break; 1112 case ERROR_CLASS_TRANSIENT_ERROR: 1113 ResetDataConnectionAfterError(state_after_size); 1114 break; 1115 case ERROR_CLASS_PERMANENT_ERROR: 1116 // It's possible that SIZE failed because the path is a directory. 1117 if (resource_type_ == RESOURCE_TYPE_UNKNOWN && 1118 response.status_code != 550) { 1119 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1120 } 1121 1122 ResetDataConnectionAfterError(state_after_size); 1123 break; 1124 default: 1125 NOTREACHED(); 1126 return Stop(ERR_UNEXPECTED); 1127 } 1128 1129 return OK; 1130} 1131 1132// CWD command 1133int FtpNetworkTransaction::DoCtrlWriteCWD() { 1134 std::string command = "CWD " + GetRequestPathForFtpCommand(true); 1135 next_state_ = STATE_CTRL_READ; 1136 return SendFtpCommand(command, command, COMMAND_CWD); 1137} 1138 1139int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) { 1140 // We should never issue CWD if we know the target resource is a file. 1141 DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_); 1142 1143 switch (GetErrorClass(response.status_code)) { 1144 case ERROR_CLASS_INITIATED: 1145 return Stop(ERR_INVALID_RESPONSE); 1146 case ERROR_CLASS_OK: 1147 next_state_ = STATE_CTRL_WRITE_LIST; 1148 break; 1149 case ERROR_CLASS_INFO_NEEDED: 1150 return Stop(ERR_INVALID_RESPONSE); 1151 case ERROR_CLASS_TRANSIENT_ERROR: 1152 // Some FTP servers send response 451 (not a valid CWD response according 1153 // to RFC 959) instead of 550. 1154 if (response.status_code == 451) 1155 return ProcessResponseCWDNotADirectory(); 1156 1157 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1158 case ERROR_CLASS_PERMANENT_ERROR: 1159 if (response.status_code == 550) 1160 return ProcessResponseCWDNotADirectory(); 1161 1162 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1163 default: 1164 NOTREACHED(); 1165 return Stop(ERR_UNEXPECTED); 1166 } 1167 1168 return OK; 1169} 1170 1171int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() { 1172 if (resource_type_ == RESOURCE_TYPE_DIRECTORY) { 1173 // We're assuming that the resource is a directory, but the server 1174 // says it's not true. The most probable interpretation is that it 1175 // doesn't exist (with FTP we can't be sure). 1176 return Stop(ERR_FILE_NOT_FOUND); 1177 } 1178 1179 // We are here because SIZE failed and we are not sure what the resource 1180 // type is. It could still be file, and SIZE could fail because of 1181 // an access error (http://crbug.com/56734). Try RETR just to be sure. 1182 resource_type_ = RESOURCE_TYPE_FILE; 1183 1184 ResetDataConnectionAfterError(STATE_CTRL_WRITE_RETR); 1185 return OK; 1186} 1187 1188// LIST command 1189int FtpNetworkTransaction::DoCtrlWriteLIST() { 1190 // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option 1191 // forces LIST output instead of NLST (which would be ambiguous for us 1192 // to parse). 1193 std::string command("LIST -l"); 1194 if (system_type_ == SYSTEM_TYPE_VMS) 1195 command = "LIST *.*;0"; 1196 1197 next_state_ = STATE_CTRL_READ; 1198 return SendFtpCommand(command, command, COMMAND_LIST); 1199} 1200 1201int FtpNetworkTransaction::ProcessResponseLIST( 1202 const FtpCtrlResponse& response) { 1203 switch (GetErrorClass(response.status_code)) { 1204 case ERROR_CLASS_INITIATED: 1205 // We want the client to start reading the response at this point. 1206 // It got here either through Start or RestartWithAuth. We want that 1207 // method to complete. Not setting next state here will make DoLoop exit 1208 // and in turn make Start/RestartWithAuth complete. 1209 response_.is_directory_listing = true; 1210 break; 1211 case ERROR_CLASS_OK: 1212 response_.is_directory_listing = true; 1213 next_state_ = STATE_CTRL_WRITE_QUIT; 1214 break; 1215 case ERROR_CLASS_INFO_NEEDED: 1216 return Stop(ERR_INVALID_RESPONSE); 1217 case ERROR_CLASS_TRANSIENT_ERROR: 1218 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1219 case ERROR_CLASS_PERMANENT_ERROR: 1220 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1221 default: 1222 NOTREACHED(); 1223 return Stop(ERR_UNEXPECTED); 1224 } 1225 return OK; 1226} 1227 1228// QUIT command 1229int FtpNetworkTransaction::DoCtrlWriteQUIT() { 1230 std::string command = "QUIT"; 1231 next_state_ = STATE_CTRL_READ; 1232 return SendFtpCommand(command, command, COMMAND_QUIT); 1233} 1234 1235int FtpNetworkTransaction::ProcessResponseQUIT( 1236 const FtpCtrlResponse& response) { 1237 ctrl_socket_->Disconnect(); 1238 return last_error_; 1239} 1240 1241// Data Connection 1242 1243int FtpNetworkTransaction::DoDataConnect() { 1244 next_state_ = STATE_DATA_CONNECT_COMPLETE; 1245 IPEndPoint ip_endpoint; 1246 AddressList data_address; 1247 // Connect to the same host as the control socket to prevent PASV port 1248 // scanning attacks. 1249 int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint); 1250 if (rv != OK) 1251 return Stop(rv); 1252 data_address = AddressList::CreateFromIPAddress( 1253 ip_endpoint.address(), data_connection_port_); 1254 data_socket_.reset(socket_factory_->CreateTransportClientSocket( 1255 data_address, net_log_.net_log(), net_log_.source())); 1256 net_log_.AddEvent( 1257 NetLog::TYPE_FTP_DATA_CONNECTION, 1258 data_socket_->NetLog().source().ToEventParametersCallback()); 1259 return data_socket_->Connect(io_callback_); 1260} 1261 1262int FtpNetworkTransaction::DoDataConnectComplete(int result) { 1263 if (result != OK && use_epsv_) { 1264 // It's possible we hit a broken server, sadly. They can break in different 1265 // ways. Some time out, some reset a connection. Fall back to PASV. 1266 // TODO(phajdan.jr): remember it for future transactions with this server. 1267 // TODO(phajdan.jr): write a test for this code path. 1268 use_epsv_ = false; 1269 next_state_ = STATE_CTRL_WRITE_PASV; 1270 return OK; 1271 } 1272 1273 // Only record the connection error after we've applied all our fallbacks. 1274 // We want to capture the final error, one we're not going to recover from. 1275 RecordDataConnectionError(result); 1276 1277 if (result != OK) 1278 return Stop(result); 1279 1280 next_state_ = state_after_data_connect_complete_; 1281 return OK; 1282} 1283 1284int FtpNetworkTransaction::DoDataRead() { 1285 DCHECK(read_data_buf_.get()); 1286 DCHECK_GT(read_data_buf_len_, 0); 1287 1288 if (data_socket_ == NULL || !data_socket_->IsConnected()) { 1289 // If we don't destroy the data socket completely, some servers will wait 1290 // for us (http://crbug.com/21127). The half-closed TCP connection needs 1291 // to be closed on our side too. 1292 data_socket_.reset(); 1293 1294 if (ctrl_socket_->IsConnected()) { 1295 // Wait for the server's response, we should get it before sending QUIT. 1296 next_state_ = STATE_CTRL_READ; 1297 return OK; 1298 } 1299 1300 // We are no longer connected to the server, so just finish the transaction. 1301 return Stop(OK); 1302 } 1303 1304 next_state_ = STATE_DATA_READ_COMPLETE; 1305 read_data_buf_->data()[0] = 0; 1306 return data_socket_->Read( 1307 read_data_buf_.get(), read_data_buf_len_, io_callback_); 1308} 1309 1310int FtpNetworkTransaction::DoDataReadComplete(int result) { 1311 return result; 1312} 1313 1314// We're using a histogram as a group of counters, with one bucket for each 1315// enumeration value. We're only interested in the values of the counters. 1316// Ignore the shape, average, and standard deviation of the histograms because 1317// they are meaningless. 1318// 1319// We use two histograms. In the first histogram we tally whether the user has 1320// seen an error of that type during the session. In the second histogram we 1321// tally the total number of times the users sees each errer. 1322void FtpNetworkTransaction::RecordDataConnectionError(int result) { 1323 // Gather data for http://crbug.com/3073. See how many users have trouble 1324 // establishing FTP data connection in passive FTP mode. 1325 enum { 1326 // Data connection successful. 1327 NET_ERROR_OK = 0, 1328 1329 // Local firewall blocked the connection. 1330 NET_ERROR_ACCESS_DENIED = 1, 1331 1332 // Connection timed out. 1333 NET_ERROR_TIMED_OUT = 2, 1334 1335 // Connection has been estabilished, but then got broken (either reset 1336 // or aborted). 1337 NET_ERROR_CONNECTION_BROKEN = 3, 1338 1339 // Connection has been refused. 1340 NET_ERROR_CONNECTION_REFUSED = 4, 1341 1342 // No connection to the internet. 1343 NET_ERROR_INTERNET_DISCONNECTED = 5, 1344 1345 // Could not reach the destination address. 1346 NET_ERROR_ADDRESS_UNREACHABLE = 6, 1347 1348 // A programming error in our network stack. 1349 NET_ERROR_UNEXPECTED = 7, 1350 1351 // Other kind of error. 1352 NET_ERROR_OTHER = 20, 1353 1354 NUM_OF_NET_ERROR_TYPES 1355 } type; 1356 switch (result) { 1357 case OK: 1358 type = NET_ERROR_OK; 1359 break; 1360 case ERR_ACCESS_DENIED: 1361 case ERR_NETWORK_ACCESS_DENIED: 1362 type = NET_ERROR_ACCESS_DENIED; 1363 break; 1364 case ERR_TIMED_OUT: 1365 type = NET_ERROR_TIMED_OUT; 1366 break; 1367 case ERR_CONNECTION_ABORTED: 1368 case ERR_CONNECTION_RESET: 1369 case ERR_CONNECTION_CLOSED: 1370 type = NET_ERROR_CONNECTION_BROKEN; 1371 break; 1372 case ERR_CONNECTION_FAILED: 1373 case ERR_CONNECTION_REFUSED: 1374 type = NET_ERROR_CONNECTION_REFUSED; 1375 break; 1376 case ERR_INTERNET_DISCONNECTED: 1377 type = NET_ERROR_INTERNET_DISCONNECTED; 1378 break; 1379 case ERR_ADDRESS_INVALID: 1380 case ERR_ADDRESS_UNREACHABLE: 1381 type = NET_ERROR_ADDRESS_UNREACHABLE; 1382 break; 1383 case ERR_UNEXPECTED: 1384 type = NET_ERROR_UNEXPECTED; 1385 break; 1386 default: 1387 type = NET_ERROR_OTHER; 1388 break; 1389 }; 1390 static bool had_error_type[NUM_OF_NET_ERROR_TYPES]; 1391 1392 DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES); 1393 if (!had_error_type[type]) { 1394 had_error_type[type] = true; 1395 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened", 1396 type, NUM_OF_NET_ERROR_TYPES); 1397 } 1398 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount", 1399 type, NUM_OF_NET_ERROR_TYPES); 1400} 1401 1402} // namespace net 1403