// 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 "build/build_config.h" #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "net/base/host_port_pair.h" #include "net/base/io_buffer.h" #include "net/base/test_completion_callback.h" #include "net/dns/mock_host_resolver.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 SocketDataProvider { public: enum State { NONE, PRE_USER, PRE_PASSWD, PRE_SYST, PRE_PWD, PRE_TYPE, PRE_SIZE, PRE_LIST_EPSV, PRE_LIST_PASV, PRE_LIST, PRE_RETR, PRE_RETR_EPSV, PRE_RETR_PASV, PRE_CWD, PRE_QUIT, PRE_NOPASV, QUIT }; FtpSocketDataProvider() : short_read_limit_(0), allow_unconsumed_reads_(false), failure_injection_state_(NONE), multiline_welcome_(false), use_epsv_(true), data_type_('I') { Init(); } // SocketDataProvider implementation. MockRead OnRead() override { if (reads_.empty()) return MockRead(SYNCHRONOUS, ERR_UNEXPECTED); MockRead result = reads_.front(); if (short_read_limit_ == 0 || result.data_len <= short_read_limit_) { reads_.pop_front(); } else { result.data_len = short_read_limit_; reads_.front().data += result.data_len; reads_.front().data_len -= result.data_len; } return result; } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_USER: return Verify("USER anonymous\r\n", data, PRE_PASSWD, "331 Password needed\r\n"); case PRE_PASSWD: { static const char response_one[] = "230 Welcome\r\n"; static 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(std::string("TYPE ") + data_type_ + "\r\n", data, PRE_SIZE, "200 TYPE set successfully\r\n"); case PRE_LIST_EPSV: return Verify("EPSV\r\n", data, PRE_LIST, "227 Entering Extended Passive Mode (|||31744|)\r\n"); case PRE_LIST_PASV: return Verify("PASV\r\n", data, PRE_LIST, "227 Entering Passive Mode 127,0,0,1,123,456\r\n"); case PRE_RETR_EPSV: return Verify("EPSV\r\n", data, PRE_RETR, "227 Entering Extended Passive Mode (|||31744|)\r\n"); case PRE_RETR_PASV: return Verify("PASV\r\n", data, PRE_RETR, "227 Entering Passive Mode 127,0,0,1,123,456\r\n"); case PRE_NOPASV: // Use unallocated 599 FTP error code to make sure it falls into the // generic ERR_FTP_FAILED bucket. return Verify("PASV\r\n", data, PRE_QUIT, "599 fail\r\n"); case PRE_QUIT: return Verify("QUIT\r\n", data, QUIT, "221 Goodbye.\r\n"); default: NOTREACHED() << "State not handled " << state(); return MockWriteResult(ASYNC, 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_; } void Reset() override { reads_.clear(); Init(); } bool AllReadDataConsumed() const override { return state_ == QUIT; } bool AllWriteDataConsumed() const override { return state_ == QUIT; } void set_multiline_welcome(bool multiline) { multiline_welcome_ = multiline; } bool use_epsv() const { return use_epsv_; } void set_use_epsv(bool use_epsv) { use_epsv_ = use_epsv; } void set_data_type(char data_type) { data_type_ = data_type; } int short_read_limit() const { return short_read_limit_; } void set_short_read_limit(int limit) { short_read_limit_ = limit; } void set_allow_unconsumed_reads(bool allow) { allow_unconsumed_reads_ = allow; } 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, const size_t next_read_length) { EXPECT_EQ(expected, data); if (expected == data) { state_ = next_state; SimulateRead(next_read, next_read_length); return MockWriteResult(ASYNC, data.length()); } return MockWriteResult(ASYNC, ERR_UNEXPECTED); } MockWriteResult Verify(const std::string& expected, const std::string& data, State next_state, const char* next_read) { return Verify(expected, data, next_state, next_read, std::strlen(next_read)); } // The next time there is a read from this socket, it will return |data|. // Before calling SimulateRead next time, the previous data must be consumed. void SimulateRead(const char* data, size_t length) { if (!allow_unconsumed_reads_) { EXPECT_TRUE(reads_.empty()) << "Unconsumed read: " << reads_.front().data; } reads_.push_back(MockRead(ASYNC, data, length)); } void SimulateRead(const char* data) { SimulateRead(data, std::strlen(data)); } private: // List of reads to be consumed. std::deque reads_; // Max number of bytes we will read at a time. 0 means no limit. int short_read_limit_; // If true, we'll not require the client to consume all data before we // mock the next read. bool allow_unconsumed_reads_; 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_; // If true, we will use EPSV command. bool use_epsv_; // Data type to be used for TYPE command. char data_type_; DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProvider); }; class FtpSocketDataProviderDirectoryListing : public FtpSocketDataProvider { public: FtpSocketDataProviderDirectoryListing() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_SIZE: return Verify("SIZE /\r\n", data, PRE_CWD, "550 I can only retrieve regular files\r\n"); case PRE_CWD: return Verify("CWD /\r\n", data, use_epsv() ? PRE_LIST_EPSV : PRE_LIST_PASV, "200 OK\r\n"); case PRE_LIST: return Verify("LIST -l\r\n", data, PRE_QUIT, "200 OK\r\n"); default: return FtpSocketDataProvider::OnWrite(data); } } private: DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderDirectoryListing); }; class FtpSocketDataProviderDirectoryListingWithPasvFallback : public FtpSocketDataProviderDirectoryListing { public: FtpSocketDataProviderDirectoryListingWithPasvFallback() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_LIST_EPSV: return Verify("EPSV\r\n", data, PRE_LIST_PASV, "500 no EPSV for you\r\n"); case PRE_SIZE: return Verify("SIZE /\r\n", data, PRE_CWD, "550 I can only retrieve regular files\r\n"); default: return FtpSocketDataProviderDirectoryListing::OnWrite(data); } } private: DISALLOW_COPY_AND_ASSIGN( FtpSocketDataProviderDirectoryListingWithPasvFallback); }; class FtpSocketDataProviderDirectoryListingZeroSize : public FtpSocketDataProviderDirectoryListing { public: FtpSocketDataProviderDirectoryListingZeroSize() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_SIZE: return Verify("SIZE /\r\n", data, PRE_CWD, "213 0\r\n"); default: return FtpSocketDataProviderDirectoryListing::OnWrite(data); } } private: DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderDirectoryListingZeroSize); }; class FtpSocketDataProviderVMSDirectoryListing : public FtpSocketDataProvider { public: FtpSocketDataProviderVMSDirectoryListing() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, 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_LIST_EPSV: return Verify("EPSV\r\n", data, PRE_LIST_PASV, "500 Invalid command\r\n"); case PRE_SIZE: return Verify("SIZE ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_CWD, "550 I can only retrieve regular files\r\n"); case PRE_CWD: return Verify("CWD ANONYMOUS_ROOT:[dir]\r\n", data, use_epsv() ? PRE_LIST_EPSV : PRE_LIST_PASV, "200 OK\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() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, 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_LIST_EPSV: return Verify("EPSV\r\n", data, PRE_LIST_PASV, "500 EPSV command unknown\r\n"); case PRE_SIZE: return Verify("SIZE ANONYMOUS_ROOT\r\n", data, PRE_CWD, "550 I can only retrieve regular files\r\n"); case PRE_CWD: return Verify("CWD ANONYMOUS_ROOT:[000000]\r\n", data, use_epsv() ? PRE_LIST_EPSV : PRE_LIST_PASV, "200 OK\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 FtpSocketDataProviderFileDownloadWithFileTypecode : public FtpSocketDataProvider { public: FtpSocketDataProviderFileDownloadWithFileTypecode() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_SIZE: return Verify("SIZE /file\r\n", data, use_epsv() ? PRE_RETR_EPSV : PRE_RETR_PASV, "213 18\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(FtpSocketDataProviderFileDownloadWithFileTypecode); }; class FtpSocketDataProviderFileDownload : public FtpSocketDataProvider { public: FtpSocketDataProviderFileDownload() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_SIZE: return Verify("SIZE /file\r\n", data, PRE_CWD, "213 18\r\n"); case PRE_CWD: return Verify("CWD /file\r\n", data, use_epsv() ? PRE_RETR_EPSV : PRE_RETR_PASV, "550 Not a directory\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 FtpSocketDataProviderPathSeparatorsNotUnescaped : public FtpSocketDataProvider { public: FtpSocketDataProviderPathSeparatorsNotUnescaped() {} MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_SIZE: return Verify("SIZE /foo%2f..%2fbar%5c\r\n", data, PRE_CWD, "213 18\r\n"); case PRE_CWD: return Verify("CWD /foo%2f..%2fbar%5c\r\n", data, use_epsv() ? PRE_RETR_EPSV : PRE_RETR_PASV, "550 Not a directory\r\n"); case PRE_RETR: return Verify("RETR /foo%2f..%2fbar%5c\r\n", data, PRE_QUIT, "200 OK\r\n"); default: return FtpSocketDataProvider::OnWrite(data); } } private: DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderPathSeparatorsNotUnescaped); }; class FtpSocketDataProviderFileNotFound : public FtpSocketDataProvider { public: FtpSocketDataProviderFileNotFound() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_SIZE: return Verify("SIZE /file\r\n", data, PRE_CWD, "550 File Not Found\r\n"); case PRE_CWD: return Verify("CWD /file\r\n", data, use_epsv() ? PRE_RETR_EPSV : PRE_RETR_PASV, "550 File Not Found\r\n"); case PRE_RETR: return Verify("RETR /file\r\n", data, PRE_QUIT, "550 File Not Found\r\n"); default: return FtpSocketDataProvider::OnWrite(data); } } private: DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileNotFound); }; class FtpSocketDataProviderFileDownloadWithPasvFallback : public FtpSocketDataProviderFileDownload { public: FtpSocketDataProviderFileDownloadWithPasvFallback() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_RETR_EPSV: return Verify("EPSV\r\n", data, PRE_RETR_PASV, "500 No can do\r\n"); case PRE_CWD: return Verify("CWD /file\r\n", data, use_epsv() ? PRE_RETR_EPSV : PRE_RETR_PASV, "550 Not a directory\r\n"); default: return FtpSocketDataProviderFileDownload::OnWrite(data); } } private: DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownloadWithPasvFallback); }; class FtpSocketDataProviderFileDownloadZeroSize : public FtpSocketDataProviderFileDownload { public: FtpSocketDataProviderFileDownloadZeroSize() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_SIZE: return Verify("SIZE /file\r\n", data, PRE_CWD, "213 0\r\n"); case PRE_CWD: return Verify("CWD /file\r\n", data, use_epsv() ? PRE_RETR_EPSV : PRE_RETR_PASV, "550 not a directory\r\n"); default: return FtpSocketDataProviderFileDownload::OnWrite(data); } } private: DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownloadZeroSize); }; class FtpSocketDataProviderFileDownloadCWD451 : public FtpSocketDataProviderFileDownload { public: FtpSocketDataProviderFileDownloadCWD451() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_CWD: return Verify("CWD /file\r\n", data, use_epsv() ? PRE_RETR_EPSV : PRE_RETR_PASV, "451 not a directory\r\n"); default: return FtpSocketDataProviderFileDownload::OnWrite(data); } } private: DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownloadCWD451); }; class FtpSocketDataProviderVMSFileDownload : public FtpSocketDataProvider { public: FtpSocketDataProviderVMSFileDownload() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, 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_LIST_EPSV: return Verify("EPSV\r\n", data, PRE_LIST_PASV, "500 EPSV command unknown\r\n"); case PRE_SIZE: return Verify("SIZE ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_CWD, "213 18\r\n"); case PRE_CWD: return Verify("CWD ANONYMOUS_ROOT:[file]\r\n", data, use_epsv() ? PRE_RETR_EPSV : PRE_RETR_PASV, "550 Not a directory\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 FtpSocketDataProviderFileDownload { public: FtpSocketDataProviderEscaping() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_SIZE: return Verify("SIZE / !\"#$%y\200\201\r\n", data, PRE_CWD, "213 18\r\n"); case PRE_CWD: return Verify("CWD / !\"#$%y\200\201\r\n", data, use_epsv() ? PRE_RETR_EPSV : PRE_RETR_PASV, "550 Not a directory\r\n"); case PRE_RETR: return Verify("RETR / !\"#$%y\200\201\r\n", data, PRE_QUIT, "200 OK\r\n"); default: return FtpSocketDataProviderFileDownload::OnWrite(data); } } private: DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEscaping); }; class FtpSocketDataProviderFileDownloadTransferStarting : public FtpSocketDataProviderFileDownload { public: FtpSocketDataProviderFileDownloadTransferStarting() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, 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() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_LIST: return Verify("LIST -l\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() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_SIZE: // Use unallocated 599 FTP error code to make sure it falls into the // generic ERR_FTP_FAILED bucket. return Verify("SIZE /file\r\n", data, PRE_QUIT, "599 Evil Response\r\n" "599 More Evil\r\n"); default: return FtpSocketDataProviderFileDownload::OnWrite(data); } } private: DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownloadInvalidResponse); }; class FtpSocketDataProviderEvilEpsv : public FtpSocketDataProviderFileDownload { public: FtpSocketDataProviderEvilEpsv(const char* epsv_response, State expected_state) : epsv_response_(epsv_response), epsv_response_length_(std::strlen(epsv_response)), expected_state_(expected_state) {} FtpSocketDataProviderEvilEpsv(const char* epsv_response, size_t epsv_response_length, State expected_state) : epsv_response_(epsv_response), epsv_response_length_(epsv_response_length), expected_state_(expected_state) {} MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_RETR_EPSV: return Verify("EPSV\r\n", data, expected_state_, epsv_response_, epsv_response_length_); default: return FtpSocketDataProviderFileDownload::OnWrite(data); } } private: const char* epsv_response_; const size_t epsv_response_length_; const State expected_state_; DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEvilEpsv); }; class FtpSocketDataProviderEvilPasv : public FtpSocketDataProviderFileDownloadWithPasvFallback { public: FtpSocketDataProviderEvilPasv(const char* pasv_response, State expected_state) : pasv_response_(pasv_response), expected_state_(expected_state) { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_RETR_PASV: return Verify("PASV\r\n", data, expected_state_, pasv_response_); default: return FtpSocketDataProviderFileDownloadWithPasvFallback::OnWrite(data); } } private: const char* pasv_response_; const State expected_state_; DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEvilPasv); }; class FtpSocketDataProviderEvilSize : public FtpSocketDataProviderFileDownload { public: FtpSocketDataProviderEvilSize(const char* size_response, State expected_state) : size_response_(size_response), expected_state_(expected_state) { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, data.length()); switch (state()) { case PRE_SIZE: return Verify("SIZE /file\r\n", data, expected_state_, size_response_); default: return FtpSocketDataProviderFileDownload::OnWrite(data); } } private: const char* size_response_; const State expected_state_; DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEvilSize); }; class FtpSocketDataProviderEvilLogin : public FtpSocketDataProviderFileDownload { public: FtpSocketDataProviderEvilLogin(const char* expected_user, const char* expected_password) : expected_user_(expected_user), expected_password_(expected_password) { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, 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() { } MockWriteResult OnWrite(const std::string& data) override { if (InjectFault()) return MockWriteResult(ASYNC, 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 ::testing::WithParamInterface { public: FtpNetworkTransactionTest() : host_resolver_(new MockHostResolver), transaction_(host_resolver_.get(), &mock_socket_factory_) { scoped_refptr rules( new RuleBasedHostResolverProc(NULL)); if (GetFamily() == AF_INET) { rules->AddIPLiteralRule("*", "127.0.0.1", "127.0.0.1"); } else if (GetFamily() == AF_INET6) { rules->AddIPLiteralRule("*", "::1", "::1"); } else { NOTREACHED(); } host_resolver_->set_rules(rules.get()); } protected: // Accessor to make code refactoring-friendly, e.g. when we change the way // parameters are passed (like more parameters). int GetFamily() { return GetParam(); } 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) { // Expect EPSV usage for non-IPv4 control connections. ctrl_socket->set_use_epsv((GetFamily() != AF_INET)); mock_socket_factory_.AddSocketDataProvider(ctrl_socket); std::string mock_data("mock-data"); MockRead data_reads[] = { // Usually FTP servers close the data connection after the entire data has // been received. MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), MockRead(mock_data.c_str()), }; scoped_ptr data_socket( new StaticSocketDataProvider(data_reads, arraysize(data_reads), NULL, 0)); mock_socket_factory_.AddSocketDataProvider(data_socket.get()); FtpRequestInfo request_info = GetRequestInfo(request); EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState()); ASSERT_EQ(ERR_IO_PENDING, transaction_.Start(&request_info, callback_.callback(), BoundNetLog())); EXPECT_NE(LOAD_STATE_IDLE, transaction_.GetLoadState()); ASSERT_EQ(expected_result, callback_.WaitForResult()); if (expected_result == OK) { scoped_refptr io_buffer(new IOBuffer(kBufferSize)); memset(io_buffer->data(), 0, kBufferSize); ASSERT_EQ(ERR_IO_PENDING, transaction_.Read(io_buffer.get(), kBufferSize, callback_.callback())); ASSERT_EQ(static_cast(mock_data.length()), callback_.WaitForResult()); EXPECT_EQ(mock_data, std::string(io_buffer->data(), mock_data.length())); // Do another Read to detect that the data socket is now closed. int rv = transaction_.Read(io_buffer.get(), kBufferSize, callback_.callback()); if (rv == ERR_IO_PENDING) { EXPECT_EQ(0, callback_.WaitForResult()); } else { EXPECT_EQ(0, rv); } } EXPECT_EQ(FtpSocketDataProvider::QUIT, ctrl_socket->state()); 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_ptr host_resolver_; MockClientSocketFactory mock_socket_factory_; FtpNetworkTransaction transaction_; TestCompletionCallback callback_; }; TEST_P(FtpNetworkTransactionTest, FailedLookup) { FtpRequestInfo request_info = GetRequestInfo("ftp://badhost"); scoped_refptr rules( new RuleBasedHostResolverProc(NULL)); rules->AddSimulatedFailure("badhost"); host_resolver_->set_rules(rules.get()); EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState()); ASSERT_EQ(ERR_IO_PENDING, transaction_.Start(&request_info, callback_.callback(), BoundNetLog())); ASSERT_EQ(ERR_NAME_NOT_RESOLVED, callback_.WaitForResult()); EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState()); } // Check that when determining the host, the square brackets decorating IPv6 // literals in URLs are stripped. TEST_P(FtpNetworkTransactionTest, StripBracketsFromIPv6Literals) { // This test only makes sense for IPv6 connections. if (GetFamily() != AF_INET6) return; host_resolver_->rules()->AddSimulatedFailure("[::1]"); // We start a transaction that is expected to fail with ERR_INVALID_RESPONSE. // The important part of this test is to make sure that we don't fail with // ERR_NAME_NOT_RESOLVED, since that would mean the decorated hostname // was used. FtpSocketDataProviderEvilSize ctrl_socket( "213 99999999999999999999999999999999\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://[::1]/file", ERR_INVALID_RESPONSE); } TEST_P(FtpNetworkTransactionTest, DirectoryTransaction) { FtpSocketDataProviderDirectoryListing ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host", OK); EXPECT_TRUE(transaction_.GetResponseInfo()->is_directory_listing); EXPECT_EQ(-1, transaction_.GetResponseInfo()->expected_content_size); EXPECT_EQ((GetFamily() == AF_INET) ? "127.0.0.1" : "::1", transaction_.GetResponseInfo()->socket_address.host()); EXPECT_EQ(21, transaction_.GetResponseInfo()->socket_address.port()); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionWithPasvFallback) { FtpSocketDataProviderDirectoryListingWithPasvFallback ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host", OK); EXPECT_TRUE(transaction_.GetResponseInfo()->is_directory_listing); EXPECT_EQ(-1, transaction_.GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionWithTypecode) { FtpSocketDataProviderDirectoryListing ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/;type=d", OK); EXPECT_TRUE(transaction_.GetResponseInfo()->is_directory_listing); EXPECT_EQ(-1, transaction_.GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionMultilineWelcome) { FtpSocketDataProviderDirectoryListing ctrl_socket; ctrl_socket.set_multiline_welcome(true); ExecuteTransaction(&ctrl_socket, "ftp://host", OK); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionShortReads2) { FtpSocketDataProviderDirectoryListing ctrl_socket; ctrl_socket.set_short_read_limit(2); ExecuteTransaction(&ctrl_socket, "ftp://host", OK); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionShortReads5) { FtpSocketDataProviderDirectoryListing ctrl_socket; ctrl_socket.set_short_read_limit(5); ExecuteTransaction(&ctrl_socket, "ftp://host", OK); } TEST_P(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.set_allow_unconsumed_reads(true); ctrl_socket.set_multiline_welcome(true); ctrl_socket.set_short_read_limit(5); ExecuteTransaction(&ctrl_socket, "ftp://host", OK); } // Regression test for http://crbug.com/60555. TEST_P(FtpNetworkTransactionTest, DirectoryTransactionZeroSize) { FtpSocketDataProviderDirectoryListingZeroSize ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host", OK); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionVMS) { FtpSocketDataProviderVMSDirectoryListing ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/dir", OK); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionVMSRootDirectory) { FtpSocketDataProviderVMSDirectoryListingRootDirectory ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host", OK); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionTransferStarting) { FtpSocketDataProviderDirectoryListingTransferStarting ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransaction) { FtpSocketDataProviderFileDownload ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); // We pass an artificial value of 18 as a response to the SIZE command. EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size); EXPECT_EQ((GetFamily() == AF_INET) ? "127.0.0.1" : "::1", transaction_.GetResponseInfo()->socket_address.host()); EXPECT_EQ(21, transaction_.GetResponseInfo()->socket_address.port()); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithPasvFallback) { FtpSocketDataProviderFileDownloadWithPasvFallback ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); // We pass an artificial value of 18 as a response to the SIZE command. EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeA) { FtpSocketDataProviderFileDownloadWithFileTypecode ctrl_socket; ctrl_socket.set_data_type('A'); ExecuteTransaction(&ctrl_socket, "ftp://host/file;type=a", OK); // We pass an artificial value of 18 as a response to the SIZE command. EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeI) { FtpSocketDataProviderFileDownloadWithFileTypecode ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/file;type=i", OK); // We pass an artificial value of 18 as a response to the SIZE command. EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionMultilineWelcome) { FtpSocketDataProviderFileDownload ctrl_socket; ctrl_socket.set_multiline_welcome(true); ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionShortReads2) { FtpSocketDataProviderFileDownload ctrl_socket; ctrl_socket.set_short_read_limit(2); ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionShortReads5) { FtpSocketDataProviderFileDownload ctrl_socket; ctrl_socket.set_short_read_limit(5); ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionZeroSize) { FtpSocketDataProviderFileDownloadZeroSize ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionCWD451) { FtpSocketDataProviderFileDownloadCWD451 ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionVMS) { FtpSocketDataProviderVMSFileDownload ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionTransferStarting) { FtpSocketDataProviderFileDownloadTransferStarting ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionInvalidResponse) { FtpSocketDataProviderFileDownloadInvalidResponse ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvReallyBadFormat) { FtpSocketDataProviderEvilPasv ctrl_socket("227 Portscan (127,0,0,\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); } TEST_P(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_P(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_P(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_P(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_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafeHost) { FtpSocketDataProviderEvilPasv ctrl_socket( "227 Portscan (10,1,2,3,123,456)\r\n", FtpSocketDataProvider::PRE_RETR); ctrl_socket.set_use_epsv(GetFamily() != AF_INET); std::string mock_data("mock-data"); MockRead data_reads[] = { MockRead(mock_data.c_str()), }; StaticSocketDataProvider data_socket1; StaticSocketDataProvider data_socket2(data_reads, arraysize(data_reads), NULL, 0); mock_socket_factory_.AddSocketDataProvider(&ctrl_socket); mock_socket_factory_.AddSocketDataProvider(&data_socket1); mock_socket_factory_.AddSocketDataProvider(&data_socket2); FtpRequestInfo request_info = GetRequestInfo("ftp://host/file"); // Start the transaction. ASSERT_EQ(ERR_IO_PENDING, transaction_.Start(&request_info, callback_.callback(), BoundNetLog())); ASSERT_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 = static_cast(transaction_.data_socket_.get()); 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 (which could be 127.0.0.1 // or ::1 depending on whether we use IPv6). for (AddressList::const_iterator it = data_socket->addresses().begin(); it != data_socket->addresses().end(); ++it) { EXPECT_NE("10.1.2.3", it->ToStringWithoutPort()); } } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvReallyBadFormat1) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||22)\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvReallyBadFormat2) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (||\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvReallyBadFormat3) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvReallyBadFormat4) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (||||)\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvReallyBadFormat5) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; // Breaking the string in the next line prevents MSVC warning C4125. const char response[] = "227 Portscan (\0\0\031" "773\0)\r\n"; FtpSocketDataProviderEvilEpsv ctrl_socket(response, sizeof(response)-1, FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvUnsafePort1) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||22|)\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvUnsafePort2) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||258|)\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvUnsafePort3) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||772|)\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvUnsafePort4) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||2049|)\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvWeirdSep) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan ($$$31744$)\r\n", FtpSocketDataProvider::PRE_RETR); ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvWeirdSepUnsafePort) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan ($$$317$)\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvIllegalHost) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|2|::1|31744|)\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilLoginBadUsername) { FtpSocketDataProviderEvilLogin ctrl_socket("hello%0Aworld", "test"); ExecuteTransaction(&ctrl_socket, "ftp://hello%0Aworld:test@host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilLoginBadPassword) { FtpSocketDataProviderEvilLogin ctrl_socket("test", "hello%0Dworld"); ExecuteTransaction(&ctrl_socket, "ftp://test:hello%0Dworld@host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionSpaceInLogin) { FtpSocketDataProviderEvilLogin ctrl_socket("hello world", "test"); ExecuteTransaction(&ctrl_socket, "ftp://hello%20world:test@host/file", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionSpaceInPassword) { FtpSocketDataProviderEvilLogin ctrl_socket("test", "hello world"); ExecuteTransaction(&ctrl_socket, "ftp://test:hello%20world@host/file", OK); } // Make sure FtpNetworkTransaction doesn't request paths like // "/foo/../bar". Doing so wouldn't be a security issue, client side, but just // doesn't seem like a good idea. TEST_P(FtpNetworkTransactionTest, DownloadTransactionPathSeparatorsNotUnescaped) { FtpSocketDataProviderPathSeparatorsNotUnescaped ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/foo%2f..%2fbar%5c", OK); // We pass an artificial value of 18 as a response to the SIZE command. EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size); } TEST_P(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_.callback(), BoundNetLog())); ASSERT_EQ(ERR_FTP_FAILED, callback_.WaitForResult()); MockRead ctrl_reads[] = { MockRead("220 host TestFTPd\r\n"), MockRead("221 Goodbye!\r\n"), MockRead(SYNCHRONOUS, OK), }; MockWrite ctrl_writes[] = { MockWrite("QUIT\r\n"), }; StaticSocketDataProvider ctrl_socket2(ctrl_reads, arraysize(ctrl_reads), ctrl_writes, arraysize(ctrl_writes)); mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2); ASSERT_EQ(ERR_IO_PENDING, transaction_.RestartWithAuth( AuthCredentials( base::ASCIIToUTF16("foo\nownz0red"), base::ASCIIToUTF16("innocent")), callback_.callback())); EXPECT_EQ(ERR_MALFORMED_IDENTITY, callback_.WaitForResult()); } TEST_P(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_.callback(), BoundNetLog())); ASSERT_EQ(ERR_FTP_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(SYNCHRONOUS, OK), }; MockWrite ctrl_writes[] = { MockWrite("USER innocent\r\n"), MockWrite("QUIT\r\n"), }; StaticSocketDataProvider ctrl_socket2(ctrl_reads, arraysize(ctrl_reads), ctrl_writes, arraysize(ctrl_writes)); mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2); ASSERT_EQ(ERR_IO_PENDING, transaction_.RestartWithAuth( AuthCredentials(base::ASCIIToUTF16("innocent"), base::ASCIIToUTF16("foo\nownz0red")), callback_.callback())); EXPECT_EQ(ERR_MALFORMED_IDENTITY, callback_.WaitForResult()); } TEST_P(FtpNetworkTransactionTest, Escaping) { FtpSocketDataProviderEscaping ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/%20%21%22%23%24%25%79%80%81", OK); } // Test for http://crbug.com/23794. TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilSize) { // Try to overflow int64_t in the response. FtpSocketDataProviderEvilSize ctrl_socket( "213 99999999999999999999999999999999\r\n", FtpSocketDataProvider::PRE_QUIT); ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); } // Test for http://crbug.com/36360. TEST_P(FtpNetworkTransactionTest, DownloadTransactionBigSize) { // Pass a valid, but large file size. The transaction should not fail. FtpSocketDataProviderEvilSize ctrl_socket( "213 3204427776\r\n", FtpSocketDataProvider::PRE_CWD); ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); EXPECT_EQ(3204427776LL, transaction_.GetResponseInfo()->expected_content_size); } // Regression test for http://crbug.com/25023. TEST_P(FtpNetworkTransactionTest, CloseConnection) { FtpSocketDataProviderCloseConnection ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host", ERR_EMPTY_RESPONSE); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionFailUser) { FtpSocketDataProviderDirectoryListing ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host", FtpSocketDataProvider::PRE_USER, FtpSocketDataProvider::PRE_QUIT, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionFailPass) { FtpSocketDataProviderDirectoryListing ctrl_socket; TransactionFailHelper(&ctrl_socket, "ftp://host", FtpSocketDataProvider::PRE_PASSWD, FtpSocketDataProvider::PRE_QUIT, "530 Login authentication failed\r\n", ERR_FTP_FAILED); } // Regression test for http://crbug.com/38707. TEST_P(FtpNetworkTransactionTest, DirectoryTransactionFailPass503) { FtpSocketDataProviderDirectoryListing ctrl_socket; TransactionFailHelper(&ctrl_socket, "ftp://host", FtpSocketDataProvider::PRE_PASSWD, FtpSocketDataProvider::PRE_QUIT, "503 Bad sequence of commands\r\n", ERR_FTP_BAD_COMMAND_SEQUENCE); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionFailSyst) { FtpSocketDataProviderDirectoryListing ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host", FtpSocketDataProvider::PRE_SYST, FtpSocketDataProvider::PRE_PWD, "599 fail\r\n", OK); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionFailPwd) { FtpSocketDataProviderDirectoryListing ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host", FtpSocketDataProvider::PRE_PWD, FtpSocketDataProvider::PRE_QUIT, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionFailType) { FtpSocketDataProviderDirectoryListing ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host", FtpSocketDataProvider::PRE_TYPE, FtpSocketDataProvider::PRE_QUIT, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionFailEpsv) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderDirectoryListing ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper( &ctrl_socket, "ftp://host", FtpSocketDataProvider::PRE_LIST_EPSV, FtpSocketDataProvider::PRE_NOPASV, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionFailCwd) { FtpSocketDataProviderDirectoryListing ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host", FtpSocketDataProvider::PRE_CWD, FtpSocketDataProvider::PRE_QUIT, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionFailList) { FtpSocketDataProviderVMSDirectoryListing ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host/dir", FtpSocketDataProvider::PRE_LIST, FtpSocketDataProvider::PRE_QUIT, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionFailUser) { FtpSocketDataProviderFileDownload ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_USER, FtpSocketDataProvider::PRE_QUIT, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionFailPass) { FtpSocketDataProviderFileDownload ctrl_socket; TransactionFailHelper(&ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_PASSWD, FtpSocketDataProvider::PRE_QUIT, "530 Login authentication failed\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionFailSyst) { FtpSocketDataProviderFileDownload ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_SYST, FtpSocketDataProvider::PRE_PWD, "599 fail\r\n", OK); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionFailPwd) { FtpSocketDataProviderFileDownload ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_PWD, FtpSocketDataProvider::PRE_QUIT, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionFailType) { FtpSocketDataProviderFileDownload ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_TYPE, FtpSocketDataProvider::PRE_QUIT, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionFailEpsv) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) return; FtpSocketDataProviderFileDownload ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper( &ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_RETR_EPSV, FtpSocketDataProvider::PRE_NOPASV, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionFailRetr) { FtpSocketDataProviderFileDownload ctrl_socket; // Use unallocated 599 FTP error code to make sure it falls into the generic // ERR_FTP_FAILED bucket. TransactionFailHelper(&ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_RETR, FtpSocketDataProvider::PRE_QUIT, "599 fail\r\n", ERR_FTP_FAILED); } TEST_P(FtpNetworkTransactionTest, FileNotFound) { FtpSocketDataProviderFileNotFound ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_FTP_FAILED); } // Test for http://crbug.com/38845. TEST_P(FtpNetworkTransactionTest, ZeroLengthDirInPWD) { FtpSocketDataProviderFileDownload ctrl_socket; TransactionFailHelper(&ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_PWD, FtpSocketDataProvider::PRE_TYPE, "257 \"\"\r\n", OK); } INSTANTIATE_TEST_CASE_P(FTP, FtpNetworkTransactionTest, ::testing::Values(AF_INET, AF_INET6)); } // namespace net