// Copyright (c) 2009 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 "build/build_config.h"
#include "base/ref_counted.h"
#include "net/base/io_buffer.h"
#include "net/base/mock_host_resolver.h"
#include "net/base/net_util.h"
#include "net/base/sys_addrinfo.h"
#include "net/base/test_completion_callback.h"
#include "net/ftp/ftp_network_session.h"
#include "net/ftp/ftp_request_info.h"
#include "net/socket/socket_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
namespace {
// Size we use for IOBuffers used to receive data from the test data socket.
const int kBufferSize = 128;
} // namespace
namespace net {
class FtpSocketDataProvider : public DynamicSocketDataProvider {
public:
enum State {
NONE,
PRE_USER,
PRE_PASSWD,
PRE_SYST,
PRE_PWD,
PRE_TYPE,
PRE_PASV,
PRE_SIZE,
PRE_MDTM,
PRE_MLSD,
PRE_LIST,
PRE_RETR,
PRE_PASV2,
PRE_CWD,
PRE_QUIT,
QUIT
};
FtpSocketDataProvider()
: failure_injection_state_(NONE),
multiline_welcome_(false) {
Init();
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_USER:
return Verify("USER anonymous\r\n", data, PRE_PASSWD,
"331 Password needed\r\n");
case PRE_PASSWD:
{
const char* response_one = "230 Welcome\r\n";
const char* response_multi = "230- One\r\n230- Two\r\n230 Three\r\n";
return Verify("PASS chrome@example.com\r\n", data, PRE_SYST,
multiline_welcome_ ? response_multi : response_one);
}
case PRE_SYST:
return Verify("SYST\r\n", data, PRE_PWD, "215 UNIX\r\n");
case PRE_PWD:
return Verify("PWD\r\n", data, PRE_TYPE,
"257 \"/\" is your current location\r\n");
case PRE_TYPE:
return Verify("TYPE I\r\n", data, PRE_PASV,
"200 TYPE is now 8-bit binary\r\n");
case PRE_PASV:
return Verify("PASV\r\n", data, PRE_SIZE,
"227 Entering Passive Mode 127,0,0,1,123,456\r\n");
case PRE_PASV2:
// Parser should also accept format without parentheses.
return Verify("PASV\r\n", data, PRE_CWD,
"227 Entering Passive Mode 127,0,0,1,123,456\r\n");
case PRE_QUIT:
return Verify("QUIT\r\n", data, QUIT, "221 Goodbye.\r\n");
default:
NOTREACHED() << "State not handled " << state();
return MockWriteResult(true, ERR_UNEXPECTED);
}
}
void InjectFailure(State state, State next_state, const char* response) {
DCHECK_EQ(NONE, failure_injection_state_);
DCHECK_NE(NONE, state);
DCHECK_NE(NONE, next_state);
DCHECK_NE(state, next_state);
failure_injection_state_ = state;
failure_injection_next_state_ = next_state;
fault_response_ = response;
}
State state() const {
return state_;
}
virtual void Reset() {
DynamicSocketDataProvider::Reset();
Init();
}
void set_multiline_welcome(bool multiline) {
multiline_welcome_ = multiline;
}
protected:
void Init() {
state_ = PRE_USER;
SimulateRead("220 host TestFTPd\r\n");
}
// If protocol fault injection has been requested, adjusts state and mocked
// read and returns true.
bool InjectFault() {
if (state_ != failure_injection_state_)
return false;
SimulateRead(fault_response_);
state_ = failure_injection_next_state_;
return true;
}
MockWriteResult Verify(const std::string& expected,
const std::string& data,
State next_state,
const char* next_read) {
EXPECT_EQ(expected, data);
if (expected == data) {
state_ = next_state;
SimulateRead(next_read);
return MockWriteResult(true, data.length());
}
return MockWriteResult(true, ERR_UNEXPECTED);
}
private:
State state_;
State failure_injection_state_;
State failure_injection_next_state_;
const char* fault_response_;
// If true, we will send multiple 230 lines as response after PASS.
bool multiline_welcome_;
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProvider);
};
class FtpSocketDataProviderDirectoryListing : public FtpSocketDataProvider {
public:
FtpSocketDataProviderDirectoryListing() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_SIZE:
return Verify("SIZE /\r\n", data, PRE_MDTM,
"550 I can only retrieve regular files\r\n");
case PRE_MDTM:
return Verify("MDTM /\r\n", data, PRE_RETR,
"213 20070221112533\r\n");
case PRE_RETR:
return Verify("RETR /\r\n", data, PRE_PASV2,
"550 Can't download directory\r\n");
case PRE_CWD:
return Verify("CWD /\r\n", data, PRE_MLSD, "200 OK\r\n");
case PRE_MLSD:
return Verify("MLSD\r\n", data, PRE_QUIT,
"150 Accepted data connection\r\n"
"226 MLSD complete\r\n");
case PRE_LIST:
return Verify("LIST\r\n", data, PRE_QUIT, "200 OK\r\n");
default:
return FtpSocketDataProvider::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderDirectoryListing);
};
class FtpSocketDataProviderVMSDirectoryListing : public FtpSocketDataProvider {
public:
FtpSocketDataProviderVMSDirectoryListing() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_SYST:
return Verify("SYST\r\n", data, PRE_PWD, "215 VMS\r\n");
case PRE_PWD:
return Verify("PWD\r\n", data, PRE_TYPE,
"257 \"ANONYMOUS_ROOT:[000000]\"\r\n");
case PRE_SIZE:
return Verify("SIZE ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_MDTM,
"550 I can only retrieve regular files\r\n");
case PRE_MDTM:
return Verify("MDTM ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_RETR,
"213 20070221112533\r\n");
case PRE_RETR:
return Verify("RETR ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_PASV2,
"550 Can't download directory\r\n");
case PRE_CWD:
return Verify("CWD ANONYMOUS_ROOT:[dir]\r\n", data, PRE_MLSD,
"200 OK\r\n");
case PRE_MLSD:
return Verify("MLSD\r\n", data, PRE_LIST, "500 Invalid command\r\n");
case PRE_LIST:
return Verify("LIST *.*;0\r\n", data, PRE_QUIT, "200 OK\r\n");
default:
return FtpSocketDataProvider::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderVMSDirectoryListing);
};
class FtpSocketDataProviderVMSDirectoryListingRootDirectory
: public FtpSocketDataProvider {
public:
FtpSocketDataProviderVMSDirectoryListingRootDirectory() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_SYST:
return Verify("SYST\r\n", data, PRE_PWD, "215 VMS\r\n");
case PRE_PWD:
return Verify("PWD\r\n", data, PRE_TYPE,
"257 \"ANONYMOUS_ROOT:[000000]\"\r\n");
case PRE_SIZE:
return Verify("SIZE ANONYMOUS_ROOT\r\n", data, PRE_MDTM,
"550 I can only retrieve regular files\r\n");
case PRE_MDTM:
return Verify("MDTM ANONYMOUS_ROOT\r\n", data, PRE_RETR,
"213 20070221112533\r\n");
case PRE_RETR:
return Verify("RETR ANONYMOUS_ROOT\r\n", data, PRE_PASV2,
"550 Can't download directory\r\n");
case PRE_CWD:
return Verify("CWD ANONYMOUS_ROOT:[000000]\r\n", data, PRE_MLSD,
"200 OK\r\n");
case PRE_MLSD:
return Verify("MLSD\r\n", data, PRE_LIST, "500 Invalid command\r\n");
case PRE_LIST:
return Verify("LIST *.*;0\r\n", data, PRE_QUIT, "200 OK\r\n");
default:
return FtpSocketDataProvider::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(
FtpSocketDataProviderVMSDirectoryListingRootDirectory);
};
class FtpSocketDataProviderFileDownload : public FtpSocketDataProvider {
public:
FtpSocketDataProviderFileDownload() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_SIZE:
return Verify("SIZE /file\r\n", data, PRE_MDTM,
"213 18\r\n");
case PRE_MDTM:
return Verify("MDTM /file\r\n", data, PRE_RETR,
"213 20070221112533\r\n");
case PRE_RETR:
return Verify("RETR /file\r\n", data, PRE_QUIT, "200 OK\r\n");
default:
return FtpSocketDataProvider::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownload);
};
class FtpSocketDataProviderVMSFileDownload : public FtpSocketDataProvider {
public:
FtpSocketDataProviderVMSFileDownload() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_SYST:
return Verify("SYST\r\n", data, PRE_PWD, "215 VMS\r\n");
case PRE_PWD:
return Verify("PWD\r\n", data, PRE_TYPE,
"257 \"ANONYMOUS_ROOT:[000000]\"\r\n");
case PRE_SIZE:
return Verify("SIZE ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_MDTM,
"213 18\r\n");
case PRE_MDTM:
return Verify("MDTM ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_RETR,
"213 20070221112533\r\n");
case PRE_RETR:
return Verify("RETR ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_QUIT,
"200 OK\r\n");
default:
return FtpSocketDataProvider::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderVMSFileDownload);
};
class FtpSocketDataProviderEscaping : public FtpSocketDataProvider {
public:
FtpSocketDataProviderEscaping() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_SIZE:
return Verify("SIZE / !\"#$%y\200\201\r\n", data, PRE_MDTM,
"213 18\r\n");
case PRE_MDTM:
return Verify("MDTM / !\"#$%y\200\201\r\n", data, PRE_RETR,
"213 20070221112533\r\n");
case PRE_RETR:
return Verify("RETR / !\"#$%y\200\201\r\n", data, PRE_QUIT,
"200 OK\r\n");
default:
return FtpSocketDataProvider::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEscaping);
};
class FtpSocketDataProviderFileDownloadAcceptedDataConnection
: public FtpSocketDataProviderFileDownload {
public:
FtpSocketDataProviderFileDownloadAcceptedDataConnection() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_RETR:
return Verify("RETR /file\r\n", data, PRE_QUIT,
"150 Accepted Data Connection\r\n");
default:
return FtpSocketDataProviderFileDownload::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(
FtpSocketDataProviderFileDownloadAcceptedDataConnection);
};
class FtpSocketDataProviderFileDownloadTransferStarting
: public FtpSocketDataProviderFileDownload {
public:
FtpSocketDataProviderFileDownloadTransferStarting() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_RETR:
return Verify("RETR /file\r\n", data, PRE_QUIT,
"125-Data connection already open.\r\n"
"125 Transfer starting.\r\n"
"226 Transfer complete.\r\n");
default:
return FtpSocketDataProviderFileDownload::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownloadTransferStarting);
};
class FtpSocketDataProviderDirectoryListingTransferStarting
: public FtpSocketDataProviderDirectoryListing {
public:
FtpSocketDataProviderDirectoryListingTransferStarting() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_LIST:
return Verify("LIST\r\n", data, PRE_QUIT,
"125-Data connection already open.\r\n"
"125 Transfer starting.\r\n"
"226 Transfer complete.\r\n");
default:
return FtpSocketDataProviderDirectoryListing::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(
FtpSocketDataProviderDirectoryListingTransferStarting);
};
class FtpSocketDataProviderFileDownloadInvalidResponse
: public FtpSocketDataProviderFileDownload {
public:
FtpSocketDataProviderFileDownloadInvalidResponse() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_SIZE:
return Verify("SIZE /file\r\n", data, PRE_QUIT,
"500 Evil Response\r\n"
"500 More Evil\r\n");
default:
return FtpSocketDataProviderFileDownload::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownloadInvalidResponse);
};
class FtpSocketDataProviderFileDownloadRetrFail
: public FtpSocketDataProviderFileDownload {
public:
FtpSocketDataProviderFileDownloadRetrFail() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_CWD:
return Verify("CWD /file\r\n", data, PRE_QUIT,
"550 file is a directory\r\n");
default:
return FtpSocketDataProviderFileDownload::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownloadRetrFail);
};
class FtpSocketDataProviderEvilPasv : public FtpSocketDataProviderFileDownload {
public:
explicit FtpSocketDataProviderEvilPasv(const char* pasv_response,
State expected_state)
: pasv_response_(pasv_response),
expected_state_(expected_state) {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_PASV:
return Verify("PASV\r\n", data, expected_state_, pasv_response_);
default:
return FtpSocketDataProviderFileDownload::OnWrite(data);
}
}
private:
const char* pasv_response_;
const State expected_state_;
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEvilPasv);
};
class FtpSocketDataProviderEvilLogin : public FtpSocketDataProviderFileDownload {
public:
FtpSocketDataProviderEvilLogin(const char* expected_user,
const char* expected_password)
: expected_user_(expected_user),
expected_password_(expected_password) {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_USER:
return Verify(std::string("USER ") + expected_user_ + "\r\n", data,
PRE_PASSWD, "331 Password needed\r\n");
case PRE_PASSWD:
return Verify(std::string("PASS ") + expected_password_ + "\r\n", data,
PRE_SYST, "230 Welcome\r\n");
default:
return FtpSocketDataProviderFileDownload::OnWrite(data);
}
}
private:
const char* expected_user_;
const char* expected_password_;
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEvilLogin);
};
class FtpSocketDataProviderCloseConnection : public FtpSocketDataProvider {
public:
FtpSocketDataProviderCloseConnection() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, data.length());
switch (state()) {
case PRE_USER:
return Verify("USER anonymous\r\n", data,
PRE_QUIT, "");
default:
return FtpSocketDataProvider::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderCloseConnection);
};
class FtpNetworkTransactionTest : public PlatformTest {
public:
FtpNetworkTransactionTest()
: host_resolver_(new MockHostResolver),
session_(new FtpNetworkSession(host_resolver_)),
transaction_(session_.get(), &mock_socket_factory_) {
}
protected:
FtpRequestInfo GetRequestInfo(const std::string& url) {
FtpRequestInfo info;
info.url = GURL(url);
return info;
}
void ExecuteTransaction(FtpSocketDataProvider* ctrl_socket,
const char* request,
int expected_result) {
std::string mock_data("mock-data");
MockRead data_reads[] = {
MockRead(mock_data.c_str()),
};
// For compatibility with FileZilla, the transaction code will use two data
// sockets for directory requests. For more info see http://crbug.com/25316.
StaticSocketDataProvider data1(data_reads, NULL);
StaticSocketDataProvider data2(data_reads, NULL);
mock_socket_factory_.AddSocketDataProvider(ctrl_socket);
mock_socket_factory_.AddSocketDataProvider(&data1);
mock_socket_factory_.AddSocketDataProvider(&data2);
FtpRequestInfo request_info = GetRequestInfo(request);
EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
ASSERT_EQ(ERR_IO_PENDING,
transaction_.Start(&request_info, &callback_, NULL));
EXPECT_NE(LOAD_STATE_IDLE, transaction_.GetLoadState());
EXPECT_EQ(expected_result, callback_.WaitForResult());
EXPECT_EQ(FtpSocketDataProvider::QUIT, ctrl_socket->state());
if (expected_result == OK) {
scoped_refptr<IOBuffer> io_buffer(new IOBuffer(kBufferSize));
memset(io_buffer->data(), 0, kBufferSize);
ASSERT_EQ(ERR_IO_PENDING,
transaction_.Read(io_buffer.get(), kBufferSize, &callback_));
EXPECT_EQ(static_cast<int>(mock_data.length()),
callback_.WaitForResult());
EXPECT_EQ(mock_data, std::string(io_buffer->data(), mock_data.length()));
}
EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
}
void TransactionFailHelper(FtpSocketDataProvider* ctrl_socket,
const char* request,
FtpSocketDataProvider::State state,
FtpSocketDataProvider::State next_state,
const char* response,
int expected_result) {
ctrl_socket->InjectFailure(state, next_state, response);
ExecuteTransaction(ctrl_socket, request, expected_result);
}
scoped_refptr<MockHostResolver> host_resolver_;
scoped_refptr<FtpNetworkSession> session_;
MockClientSocketFactory mock_socket_factory_;
FtpNetworkTransaction transaction_;
TestCompletionCallback callback_;
};
TEST_F(FtpNetworkTransactionTest, FailedLookup) {
FtpRequestInfo request_info = GetRequestInfo("ftp://badhost");
host_resolver_->rules()->AddSimulatedFailure("badhost");
EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
ASSERT_EQ(ERR_IO_PENDING,
transaction_.Start(&request_info, &callback_, NULL));
EXPECT_EQ(ERR_NAME_NOT_RESOLVED, callback_.WaitForResult());
EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransaction) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionMultilineWelcome) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
ctrl_socket.set_multiline_welcome(true);
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionShortReads2) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
ctrl_socket.set_short_read_limit(2);
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionShortReads5) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
ctrl_socket.set_short_read_limit(5);
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionMultilineWelcomeShort) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
// The client will not consume all three 230 lines. That's good, we want to
// test that scenario.
ctrl_socket.allow_unconsumed_reads(true);
ctrl_socket.set_multiline_welcome(true);
ctrl_socket.set_short_read_limit(5);
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionVMS) {
FtpSocketDataProviderVMSDirectoryListing ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host/dir", OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionVMSRootDirectory) {
FtpSocketDataProviderVMSDirectoryListingRootDirectory ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionTransferStarting) {
FtpSocketDataProviderDirectoryListingTransferStarting ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransaction) {
FtpSocketDataProviderFileDownload ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionMultilineWelcome) {
FtpSocketDataProviderFileDownload ctrl_socket;
ctrl_socket.set_multiline_welcome(true);
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionShortReads2) {
FtpSocketDataProviderFileDownload ctrl_socket;
ctrl_socket.set_short_read_limit(2);
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionShortReads5) {
FtpSocketDataProviderFileDownload ctrl_socket;
ctrl_socket.set_short_read_limit(5);
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionVMS) {
FtpSocketDataProviderVMSFileDownload ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionAcceptedDataConnection) {
FtpSocketDataProviderFileDownloadAcceptedDataConnection ctrl_socket;
std::string mock_data("mock-data");
MockRead data_reads[] = {
MockRead(mock_data.c_str()),
};
StaticSocketDataProvider data_socket1(data_reads, NULL);
mock_socket_factory_.AddSocketDataProvider(&ctrl_socket);
mock_socket_factory_.AddSocketDataProvider(&data_socket1);
FtpRequestInfo request_info = GetRequestInfo("ftp://host/file");
// Start the transaction.
ASSERT_EQ(ERR_IO_PENDING,
transaction_.Start(&request_info, &callback_, NULL));
EXPECT_EQ(OK, callback_.WaitForResult());
// The transaction fires the callback when we can start reading data.
EXPECT_EQ(FtpSocketDataProvider::PRE_QUIT, ctrl_socket.state());
EXPECT_EQ(LOAD_STATE_SENDING_REQUEST, transaction_.GetLoadState());
scoped_refptr<IOBuffer> io_buffer(new IOBuffer(kBufferSize));
memset(io_buffer->data(), 0, kBufferSize);
ASSERT_EQ(ERR_IO_PENDING,
transaction_.Read(io_buffer.get(), kBufferSize, &callback_));
EXPECT_EQ(LOAD_STATE_READING_RESPONSE, transaction_.GetLoadState());
EXPECT_EQ(static_cast<int>(mock_data.length()),
callback_.WaitForResult());
EXPECT_EQ(LOAD_STATE_READING_RESPONSE, transaction_.GetLoadState());
EXPECT_EQ(mock_data, std::string(io_buffer->data(), mock_data.length()));
// FTP server should disconnect the data socket. It is also a signal for the
// FtpNetworkTransaction that the data transfer is finished.
ClientSocket* data_socket = mock_socket_factory_.GetMockTCPClientSocket(1);
ASSERT_TRUE(data_socket);
data_socket->Disconnect();
// We should issue Reads until one returns EOF...
ASSERT_EQ(ERR_IO_PENDING,
transaction_.Read(io_buffer.get(), kBufferSize, &callback_));
// Make sure the transaction finishes cleanly.
EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
EXPECT_EQ(OK, callback_.WaitForResult());
EXPECT_EQ(FtpSocketDataProvider::QUIT, ctrl_socket.state());
EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionTransferStarting) {
FtpSocketDataProviderFileDownloadTransferStarting ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionInvalidResponse) {
FtpSocketDataProviderFileDownloadInvalidResponse ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafePort1) {
FtpSocketDataProviderEvilPasv ctrl_socket("227 Portscan (127,0,0,1,0,22)\r\n",
FtpSocketDataProvider::PRE_QUIT);
ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafePort2) {
// Still unsafe. 1 * 256 + 2 = 258, which is < 1024.
FtpSocketDataProviderEvilPasv ctrl_socket("227 Portscan (127,0,0,1,1,2)\r\n",
FtpSocketDataProvider::PRE_QUIT);
ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafePort3) {
// Still unsafe. 3 * 256 + 4 = 772, which is < 1024.
FtpSocketDataProviderEvilPasv ctrl_socket("227 Portscan (127,0,0,1,3,4)\r\n",
FtpSocketDataProvider::PRE_QUIT);
ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafePort4) {
// Unsafe. 8 * 256 + 1 = 2049, which is used by nfs.
FtpSocketDataProviderEvilPasv ctrl_socket("227 Portscan (127,0,0,1,8,1)\r\n",
FtpSocketDataProvider::PRE_QUIT);
ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafeHost) {
FtpSocketDataProviderEvilPasv ctrl_socket(
"227 Portscan (10,1,2,3,4,123,456)\r\n", FtpSocketDataProvider::PRE_SIZE);
std::string mock_data("mock-data");
MockRead data_reads[] = {
MockRead(mock_data.c_str()),
};
StaticSocketDataProvider data_socket1(data_reads, NULL);
mock_socket_factory_.AddSocketDataProvider(&ctrl_socket);
mock_socket_factory_.AddSocketDataProvider(&data_socket1);
FtpRequestInfo request_info = GetRequestInfo("ftp://host/file");
// Start the transaction.
ASSERT_EQ(ERR_IO_PENDING,
transaction_.Start(&request_info, &callback_, NULL));
EXPECT_EQ(OK, callback_.WaitForResult());
// The transaction fires the callback when we can start reading data. That
// means that the data socket should be open.
MockTCPClientSocket* data_socket =
mock_socket_factory_.GetMockTCPClientSocket(1);
ASSERT_TRUE(data_socket);
ASSERT_TRUE(data_socket->IsConnected());
// Even if the PASV response specified some other address, we connect
// to the address we used for control connection.
EXPECT_EQ("127.0.0.1", NetAddressToString(data_socket->addresses().head()));
// Make sure we have only one host entry in the AddressList.
EXPECT_FALSE(data_socket->addresses().head()->ai_next);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilLoginBadUsername) {
FtpSocketDataProviderEvilLogin ctrl_socket("hello%0Aworld", "test");
ExecuteTransaction(&ctrl_socket, "ftp://hello%0Aworld:test@host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilLoginBadPassword) {
FtpSocketDataProviderEvilLogin ctrl_socket("test", "hello%0Dworld");
ExecuteTransaction(&ctrl_socket, "ftp://test:hello%0Dworld@host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionSpaceInLogin) {
FtpSocketDataProviderEvilLogin ctrl_socket("hello world", "test");
ExecuteTransaction(&ctrl_socket, "ftp://hello%20world:test@host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionSpaceInPassword) {
FtpSocketDataProviderEvilLogin ctrl_socket("test", "hello world");
ExecuteTransaction(&ctrl_socket, "ftp://test:hello%20world@host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, EvilRestartUser) {
FtpSocketDataProvider ctrl_socket1;
ctrl_socket1.InjectFailure(FtpSocketDataProvider::PRE_PASSWD,
FtpSocketDataProvider::PRE_QUIT,
"530 Login authentication failed\r\n");
mock_socket_factory_.AddSocketDataProvider(&ctrl_socket1);
FtpRequestInfo request_info = GetRequestInfo("ftp://host/file");
ASSERT_EQ(ERR_IO_PENDING,
transaction_.Start(&request_info, &callback_, NULL));
EXPECT_EQ(ERR_FAILED, callback_.WaitForResult());
MockRead ctrl_reads[] = {
MockRead("220 host TestFTPd\r\n"),
MockRead("221 Goodbye!\r\n"),
MockRead(false, OK),
};
MockWrite ctrl_writes[] = {
MockWrite("QUIT\r\n"),
};
StaticSocketDataProvider ctrl_socket2(ctrl_reads, ctrl_writes);
mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2);
ASSERT_EQ(ERR_IO_PENDING, transaction_.RestartWithAuth(L"foo\nownz0red",
L"innocent",
&callback_));
EXPECT_EQ(ERR_MALFORMED_IDENTITY, callback_.WaitForResult());
}
TEST_F(FtpNetworkTransactionTest, EvilRestartPassword) {
FtpSocketDataProvider ctrl_socket1;
ctrl_socket1.InjectFailure(FtpSocketDataProvider::PRE_PASSWD,
FtpSocketDataProvider::PRE_QUIT,
"530 Login authentication failed\r\n");
mock_socket_factory_.AddSocketDataProvider(&ctrl_socket1);
FtpRequestInfo request_info = GetRequestInfo("ftp://host/file");
ASSERT_EQ(ERR_IO_PENDING,
transaction_.Start(&request_info, &callback_, NULL));
EXPECT_EQ(ERR_FAILED, callback_.WaitForResult());
MockRead ctrl_reads[] = {
MockRead("220 host TestFTPd\r\n"),
MockRead("331 User okay, send password\r\n"),
MockRead("221 Goodbye!\r\n"),
MockRead(false, OK),
};
MockWrite ctrl_writes[] = {
MockWrite("USER innocent\r\n"),
MockWrite("QUIT\r\n"),
};
StaticSocketDataProvider ctrl_socket2(ctrl_reads, ctrl_writes);
mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2);
ASSERT_EQ(ERR_IO_PENDING, transaction_.RestartWithAuth(L"innocent",
L"foo\nownz0red",
&callback_));
EXPECT_EQ(ERR_MALFORMED_IDENTITY, callback_.WaitForResult());
}
TEST_F(FtpNetworkTransactionTest, Escaping) {
FtpSocketDataProviderEscaping ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host/%20%21%22%23%24%25%79%80%81",
OK);
}
// Regression test for http://crbug.com/25023.
TEST_F(FtpNetworkTransactionTest, CloseConnection) {
FtpSocketDataProviderCloseConnection ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host", ERR_EMPTY_RESPONSE);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailUser) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_USER,
FtpSocketDataProvider::PRE_QUIT,
"500 no such user\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPass) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_PASSWD,
FtpSocketDataProvider::PRE_QUIT,
"530 Login authentication failed\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailSyst) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_SYST,
FtpSocketDataProvider::PRE_PWD,
"500 failed syst\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPwd) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_PWD,
FtpSocketDataProvider::PRE_QUIT,
"500 failed pwd\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailType) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_TYPE,
FtpSocketDataProvider::PRE_QUIT,
"500 failed type\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPasv) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_PASV,
FtpSocketDataProvider::PRE_QUIT,
"500 failed pasv\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionMalformedMdtm) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_MDTM,
FtpSocketDataProvider::PRE_RETR,
"213 foobar\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailMdtm) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_MDTM,
FtpSocketDataProvider::PRE_RETR,
"500 failed mdtm\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPasv2) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_PASV2,
FtpSocketDataProvider::PRE_QUIT,
"500 failed pasv2\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailCwd) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_CWD,
FtpSocketDataProvider::PRE_QUIT,
"500 failed cwd\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFileNotFound) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_CWD,
FtpSocketDataProvider::PRE_QUIT,
"550 cannot open file\r\n",
ERR_FILE_NOT_FOUND);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailMlsd) {
FtpSocketDataProviderDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpSocketDataProvider::PRE_MLSD,
FtpSocketDataProvider::PRE_LIST,
"500 Unrecognized command\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailList) {
FtpSocketDataProviderVMSDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/dir",
FtpSocketDataProvider::PRE_LIST,
FtpSocketDataProvider::PRE_QUIT,
"500 failed list\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailUser) {
FtpSocketDataProviderFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpSocketDataProvider::PRE_USER,
FtpSocketDataProvider::PRE_QUIT,
"500 no such user\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailPass) {
FtpSocketDataProviderFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpSocketDataProvider::PRE_PASSWD,
FtpSocketDataProvider::PRE_QUIT,
"530 Login authentication failed\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailSyst) {
FtpSocketDataProviderFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpSocketDataProvider::PRE_SYST,
FtpSocketDataProvider::PRE_PWD,
"500 failed syst\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailPwd) {
FtpSocketDataProviderFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpSocketDataProvider::PRE_PWD,
FtpSocketDataProvider::PRE_QUIT,
"500 failed pwd\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailType) {
FtpSocketDataProviderFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpSocketDataProvider::PRE_TYPE,
FtpSocketDataProvider::PRE_QUIT,
"500 failed type\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailPasv) {
FtpSocketDataProviderFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpSocketDataProvider::PRE_PASV,
FtpSocketDataProvider::PRE_QUIT,
"500 failed pasv\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailMdtm) {
FtpSocketDataProviderFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpSocketDataProvider::PRE_MDTM,
FtpSocketDataProvider::PRE_RETR,
"500 failed mdtm\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailRetr) {
FtpSocketDataProviderFileDownloadRetrFail ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpSocketDataProvider::PRE_RETR,
FtpSocketDataProvider::PRE_QUIT,
"500 failed retr\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFileNotFound) {
FtpSocketDataProviderFileDownloadRetrFail ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpSocketDataProvider::PRE_RETR,
FtpSocketDataProvider::PRE_PASV2,
"550 cannot open file\r\n",
ERR_FILE_NOT_FOUND);
}
} // namespace net