Index: net/ftp/ftp_network_transaction.cc |
diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc |
deleted file mode 100644 |
index ef6adb8bf759124f1bb14462ded90c72dd1de596..0000000000000000000000000000000000000000 |
--- a/net/ftp/ftp_network_transaction.cc |
+++ /dev/null |
@@ -1,1384 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "net/ftp/ftp_network_transaction.h" |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/compiler_specific.h" |
-#include "base/metrics/histogram.h" |
-#include "base/profiler/scoped_tracker.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/strings/string_util.h" |
-#include "base/strings/string_split.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "base/values.h" |
-#include "net/base/address_list.h" |
-#include "net/base/connection_type_histograms.h" |
-#include "net/base/escape.h" |
-#include "net/base/net_errors.h" |
-#include "net/base/net_log.h" |
-#include "net/base/net_util.h" |
-#include "net/ftp/ftp_network_session.h" |
-#include "net/ftp/ftp_request_info.h" |
-#include "net/ftp/ftp_util.h" |
-#include "net/socket/client_socket_factory.h" |
-#include "net/socket/stream_socket.h" |
- |
-const char kCRLF[] = "\r\n"; |
- |
-const int kCtrlBufLen = 1024; |
- |
-namespace { |
- |
-// Returns true if |input| can be safely used as a part of FTP command. |
-bool IsValidFTPCommandString(const std::string& input) { |
- // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII |
- // characters in the command if the request path contains them. To be |
- // compatible, we do the same and allow non-ASCII characters in a command. |
- |
- // Protect agains newline injection attack. |
- if (input.find_first_of("\r\n") != std::string::npos) |
- return false; |
- |
- return true; |
-} |
- |
-enum ErrorClass { |
- // The requested action was initiated. The client should expect another |
- // reply before issuing the next command. |
- ERROR_CLASS_INITIATED, |
- |
- // The requested action has been successfully completed. |
- ERROR_CLASS_OK, |
- |
- // The command has been accepted, but to complete the operation, more |
- // information must be sent by the client. |
- ERROR_CLASS_INFO_NEEDED, |
- |
- // The command was not accepted and the requested action did not take place. |
- // This condition is temporary, and the client is encouraged to restart the |
- // command sequence. |
- ERROR_CLASS_TRANSIENT_ERROR, |
- |
- // The command was not accepted and the requested action did not take place. |
- // This condition is rather permanent, and the client is discouraged from |
- // repeating the exact request. |
- ERROR_CLASS_PERMANENT_ERROR, |
-}; |
- |
-// Returns the error class for given response code. Caller should ensure |
-// that |response_code| is in range 100-599. |
-ErrorClass GetErrorClass(int response_code) { |
- if (response_code >= 100 && response_code <= 199) |
- return ERROR_CLASS_INITIATED; |
- |
- if (response_code >= 200 && response_code <= 299) |
- return ERROR_CLASS_OK; |
- |
- if (response_code >= 300 && response_code <= 399) |
- return ERROR_CLASS_INFO_NEEDED; |
- |
- if (response_code >= 400 && response_code <= 499) |
- return ERROR_CLASS_TRANSIENT_ERROR; |
- |
- if (response_code >= 500 && response_code <= 599) |
- return ERROR_CLASS_PERMANENT_ERROR; |
- |
- // We should not be called on invalid error codes. |
- NOTREACHED() << response_code; |
- return ERROR_CLASS_PERMANENT_ERROR; |
-} |
- |
-// Returns network error code for received FTP |response_code|. |
-int GetNetErrorCodeForFtpResponseCode(int response_code) { |
- switch (response_code) { |
- case 421: |
- return net::ERR_FTP_SERVICE_UNAVAILABLE; |
- case 426: |
- return net::ERR_FTP_TRANSFER_ABORTED; |
- case 450: |
- return net::ERR_FTP_FILE_BUSY; |
- case 500: |
- case 501: |
- return net::ERR_FTP_SYNTAX_ERROR; |
- case 502: |
- case 504: |
- return net::ERR_FTP_COMMAND_NOT_SUPPORTED; |
- case 503: |
- return net::ERR_FTP_BAD_COMMAND_SEQUENCE; |
- default: |
- return net::ERR_FTP_FAILED; |
- } |
-} |
- |
-// From RFC 2428 Section 3: |
-// The text returned in response to the EPSV command MUST be: |
-// <some text> (<d><d><d><tcp-port><d>) |
-// <d> is a delimiter character, ideally to be | |
-bool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response, |
- int* port) { |
- if (response.lines.size() != 1) |
- return false; |
- const char* ptr = response.lines[0].c_str(); |
- while (*ptr && *ptr != '(') |
- ++ptr; |
- if (!*ptr) |
- return false; |
- char sep = *(++ptr); |
- if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep) |
- return false; |
- if (!isdigit(*(++ptr))) |
- return false; |
- *port = *ptr - '0'; |
- while (isdigit(*(++ptr))) { |
- *port *= 10; |
- *port += *ptr - '0'; |
- } |
- if (*ptr != sep) |
- return false; |
- |
- return true; |
-} |
- |
-// There are two way we can receive IP address and port. |
-// (127,0,0,1,23,21) IP address and port encapsulated in (). |
-// 127,0,0,1,23,21 IP address and port without (). |
-// |
-// See RFC 959, Section 4.1.2 |
-bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response, |
- int* port) { |
- if (response.lines.size() != 1) |
- return false; |
- |
- std::string line(response.lines[0]); |
- if (!base::IsStringASCII(line)) |
- return false; |
- if (line.length() < 2) |
- return false; |
- |
- size_t paren_pos = line.find('('); |
- if (paren_pos == std::string::npos) { |
- // Find the first comma and use it to locate the beginning |
- // of the response data. |
- size_t comma_pos = line.find(','); |
- if (comma_pos == std::string::npos) |
- return false; |
- |
- size_t space_pos = line.rfind(' ', comma_pos); |
- if (space_pos != std::string::npos) |
- line = line.substr(space_pos + 1); |
- } else { |
- // Remove the parentheses and use the text inside them. |
- size_t closing_paren_pos = line.rfind(')'); |
- if (closing_paren_pos == std::string::npos) |
- return false; |
- if (closing_paren_pos <= paren_pos) |
- return false; |
- |
- line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1); |
- } |
- |
- // Split the line into comma-separated pieces and extract |
- // the last two. |
- std::vector<std::string> pieces; |
- base::SplitString(line, ',', &pieces); |
- if (pieces.size() != 6) |
- return false; |
- |
- // Ignore the IP address supplied in the response. We are always going |
- // to connect back to the same server to prevent FTP PASV port scanning. |
- int p0, p1; |
- if (!base::StringToInt(pieces[4], &p0)) |
- return false; |
- if (!base::StringToInt(pieces[5], &p1)) |
- return false; |
- *port = (p0 << 8) + p1; |
- |
- return true; |
-} |
- |
-} // namespace |
- |
-namespace net { |
- |
-FtpNetworkTransaction::FtpNetworkTransaction( |
- FtpNetworkSession* session, |
- ClientSocketFactory* socket_factory) |
- : command_sent_(COMMAND_NONE), |
- io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete, |
- base::Unretained(this))), |
- session_(session), |
- request_(NULL), |
- resolver_(session->host_resolver()), |
- read_ctrl_buf_(new IOBuffer(kCtrlBufLen)), |
- read_data_buf_len_(0), |
- last_error_(OK), |
- system_type_(SYSTEM_TYPE_UNKNOWN), |
- // Use image (binary) transfer by default. It should always work, |
- // whereas the ascii transfer may damage binary data. |
- data_type_(DATA_TYPE_IMAGE), |
- resource_type_(RESOURCE_TYPE_UNKNOWN), |
- use_epsv_(true), |
- data_connection_port_(0), |
- socket_factory_(socket_factory), |
- next_state_(STATE_NONE), |
- state_after_data_connect_complete_(STATE_NONE) { |
-} |
- |
-FtpNetworkTransaction::~FtpNetworkTransaction() { |
-} |
- |
-int FtpNetworkTransaction::Stop(int error) { |
- if (command_sent_ == COMMAND_QUIT) |
- return error; |
- |
- next_state_ = STATE_CTRL_WRITE_QUIT; |
- last_error_ = error; |
- return OK; |
-} |
- |
-int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info, |
- const CompletionCallback& callback, |
- const BoundNetLog& net_log) { |
- net_log_ = net_log; |
- request_ = request_info; |
- |
- ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_)); |
- |
- if (request_->url.has_username()) { |
- base::string16 username; |
- base::string16 password; |
- GetIdentityFromURL(request_->url, &username, &password); |
- credentials_.Set(username, password); |
- } else { |
- credentials_.Set(base::ASCIIToUTF16("anonymous"), |
- base::ASCIIToUTF16("chrome@example.com")); |
- } |
- |
- DetectTypecode(); |
- |
- next_state_ = STATE_CTRL_RESOLVE_HOST; |
- int rv = DoLoop(OK); |
- if (rv == ERR_IO_PENDING) |
- user_callback_ = callback; |
- return rv; |
-} |
- |
-int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials, |
- const CompletionCallback& callback) { |
- ResetStateForRestart(); |
- |
- credentials_ = credentials; |
- |
- next_state_ = STATE_CTRL_RESOLVE_HOST; |
- int rv = DoLoop(OK); |
- if (rv == ERR_IO_PENDING) |
- user_callback_ = callback; |
- return rv; |
-} |
- |
-int FtpNetworkTransaction::Read(IOBuffer* buf, |
- int buf_len, |
- const CompletionCallback& callback) { |
- DCHECK(buf); |
- DCHECK_GT(buf_len, 0); |
- |
- read_data_buf_ = buf; |
- read_data_buf_len_ = buf_len; |
- |
- next_state_ = STATE_DATA_READ; |
- int rv = DoLoop(OK); |
- if (rv == ERR_IO_PENDING) |
- user_callback_ = callback; |
- return rv; |
-} |
- |
-const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const { |
- return &response_; |
-} |
- |
-LoadState FtpNetworkTransaction::GetLoadState() const { |
- if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE) |
- return LOAD_STATE_RESOLVING_HOST; |
- |
- if (next_state_ == STATE_CTRL_CONNECT_COMPLETE || |
- next_state_ == STATE_DATA_CONNECT_COMPLETE) |
- return LOAD_STATE_CONNECTING; |
- |
- if (next_state_ == STATE_DATA_READ_COMPLETE) |
- return LOAD_STATE_READING_RESPONSE; |
- |
- if (command_sent_ == COMMAND_RETR && read_data_buf_.get()) |
- return LOAD_STATE_READING_RESPONSE; |
- |
- if (command_sent_ == COMMAND_QUIT) |
- return LOAD_STATE_IDLE; |
- |
- if (command_sent_ != COMMAND_NONE) |
- return LOAD_STATE_SENDING_REQUEST; |
- |
- return LOAD_STATE_IDLE; |
-} |
- |
-uint64 FtpNetworkTransaction::GetUploadProgress() const { |
- return 0; |
-} |
- |
-void FtpNetworkTransaction::ResetStateForRestart() { |
- command_sent_ = COMMAND_NONE; |
- user_callback_.Reset(); |
- response_ = FtpResponseInfo(); |
- read_ctrl_buf_ = new IOBuffer(kCtrlBufLen); |
- ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_)); |
- read_data_buf_ = NULL; |
- read_data_buf_len_ = 0; |
- if (write_buf_.get()) |
- write_buf_->SetOffset(0); |
- last_error_ = OK; |
- data_connection_port_ = 0; |
- ctrl_socket_.reset(); |
- data_socket_.reset(); |
- next_state_ = STATE_NONE; |
- state_after_data_connect_complete_ = STATE_NONE; |
-} |
- |
-void FtpNetworkTransaction::EstablishDataConnection(State state_after_connect) { |
- DCHECK(state_after_connect == STATE_CTRL_WRITE_RETR || |
- state_after_connect == STATE_CTRL_WRITE_LIST); |
- state_after_data_connect_complete_ = state_after_connect; |
- next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV; |
-} |
- |
-void FtpNetworkTransaction::DoCallback(int rv) { |
- DCHECK(rv != ERR_IO_PENDING); |
- DCHECK(!user_callback_.is_null()); |
- |
- // Since Run may result in Read being called, clear callback_ up front. |
- CompletionCallback c = user_callback_; |
- user_callback_.Reset(); |
- c.Run(rv); |
-} |
- |
-void FtpNetworkTransaction::OnIOComplete(int result) { |
- // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed. |
- tracked_objects::ScopedTracker tracking_profile( |
- FROM_HERE_WITH_EXPLICIT_FUNCTION( |
- "436634 FtpNetworkTransaction::OnIOComplete")); |
- |
- int rv = DoLoop(result); |
- if (rv != ERR_IO_PENDING) |
- DoCallback(rv); |
-} |
- |
-int FtpNetworkTransaction::ProcessCtrlResponse() { |
- FtpCtrlResponse response = ctrl_response_buffer_->PopResponse(); |
- |
- int rv = OK; |
- switch (command_sent_) { |
- case COMMAND_NONE: |
- // TODO(phajdan.jr): Check for errors in the welcome message. |
- next_state_ = STATE_CTRL_WRITE_USER; |
- break; |
- case COMMAND_USER: |
- rv = ProcessResponseUSER(response); |
- break; |
- case COMMAND_PASS: |
- rv = ProcessResponsePASS(response); |
- break; |
- case COMMAND_SYST: |
- rv = ProcessResponseSYST(response); |
- break; |
- case COMMAND_PWD: |
- rv = ProcessResponsePWD(response); |
- break; |
- case COMMAND_TYPE: |
- rv = ProcessResponseTYPE(response); |
- break; |
- case COMMAND_EPSV: |
- rv = ProcessResponseEPSV(response); |
- break; |
- case COMMAND_PASV: |
- rv = ProcessResponsePASV(response); |
- break; |
- case COMMAND_SIZE: |
- rv = ProcessResponseSIZE(response); |
- break; |
- case COMMAND_RETR: |
- rv = ProcessResponseRETR(response); |
- break; |
- case COMMAND_CWD: |
- rv = ProcessResponseCWD(response); |
- break; |
- case COMMAND_LIST: |
- rv = ProcessResponseLIST(response); |
- break; |
- case COMMAND_QUIT: |
- rv = ProcessResponseQUIT(response); |
- break; |
- default: |
- LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_; |
- return ERR_UNEXPECTED; |
- } |
- |
- // We may get multiple responses for some commands, |
- // see http://crbug.com/18036. |
- while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) { |
- response = ctrl_response_buffer_->PopResponse(); |
- |
- switch (command_sent_) { |
- case COMMAND_RETR: |
- rv = ProcessResponseRETR(response); |
- break; |
- case COMMAND_LIST: |
- rv = ProcessResponseLIST(response); |
- break; |
- default: |
- // Multiple responses for other commands are invalid. |
- return Stop(ERR_INVALID_RESPONSE); |
- } |
- } |
- |
- return rv; |
-} |
- |
-// Used to prepare and send FTP command. |
-int FtpNetworkTransaction::SendFtpCommand(const std::string& command, |
- const std::string& command_for_log, |
- Command cmd) { |
- // If we send a new command when we still have unprocessed responses |
- // for previous commands, the response receiving code will have no way to know |
- // which responses are for which command. |
- DCHECK(!ctrl_response_buffer_->ResponseAvailable()); |
- |
- DCHECK(!write_command_buf_.get()); |
- DCHECK(!write_buf_.get()); |
- |
- if (!IsValidFTPCommandString(command)) { |
- // Callers should validate the command themselves and return a more specific |
- // error code. |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- |
- command_sent_ = cmd; |
- |
- write_command_buf_ = new IOBufferWithSize(command.length() + 2); |
- write_buf_ = new DrainableIOBuffer(write_command_buf_.get(), |
- write_command_buf_->size()); |
- memcpy(write_command_buf_->data(), command.data(), command.length()); |
- memcpy(write_command_buf_->data() + command.length(), kCRLF, 2); |
- |
- net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT, |
- NetLog::StringCallback("command", &command_for_log)); |
- |
- next_state_ = STATE_CTRL_WRITE; |
- return OK; |
-} |
- |
-std::string FtpNetworkTransaction::GetRequestPathForFtpCommand( |
- bool is_directory) const { |
- std::string path(current_remote_directory_); |
- if (request_->url.has_path()) { |
- std::string gurl_path(request_->url.path()); |
- |
- // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path. |
- std::string::size_type pos = gurl_path.rfind(';'); |
- if (pos != std::string::npos) |
- gurl_path.resize(pos); |
- |
- path.append(gurl_path); |
- } |
- // Make sure that if the path is expected to be a file, it won't end |
- // with a trailing slash. |
- if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/') |
- path.erase(path.length() - 1); |
- UnescapeRule::Type unescape_rules = UnescapeRule::SPACES | |
- UnescapeRule::URL_SPECIAL_CHARS; |
- // This may unescape to non-ASCII characters, but we allow that. See the |
- // comment for IsValidFTPCommandString. |
- path = net::UnescapeURLComponent(path, unescape_rules); |
- |
- if (system_type_ == SYSTEM_TYPE_VMS) { |
- if (is_directory) |
- path = FtpUtil::UnixDirectoryPathToVMS(path); |
- else |
- path = FtpUtil::UnixFilePathToVMS(path); |
- } |
- |
- DCHECK(IsValidFTPCommandString(path)); |
- return path; |
-} |
- |
-void FtpNetworkTransaction::DetectTypecode() { |
- if (!request_->url.has_path()) |
- return; |
- std::string gurl_path(request_->url.path()); |
- |
- // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path. |
- std::string::size_type pos = gurl_path.rfind(';'); |
- if (pos == std::string::npos) |
- return; |
- std::string typecode_string(gurl_path.substr(pos)); |
- if (typecode_string == ";type=a") { |
- data_type_ = DATA_TYPE_ASCII; |
- resource_type_ = RESOURCE_TYPE_FILE; |
- } else if (typecode_string == ";type=i") { |
- data_type_ = DATA_TYPE_IMAGE; |
- resource_type_ = RESOURCE_TYPE_FILE; |
- } else if (typecode_string == ";type=d") { |
- resource_type_ = RESOURCE_TYPE_DIRECTORY; |
- } |
-} |
- |
-int FtpNetworkTransaction::DoLoop(int result) { |
- DCHECK(next_state_ != STATE_NONE); |
- |
- int rv = result; |
- do { |
- State state = next_state_; |
- next_state_ = STATE_NONE; |
- switch (state) { |
- case STATE_CTRL_RESOLVE_HOST: |
- DCHECK(rv == OK); |
- rv = DoCtrlResolveHost(); |
- break; |
- case STATE_CTRL_RESOLVE_HOST_COMPLETE: |
- rv = DoCtrlResolveHostComplete(rv); |
- break; |
- case STATE_CTRL_CONNECT: |
- DCHECK(rv == OK); |
- rv = DoCtrlConnect(); |
- break; |
- case STATE_CTRL_CONNECT_COMPLETE: |
- rv = DoCtrlConnectComplete(rv); |
- break; |
- case STATE_CTRL_READ: |
- DCHECK(rv == OK); |
- rv = DoCtrlRead(); |
- break; |
- case STATE_CTRL_READ_COMPLETE: |
- rv = DoCtrlReadComplete(rv); |
- break; |
- case STATE_CTRL_WRITE: |
- DCHECK(rv == OK); |
- rv = DoCtrlWrite(); |
- break; |
- case STATE_CTRL_WRITE_COMPLETE: |
- rv = DoCtrlWriteComplete(rv); |
- break; |
- case STATE_CTRL_WRITE_USER: |
- DCHECK(rv == OK); |
- rv = DoCtrlWriteUSER(); |
- break; |
- case STATE_CTRL_WRITE_PASS: |
- DCHECK(rv == OK); |
- rv = DoCtrlWritePASS(); |
- break; |
- case STATE_CTRL_WRITE_SYST: |
- DCHECK(rv == OK); |
- rv = DoCtrlWriteSYST(); |
- break; |
- case STATE_CTRL_WRITE_PWD: |
- DCHECK(rv == OK); |
- rv = DoCtrlWritePWD(); |
- break; |
- case STATE_CTRL_WRITE_TYPE: |
- DCHECK(rv == OK); |
- rv = DoCtrlWriteTYPE(); |
- break; |
- case STATE_CTRL_WRITE_EPSV: |
- DCHECK(rv == OK); |
- rv = DoCtrlWriteEPSV(); |
- break; |
- case STATE_CTRL_WRITE_PASV: |
- DCHECK(rv == OK); |
- rv = DoCtrlWritePASV(); |
- break; |
- case STATE_CTRL_WRITE_RETR: |
- DCHECK(rv == OK); |
- rv = DoCtrlWriteRETR(); |
- break; |
- case STATE_CTRL_WRITE_SIZE: |
- DCHECK(rv == OK); |
- rv = DoCtrlWriteSIZE(); |
- break; |
- case STATE_CTRL_WRITE_CWD: |
- DCHECK(rv == OK); |
- rv = DoCtrlWriteCWD(); |
- break; |
- case STATE_CTRL_WRITE_LIST: |
- DCHECK(rv == OK); |
- rv = DoCtrlWriteLIST(); |
- break; |
- case STATE_CTRL_WRITE_QUIT: |
- DCHECK(rv == OK); |
- rv = DoCtrlWriteQUIT(); |
- break; |
- case STATE_DATA_CONNECT: |
- DCHECK(rv == OK); |
- rv = DoDataConnect(); |
- break; |
- case STATE_DATA_CONNECT_COMPLETE: |
- rv = DoDataConnectComplete(rv); |
- break; |
- case STATE_DATA_READ: |
- DCHECK(rv == OK); |
- rv = DoDataRead(); |
- break; |
- case STATE_DATA_READ_COMPLETE: |
- rv = DoDataReadComplete(rv); |
- break; |
- default: |
- NOTREACHED() << "bad state"; |
- rv = ERR_UNEXPECTED; |
- break; |
- } |
- } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); |
- return rv; |
-} |
- |
-int FtpNetworkTransaction::DoCtrlResolveHost() { |
- next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE; |
- |
- HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url)); |
- // No known referrer. |
- return resolver_.Resolve( |
- info, |
- DEFAULT_PRIORITY, |
- &addresses_, |
- base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)), |
- net_log_); |
-} |
- |
-int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) { |
- if (result == OK) |
- next_state_ = STATE_CTRL_CONNECT; |
- return result; |
-} |
- |
-int FtpNetworkTransaction::DoCtrlConnect() { |
- next_state_ = STATE_CTRL_CONNECT_COMPLETE; |
- ctrl_socket_ = socket_factory_->CreateTransportClientSocket( |
- addresses_, net_log_.net_log(), net_log_.source()); |
- net_log_.AddEvent( |
- NetLog::TYPE_FTP_CONTROL_CONNECTION, |
- ctrl_socket_->NetLog().source().ToEventParametersCallback()); |
- return ctrl_socket_->Connect(io_callback_); |
-} |
- |
-int FtpNetworkTransaction::DoCtrlConnectComplete(int result) { |
- if (result == OK) { |
- // Put the peer's IP address and port into the response. |
- IPEndPoint ip_endpoint; |
- result = ctrl_socket_->GetPeerAddress(&ip_endpoint); |
- if (result == OK) { |
- response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint); |
- next_state_ = STATE_CTRL_READ; |
- |
- if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) { |
- // Do not use EPSV for IPv4 connections. Some servers become confused |
- // and we time out while waiting to connect. PASV is perfectly fine for |
- // IPv4. Note that this blacklists IPv4 not to use EPSV instead of |
- // whitelisting IPv6 to use it, to make the code more future-proof: |
- // all future protocols should just use EPSV. |
- use_epsv_ = false; |
- } |
- } |
- } |
- return result; |
-} |
- |
-int FtpNetworkTransaction::DoCtrlRead() { |
- next_state_ = STATE_CTRL_READ_COMPLETE; |
- return ctrl_socket_->Read(read_ctrl_buf_.get(), kCtrlBufLen, io_callback_); |
-} |
- |
-int FtpNetworkTransaction::DoCtrlReadComplete(int result) { |
- if (result == 0) { |
- // Some servers (for example Pure-FTPd) apparently close the control |
- // connection when anonymous login is not permitted. For more details |
- // see http://crbug.com/25023. |
- if (command_sent_ == COMMAND_USER && |
- credentials_.username() == base::ASCIIToUTF16("anonymous")) { |
- response_.needs_auth = true; |
- } |
- return Stop(ERR_EMPTY_RESPONSE); |
- } |
- if (result < 0) |
- return Stop(result); |
- |
- ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result); |
- |
- if (!ctrl_response_buffer_->ResponseAvailable()) { |
- // Read more data from the control socket. |
- next_state_ = STATE_CTRL_READ; |
- return OK; |
- } |
- |
- return ProcessCtrlResponse(); |
-} |
- |
-int FtpNetworkTransaction::DoCtrlWrite() { |
- next_state_ = STATE_CTRL_WRITE_COMPLETE; |
- |
- return ctrl_socket_->Write( |
- write_buf_.get(), write_buf_->BytesRemaining(), io_callback_); |
-} |
- |
-int FtpNetworkTransaction::DoCtrlWriteComplete(int result) { |
- if (result < 0) |
- return result; |
- |
- write_buf_->DidConsume(result); |
- if (write_buf_->BytesRemaining() == 0) { |
- // Clear the write buffer. |
- write_buf_ = NULL; |
- write_command_buf_ = NULL; |
- |
- next_state_ = STATE_CTRL_READ; |
- } else { |
- next_state_ = STATE_CTRL_WRITE; |
- } |
- return OK; |
-} |
- |
-// FTP Commands and responses |
- |
-// USER Command. |
-int FtpNetworkTransaction::DoCtrlWriteUSER() { |
- std::string command = "USER " + base::UTF16ToUTF8(credentials_.username()); |
- |
- if (!IsValidFTPCommandString(command)) |
- return Stop(ERR_MALFORMED_IDENTITY); |
- |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, "USER ***", COMMAND_USER); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponseUSER( |
- const FtpCtrlResponse& response) { |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_OK: |
- next_state_ = STATE_CTRL_WRITE_SYST; |
- break; |
- case ERROR_CLASS_INFO_NEEDED: |
- next_state_ = STATE_CTRL_WRITE_PASS; |
- break; |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- case ERROR_CLASS_PERMANENT_ERROR: |
- response_.needs_auth = true; |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- return OK; |
-} |
- |
-// PASS command. |
-int FtpNetworkTransaction::DoCtrlWritePASS() { |
- std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password()); |
- |
- if (!IsValidFTPCommandString(command)) |
- return Stop(ERR_MALFORMED_IDENTITY); |
- |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, "PASS ***", COMMAND_PASS); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponsePASS( |
- const FtpCtrlResponse& response) { |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_OK: |
- next_state_ = STATE_CTRL_WRITE_SYST; |
- break; |
- case ERROR_CLASS_INFO_NEEDED: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- case ERROR_CLASS_PERMANENT_ERROR: |
- response_.needs_auth = true; |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- return OK; |
-} |
- |
-// SYST command. |
-int FtpNetworkTransaction::DoCtrlWriteSYST() { |
- std::string command = "SYST"; |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, command, COMMAND_SYST); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponseSYST( |
- const FtpCtrlResponse& response) { |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_INITIATED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_OK: { |
- // All important info should be on the first line. |
- std::string line = response.lines[0]; |
- // The response should be ASCII, which allows us to do case-insensitive |
- // comparisons easily. If it is not ASCII, we leave the system type |
- // as unknown. |
- if (base::IsStringASCII(line)) { |
- line = base::StringToLowerASCII(line); |
- |
- // Remove all whitespace, to correctly handle cases like fancy "V M S" |
- // response instead of "VMS". |
- base::RemoveChars(line, base::kWhitespaceASCII, &line); |
- |
- // The "magic" strings we test for below have been gathered by an |
- // empirical study. VMS needs to come first because some VMS systems |
- // also respond with "UNIX emulation", which is not perfect. It is much |
- // more reliable to talk to these servers in their native language. |
- if (line.find("vms") != std::string::npos) { |
- system_type_ = SYSTEM_TYPE_VMS; |
- } else if (line.find("l8") != std::string::npos || |
- line.find("unix") != std::string::npos || |
- line.find("bsd") != std::string::npos) { |
- system_type_ = SYSTEM_TYPE_UNIX; |
- } else if (line.find("win32") != std::string::npos || |
- line.find("windows") != std::string::npos) { |
- system_type_ = SYSTEM_TYPE_WINDOWS; |
- } else if (line.find("os/2") != std::string::npos) { |
- system_type_ = SYSTEM_TYPE_OS2; |
- } |
- } |
- next_state_ = STATE_CTRL_WRITE_PWD; |
- break; |
- } |
- case ERROR_CLASS_INFO_NEEDED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- case ERROR_CLASS_PERMANENT_ERROR: |
- // Server does not recognize the SYST command so proceed. |
- next_state_ = STATE_CTRL_WRITE_PWD; |
- break; |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- return OK; |
-} |
- |
-// PWD command. |
-int FtpNetworkTransaction::DoCtrlWritePWD() { |
- std::string command = "PWD"; |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, command, COMMAND_PWD); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) { |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_INITIATED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_OK: { |
- // The info we look for should be on the first line. |
- std::string line = response.lines[0]; |
- if (line.empty()) |
- return Stop(ERR_INVALID_RESPONSE); |
- std::string::size_type quote_pos = line.find('"'); |
- if (quote_pos != std::string::npos) { |
- line = line.substr(quote_pos + 1); |
- quote_pos = line.find('"'); |
- if (quote_pos == std::string::npos) |
- return Stop(ERR_INVALID_RESPONSE); |
- line = line.substr(0, quote_pos); |
- } |
- if (system_type_ == SYSTEM_TYPE_VMS) |
- line = FtpUtil::VMSPathToUnix(line); |
- if (line.length() && line[line.length() - 1] == '/') |
- line.erase(line.length() - 1); |
- current_remote_directory_ = line; |
- next_state_ = STATE_CTRL_WRITE_TYPE; |
- break; |
- } |
- case ERROR_CLASS_INFO_NEEDED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- case ERROR_CLASS_PERMANENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- return OK; |
-} |
- |
-// TYPE command. |
-int FtpNetworkTransaction::DoCtrlWriteTYPE() { |
- std::string command = "TYPE "; |
- if (data_type_ == DATA_TYPE_ASCII) { |
- command += "A"; |
- } else if (data_type_ == DATA_TYPE_IMAGE) { |
- command += "I"; |
- } else { |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, command, COMMAND_TYPE); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponseTYPE( |
- const FtpCtrlResponse& response) { |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_INITIATED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_OK: |
- next_state_ = STATE_CTRL_WRITE_SIZE; |
- break; |
- case ERROR_CLASS_INFO_NEEDED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- case ERROR_CLASS_PERMANENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- return OK; |
-} |
- |
-// EPSV command |
-int FtpNetworkTransaction::DoCtrlWriteEPSV() { |
- const std::string command = "EPSV"; |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, command, COMMAND_EPSV); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponseEPSV( |
- const FtpCtrlResponse& response) { |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_INITIATED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_OK: { |
- int port; |
- if (!ExtractPortFromEPSVResponse(response, &port)) |
- return Stop(ERR_INVALID_RESPONSE); |
- if (port < 1024 || !IsPortAllowedByFtp(port)) |
- return Stop(ERR_UNSAFE_PORT); |
- data_connection_port_ = static_cast<uint16>(port); |
- next_state_ = STATE_DATA_CONNECT; |
- break; |
- } |
- case ERROR_CLASS_INFO_NEEDED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- case ERROR_CLASS_PERMANENT_ERROR: |
- use_epsv_ = false; |
- next_state_ = STATE_CTRL_WRITE_PASV; |
- return OK; |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- return OK; |
-} |
- |
-// PASV command |
-int FtpNetworkTransaction::DoCtrlWritePASV() { |
- std::string command = "PASV"; |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, command, COMMAND_PASV); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponsePASV( |
- const FtpCtrlResponse& response) { |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_INITIATED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_OK: { |
- int port; |
- if (!ExtractPortFromPASVResponse(response, &port)) |
- return Stop(ERR_INVALID_RESPONSE); |
- if (port < 1024 || !IsPortAllowedByFtp(port)) |
- return Stop(ERR_UNSAFE_PORT); |
- data_connection_port_ = static_cast<uint16>(port); |
- next_state_ = STATE_DATA_CONNECT; |
- break; |
- } |
- case ERROR_CLASS_INFO_NEEDED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- case ERROR_CLASS_PERMANENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- return OK; |
-} |
- |
-// RETR command |
-int FtpNetworkTransaction::DoCtrlWriteRETR() { |
- std::string command = "RETR " + GetRequestPathForFtpCommand(false); |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, command, COMMAND_RETR); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponseRETR( |
- const FtpCtrlResponse& response) { |
- // Resource type should be either filled in by DetectTypecode() or |
- // detected with CWD. RETR is sent only when the resource is a file. |
- DCHECK_EQ(RESOURCE_TYPE_FILE, resource_type_); |
- |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_INITIATED: |
- // We want the client to start reading the response at this point. |
- // It got here either through Start or RestartWithAuth. We want that |
- // method to complete. Not setting next state here will make DoLoop exit |
- // and in turn make Start/RestartWithAuth complete. |
- break; |
- case ERROR_CLASS_OK: |
- next_state_ = STATE_CTRL_WRITE_QUIT; |
- break; |
- case ERROR_CLASS_INFO_NEEDED: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- case ERROR_CLASS_PERMANENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- |
- return OK; |
-} |
- |
-// SIZE command |
-int FtpNetworkTransaction::DoCtrlWriteSIZE() { |
- std::string command = "SIZE " + GetRequestPathForFtpCommand(false); |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, command, COMMAND_SIZE); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponseSIZE( |
- const FtpCtrlResponse& response) { |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_INITIATED: |
- break; |
- case ERROR_CLASS_OK: |
- if (response.lines.size() != 1) |
- return Stop(ERR_INVALID_RESPONSE); |
- int64 size; |
- if (!base::StringToInt64(response.lines[0], &size)) |
- return Stop(ERR_INVALID_RESPONSE); |
- if (size < 0) |
- return Stop(ERR_INVALID_RESPONSE); |
- |
- // A successful response to SIZE does not mean the resource is a file. |
- // Some FTP servers (for example, the qnx one) send a SIZE even for |
- // directories. |
- response_.expected_content_size = size; |
- break; |
- case ERROR_CLASS_INFO_NEEDED: |
- break; |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- break; |
- case ERROR_CLASS_PERMANENT_ERROR: |
- // It's possible that SIZE failed because the path is a directory. |
- // TODO(xunjieli): Add a test for this case. |
- if (resource_type_ == RESOURCE_TYPE_UNKNOWN && |
- response.status_code != 550) { |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- } |
- break; |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- |
- // If the resource is known beforehand to be a file, RETR should be issued, |
- // otherwise do CWD which will detect the resource type. |
- if (resource_type_ == RESOURCE_TYPE_FILE) |
- EstablishDataConnection(STATE_CTRL_WRITE_RETR); |
- else |
- next_state_ = STATE_CTRL_WRITE_CWD; |
- return OK; |
-} |
- |
-// CWD command |
-int FtpNetworkTransaction::DoCtrlWriteCWD() { |
- std::string command = "CWD " + GetRequestPathForFtpCommand(true); |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, command, COMMAND_CWD); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) { |
- // CWD should be invoked only when the resource is not a file. |
- DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_); |
- |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_INITIATED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_OK: |
- resource_type_ = RESOURCE_TYPE_DIRECTORY; |
- EstablishDataConnection(STATE_CTRL_WRITE_LIST); |
- break; |
- case ERROR_CLASS_INFO_NEEDED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- // Some FTP servers send response 451 (not a valid CWD response according |
- // to RFC 959) instead of 550. |
- if (response.status_code == 451) |
- return ProcessResponseCWDNotADirectory(); |
- |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- case ERROR_CLASS_PERMANENT_ERROR: |
- if (response.status_code == 550) |
- return ProcessResponseCWDNotADirectory(); |
- |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- |
- return OK; |
-} |
- |
-int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() { |
- if (resource_type_ == RESOURCE_TYPE_DIRECTORY) { |
- // We're assuming that the resource is a directory, but the server |
- // says it's not true. The most probable interpretation is that it |
- // doesn't exist (with FTP we can't be sure). |
- return Stop(ERR_FILE_NOT_FOUND); |
- } |
- |
- // If it is not a directory, it is probably a file. |
- resource_type_ = RESOURCE_TYPE_FILE; |
- |
- EstablishDataConnection(STATE_CTRL_WRITE_RETR); |
- return OK; |
-} |
- |
-// LIST command |
-int FtpNetworkTransaction::DoCtrlWriteLIST() { |
- // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option |
- // forces LIST output instead of NLST (which would be ambiguous for us |
- // to parse). |
- std::string command("LIST -l"); |
- if (system_type_ == SYSTEM_TYPE_VMS) |
- command = "LIST *.*;0"; |
- |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, command, COMMAND_LIST); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponseLIST( |
- const FtpCtrlResponse& response) { |
- // Resource type should be either filled in by DetectTypecode() or |
- // detected with CWD. LIST is sent only when the resource is a directory. |
- DCHECK_EQ(RESOURCE_TYPE_DIRECTORY, resource_type_); |
- |
- switch (GetErrorClass(response.status_code)) { |
- case ERROR_CLASS_INITIATED: |
- // We want the client to start reading the response at this point. |
- // It got here either through Start or RestartWithAuth. We want that |
- // method to complete. Not setting next state here will make DoLoop exit |
- // and in turn make Start/RestartWithAuth complete. |
- response_.is_directory_listing = true; |
- break; |
- case ERROR_CLASS_OK: |
- response_.is_directory_listing = true; |
- next_state_ = STATE_CTRL_WRITE_QUIT; |
- break; |
- case ERROR_CLASS_INFO_NEEDED: |
- return Stop(ERR_INVALID_RESPONSE); |
- case ERROR_CLASS_TRANSIENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- case ERROR_CLASS_PERMANENT_ERROR: |
- return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
- default: |
- NOTREACHED(); |
- return Stop(ERR_UNEXPECTED); |
- } |
- return OK; |
-} |
- |
-// QUIT command |
-int FtpNetworkTransaction::DoCtrlWriteQUIT() { |
- std::string command = "QUIT"; |
- next_state_ = STATE_CTRL_READ; |
- return SendFtpCommand(command, command, COMMAND_QUIT); |
-} |
- |
-int FtpNetworkTransaction::ProcessResponseQUIT( |
- const FtpCtrlResponse& response) { |
- ctrl_socket_->Disconnect(); |
- return last_error_; |
-} |
- |
-// Data Connection |
- |
-int FtpNetworkTransaction::DoDataConnect() { |
- next_state_ = STATE_DATA_CONNECT_COMPLETE; |
- IPEndPoint ip_endpoint; |
- AddressList data_address; |
- // Connect to the same host as the control socket to prevent PASV port |
- // scanning attacks. |
- int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint); |
- if (rv != OK) |
- return Stop(rv); |
- data_address = AddressList::CreateFromIPAddress( |
- ip_endpoint.address(), data_connection_port_); |
- data_socket_ = socket_factory_->CreateTransportClientSocket( |
- data_address, net_log_.net_log(), net_log_.source()); |
- net_log_.AddEvent( |
- NetLog::TYPE_FTP_DATA_CONNECTION, |
- data_socket_->NetLog().source().ToEventParametersCallback()); |
- return data_socket_->Connect(io_callback_); |
-} |
- |
-int FtpNetworkTransaction::DoDataConnectComplete(int result) { |
- if (result != OK && use_epsv_) { |
- // It's possible we hit a broken server, sadly. They can break in different |
- // ways. Some time out, some reset a connection. Fall back to PASV. |
- // TODO(phajdan.jr): remember it for future transactions with this server. |
- // TODO(phajdan.jr): write a test for this code path. |
- use_epsv_ = false; |
- next_state_ = STATE_CTRL_WRITE_PASV; |
- return OK; |
- } |
- |
- // Only record the connection error after we've applied all our fallbacks. |
- // We want to capture the final error, one we're not going to recover from. |
- RecordDataConnectionError(result); |
- |
- if (result != OK) |
- return Stop(result); |
- |
- next_state_ = state_after_data_connect_complete_; |
- return OK; |
-} |
- |
-int FtpNetworkTransaction::DoDataRead() { |
- DCHECK(read_data_buf_.get()); |
- DCHECK_GT(read_data_buf_len_, 0); |
- |
- if (data_socket_ == NULL || !data_socket_->IsConnected()) { |
- // If we don't destroy the data socket completely, some servers will wait |
- // for us (http://crbug.com/21127). The half-closed TCP connection needs |
- // to be closed on our side too. |
- data_socket_.reset(); |
- |
- if (ctrl_socket_->IsConnected()) { |
- // Wait for the server's response, we should get it before sending QUIT. |
- next_state_ = STATE_CTRL_READ; |
- return OK; |
- } |
- |
- // We are no longer connected to the server, so just finish the transaction. |
- return Stop(OK); |
- } |
- |
- next_state_ = STATE_DATA_READ_COMPLETE; |
- read_data_buf_->data()[0] = 0; |
- return data_socket_->Read( |
- read_data_buf_.get(), read_data_buf_len_, io_callback_); |
-} |
- |
-int FtpNetworkTransaction::DoDataReadComplete(int result) { |
- return result; |
-} |
- |
-// We're using a histogram as a group of counters, with one bucket for each |
-// enumeration value. We're only interested in the values of the counters. |
-// Ignore the shape, average, and standard deviation of the histograms because |
-// they are meaningless. |
-// |
-// We use two histograms. In the first histogram we tally whether the user has |
-// seen an error of that type during the session. In the second histogram we |
-// tally the total number of times the users sees each errer. |
-void FtpNetworkTransaction::RecordDataConnectionError(int result) { |
- // Gather data for http://crbug.com/3073. See how many users have trouble |
- // establishing FTP data connection in passive FTP mode. |
- enum { |
- // Data connection successful. |
- NET_ERROR_OK = 0, |
- |
- // Local firewall blocked the connection. |
- NET_ERROR_ACCESS_DENIED = 1, |
- |
- // Connection timed out. |
- NET_ERROR_TIMED_OUT = 2, |
- |
- // Connection has been estabilished, but then got broken (either reset |
- // or aborted). |
- NET_ERROR_CONNECTION_BROKEN = 3, |
- |
- // Connection has been refused. |
- NET_ERROR_CONNECTION_REFUSED = 4, |
- |
- // No connection to the internet. |
- NET_ERROR_INTERNET_DISCONNECTED = 5, |
- |
- // Could not reach the destination address. |
- NET_ERROR_ADDRESS_UNREACHABLE = 6, |
- |
- // A programming error in our network stack. |
- NET_ERROR_UNEXPECTED = 7, |
- |
- // Other kind of error. |
- NET_ERROR_OTHER = 20, |
- |
- NUM_OF_NET_ERROR_TYPES |
- } type; |
- switch (result) { |
- case OK: |
- type = NET_ERROR_OK; |
- break; |
- case ERR_ACCESS_DENIED: |
- case ERR_NETWORK_ACCESS_DENIED: |
- type = NET_ERROR_ACCESS_DENIED; |
- break; |
- case ERR_TIMED_OUT: |
- type = NET_ERROR_TIMED_OUT; |
- break; |
- case ERR_CONNECTION_ABORTED: |
- case ERR_CONNECTION_RESET: |
- case ERR_CONNECTION_CLOSED: |
- type = NET_ERROR_CONNECTION_BROKEN; |
- break; |
- case ERR_CONNECTION_FAILED: |
- case ERR_CONNECTION_REFUSED: |
- type = NET_ERROR_CONNECTION_REFUSED; |
- break; |
- case ERR_INTERNET_DISCONNECTED: |
- type = NET_ERROR_INTERNET_DISCONNECTED; |
- break; |
- case ERR_ADDRESS_INVALID: |
- case ERR_ADDRESS_UNREACHABLE: |
- type = NET_ERROR_ADDRESS_UNREACHABLE; |
- break; |
- case ERR_UNEXPECTED: |
- type = NET_ERROR_UNEXPECTED; |
- break; |
- default: |
- type = NET_ERROR_OTHER; |
- break; |
- }; |
- static bool had_error_type[NUM_OF_NET_ERROR_TYPES]; |
- |
- DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES); |
- if (!had_error_type[type]) { |
- had_error_type[type] = true; |
- UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened", |
- type, NUM_OF_NET_ERROR_TYPES); |
- } |
- UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount", |
- type, NUM_OF_NET_ERROR_TYPES); |
-} |
- |
-} // namespace net |