|  | // 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/containers/circular_deque.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 "net/test/gtest_util.h" | 
|  | #include "net/test/test_with_scoped_task_environment.h" | 
|  | #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "testing/platform_test.h" | 
|  |  | 
|  | #include "starboard/memory.h" | 
|  |  | 
|  | using net::test::IsError; | 
|  | using net::test::IsOk; | 
|  |  | 
|  | 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(); | 
|  | } | 
|  | ~FtpSocketDataProvider() override = default; | 
|  |  | 
|  | // 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,123\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,123\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. | 
|  | base::circular_deque<MockRead> 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() = default; | 
|  | ~FtpSocketDataProviderDirectoryListing() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderDirectoryListingWithPasvFallback() override = default; | 
|  |  | 
|  | 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 FtpSocketDataProviderVMSDirectoryListing : public FtpSocketDataProvider { | 
|  | public: | 
|  | FtpSocketDataProviderVMSDirectoryListing() = default; | 
|  | ~FtpSocketDataProviderVMSDirectoryListing() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderVMSDirectoryListingRootDirectory() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderFileDownloadWithFileTypecode() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderFileDownload() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderPathSeparatorsNotUnescaped() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderFileNotFound() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderFileDownloadWithPasvFallback() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderFileDownloadZeroSize() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderFileDownloadCWD451() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderVMSFileDownload() override = default; | 
|  |  | 
|  | 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() = default; | 
|  | ~FtpSocketDataProviderEscaping() override = default; | 
|  |  | 
|  | 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 FtpSocketDataProviderFileDownloadInvalidResponse | 
|  | : public FtpSocketDataProviderFileDownload { | 
|  | public: | 
|  | FtpSocketDataProviderFileDownloadInvalidResponse() = default; | 
|  | ~FtpSocketDataProviderFileDownloadInvalidResponse() override = default; | 
|  |  | 
|  | 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) {} | 
|  |  | 
|  | ~FtpSocketDataProviderEvilEpsv() override = default; | 
|  |  | 
|  | 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* const 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) { | 
|  | } | 
|  | ~FtpSocketDataProviderEvilPasv() override = default; | 
|  |  | 
|  | 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* const 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) { | 
|  | } | 
|  | ~FtpSocketDataProviderEvilSize() override = default; | 
|  |  | 
|  | 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* const 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) { | 
|  | } | 
|  | ~FtpSocketDataProviderEvilLogin() override = default; | 
|  |  | 
|  | 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* const expected_user_; | 
|  | const char* const expected_password_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEvilLogin); | 
|  | }; | 
|  |  | 
|  | class FtpNetworkTransactionTest : public PlatformTest, | 
|  | public ::testing::WithParamInterface<int>, | 
|  | public WithScopedTaskEnvironment { | 
|  | public: | 
|  | FtpNetworkTransactionTest() : host_resolver_(new MockHostResolver) { | 
|  | SetUpTransaction(); | 
|  |  | 
|  | scoped_refptr<RuleBasedHostResolverProc> rules( | 
|  | new RuleBasedHostResolverProc(nullptr)); | 
|  | 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()); | 
|  | } | 
|  | ~FtpNetworkTransactionTest() override = default; | 
|  |  | 
|  | // Sets up an FtpNetworkTransaction and MocketClientSocketFactory, replacing | 
|  | // the default one. Only needs to be called if a test runs multiple | 
|  | // transactions. | 
|  | void SetUpTransaction() { | 
|  | mock_socket_factory_ = std::make_unique<MockClientSocketFactory>(); | 
|  | transaction_ = std::make_unique<FtpNetworkTransaction>( | 
|  | host_resolver_.get(), mock_socket_factory_.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()), | 
|  | }; | 
|  |  | 
|  | std::unique_ptr<StaticSocketDataProvider> data_socket = | 
|  | std::make_unique<StaticSocketDataProvider>(data_reads, | 
|  | base::span<MockWrite>()); | 
|  | 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(), | 
|  | NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS)); | 
|  | EXPECT_NE(LOAD_STATE_IDLE, transaction_->GetLoadState()); | 
|  | ASSERT_EQ(expected_result, callback_.WaitForResult()); | 
|  | if (expected_result == OK) { | 
|  | scoped_refptr<IOBuffer> io_buffer = | 
|  | base::MakeRefCounted<IOBuffer>(kBufferSize); | 
|  | SbMemorySet(io_buffer->data(), 0, kBufferSize); | 
|  | ASSERT_EQ(ERR_IO_PENDING, transaction_->Read(io_buffer.get(), kBufferSize, | 
|  | callback_.callback())); | 
|  | ASSERT_EQ(static_cast<int>(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); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<MockHostResolver> host_resolver_; | 
|  | std::unique_ptr<MockClientSocketFactory> mock_socket_factory_; | 
|  | std::unique_ptr<FtpNetworkTransaction> transaction_; | 
|  | TestCompletionCallback callback_; | 
|  | }; | 
|  |  | 
|  | TEST_P(FtpNetworkTransactionTest, FailedLookup) { | 
|  | FtpRequestInfo request_info = GetRequestInfo("ftp://badhost"); | 
|  | scoped_refptr<RuleBasedHostResolverProc> rules( | 
|  | new RuleBasedHostResolverProc(nullptr)); | 
|  | 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(), | 
|  | NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS)); | 
|  | ASSERT_THAT(callback_.WaitForResult(), IsError(ERR_NAME_NOT_RESOLVED)); | 
|  | 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) { | 
|  | FtpSocketDataProviderDirectoryListing ctrl_socket; | 
|  | ctrl_socket.InjectFailure(FtpSocketDataProvider::PRE_SIZE, | 
|  | FtpSocketDataProvider::PRE_CWD, "213 0\r\n"); | 
|  | 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) { | 
|  | FtpSocketDataProviderDirectoryListing ctrl_socket; | 
|  | ctrl_socket.InjectFailure(FtpSocketDataProvider::PRE_LIST, | 
|  | FtpSocketDataProvider::PRE_QUIT, | 
|  | "125-Data connection already open.\r\n" | 
|  | "125  Transfer starting.\r\n" | 
|  | "226 Transfer complete.\r\n"); | 
|  | 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) { | 
|  | FtpSocketDataProviderFileDownload ctrl_socket; | 
|  | ctrl_socket.InjectFailure(FtpSocketDataProvider::PRE_RETR, | 
|  | FtpSocketDataProvider::PRE_QUIT, | 
|  | "125-Data connection already open.\r\n" | 
|  | "125  Transfer starting.\r\n" | 
|  | "226 Transfer complete.\r\n"); | 
|  | 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, DownloadTransactionEvilPasvInvalidPort1) { | 
|  | // Unsafe. 8 * 256 + 1 = 2049, which is used by nfs. | 
|  | FtpSocketDataProviderEvilPasv ctrl_socket( | 
|  | "227 Portscan (127,0,0,1,256,100)\r\n", FtpSocketDataProvider::PRE_QUIT); | 
|  | ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); | 
|  | } | 
|  |  | 
|  | TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvInvalidPort2) { | 
|  | // Unsafe. 8 * 256 + 1 = 2049, which is used by nfs. | 
|  | FtpSocketDataProviderEvilPasv ctrl_socket( | 
|  | "227 Portscan (127,0,0,1,100,256)\r\n", FtpSocketDataProvider::PRE_QUIT); | 
|  | ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); | 
|  | } | 
|  |  | 
|  | TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvInvalidPort3) { | 
|  | // Unsafe. 8 * 256 + 1 = 2049, which is used by nfs. | 
|  | FtpSocketDataProviderEvilPasv ctrl_socket( | 
|  | "227 Portscan (127,0,0,1,-100,100)\r\n", FtpSocketDataProvider::PRE_QUIT); | 
|  | ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); | 
|  | } | 
|  |  | 
|  | TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvInvalidPort4) { | 
|  | // Unsafe. 8 * 256 + 1 = 2049, which is used by nfs. | 
|  | FtpSocketDataProviderEvilPasv ctrl_socket( | 
|  | "227 Portscan (127,0,0,1,100,-100)\r\n", FtpSocketDataProvider::PRE_QUIT); | 
|  | ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); | 
|  | } | 
|  |  | 
|  | TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafeHost) { | 
|  | FtpSocketDataProviderEvilPasv ctrl_socket( | 
|  | "227 Portscan (10,1,2,3,123,123)\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, base::span<MockWrite>()); | 
|  | 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(), | 
|  | NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS)); | 
|  | ASSERT_THAT(callback_.WaitForResult(), IsOk()); | 
|  |  | 
|  | // 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<MockTCPClientSocket*>(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 (auto 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, DownloadTransactionEvilEpsvInvalidPort) { | 
|  | // This test makes no sense for IPv4 connections (we don't use EPSV there). | 
|  | if (GetFamily() == AF_INET) | 
|  | return; | 
|  |  | 
|  | FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||4294973296|)\r\n", | 
|  | FtpSocketDataProvider::PRE_QUIT); | 
|  | ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); | 
|  | } | 
|  |  | 
|  | 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(), | 
|  | NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS)); | 
|  | ASSERT_THAT(callback_.WaitForResult(), IsError(ERR_FTP_FAILED)); | 
|  |  | 
|  | 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, 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_THAT(callback_.WaitForResult(), IsError(ERR_MALFORMED_IDENTITY)); | 
|  | } | 
|  |  | 
|  | 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(), | 
|  | NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS)); | 
|  | ASSERT_THAT(callback_.WaitForResult(), IsError(ERR_FTP_FAILED)); | 
|  |  | 
|  | 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, 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_THAT(callback_.WaitForResult(), IsError(ERR_MALFORMED_IDENTITY)); | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | FtpSocketDataProvider ctrl_socket; | 
|  | ctrl_socket.InjectFailure(FtpSocketDataProvider::PRE_USER, | 
|  | FtpSocketDataProvider::PRE_QUIT, ""); | 
|  | 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); | 
|  | } | 
|  |  | 
|  | TEST_P(FtpNetworkTransactionTest, UnexpectedInitiatedResponseForDirectory) { | 
|  | // The states for a directory listing where an initiated response will cause | 
|  | // an error.  Includes all commands sent on the directory listing path, except | 
|  | // CWD, SIZE, LIST, and QUIT commands. | 
|  | FtpSocketDataProvider::State kFailingStates[] = { | 
|  | FtpSocketDataProvider::PRE_USER, FtpSocketDataProvider::PRE_PASSWD, | 
|  | FtpSocketDataProvider::PRE_SYST, FtpSocketDataProvider::PRE_PWD, | 
|  | FtpSocketDataProvider::PRE_TYPE, | 
|  | GetFamily() != AF_INET ? FtpSocketDataProvider::PRE_LIST_EPSV | 
|  | : FtpSocketDataProvider::PRE_LIST_PASV, | 
|  | FtpSocketDataProvider::PRE_CWD, | 
|  | }; | 
|  |  | 
|  | for (FtpSocketDataProvider::State state : kFailingStates) { | 
|  | SetUpTransaction(); | 
|  | FtpSocketDataProviderDirectoryListing ctrl_socket; | 
|  | ctrl_socket.InjectFailure(state, FtpSocketDataProvider::PRE_QUIT, | 
|  | "157 Foo\r\n"); | 
|  | ExecuteTransaction(&ctrl_socket, "ftp://host/", ERR_INVALID_RESPONSE); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_P(FtpNetworkTransactionTest, UnexpectedInitiatedResponseForFile) { | 
|  | // The states for a download where an initiated response will cause an error. | 
|  | // Includes all commands sent on the file download path, except CWD, SIZE, and | 
|  | // QUIT commands. | 
|  | const FtpSocketDataProvider::State kFailingStates[] = { | 
|  | FtpSocketDataProvider::PRE_USER, FtpSocketDataProvider::PRE_PASSWD, | 
|  | FtpSocketDataProvider::PRE_SYST, FtpSocketDataProvider::PRE_PWD, | 
|  | FtpSocketDataProvider::PRE_TYPE, | 
|  | GetFamily() != AF_INET ? FtpSocketDataProvider::PRE_RETR_EPSV | 
|  | : FtpSocketDataProvider::PRE_RETR_PASV, | 
|  | FtpSocketDataProvider::PRE_CWD}; | 
|  |  | 
|  | for (FtpSocketDataProvider::State state : kFailingStates) { | 
|  | LOG(ERROR) << "??: " << state; | 
|  | SetUpTransaction(); | 
|  | FtpSocketDataProviderFileDownload ctrl_socket; | 
|  | ctrl_socket.InjectFailure(state, FtpSocketDataProvider::PRE_QUIT, | 
|  | "157 Foo\r\n"); | 
|  | ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Make sure that receiving extra unexpected responses correctly results in | 
|  | // sending a QUIT message, without triggering a DCHECK. | 
|  | TEST_P(FtpNetworkTransactionTest, ExtraResponses) { | 
|  | FtpSocketDataProviderDirectoryListing ctrl_socket; | 
|  | ctrl_socket.InjectFailure(FtpSocketDataProvider::PRE_TYPE, | 
|  | FtpSocketDataProvider::PRE_QUIT, | 
|  | "157 Foo\r\n" | 
|  | "157 Bar\r\n" | 
|  | "157 Trombones\r\n"); | 
|  | ExecuteTransaction(&ctrl_socket, "ftp://host/", ERR_INVALID_RESPONSE); | 
|  | } | 
|  |  | 
|  | // Make sure that receiving extra unexpected responses to a QUIT message | 
|  | // correctly results in ending the transaction with an error, without triggering | 
|  | // a DCHECK. | 
|  | TEST_P(FtpNetworkTransactionTest, ExtraQuitResponses) { | 
|  | FtpSocketDataProviderDirectoryListing ctrl_socket; | 
|  | ctrl_socket.InjectFailure(FtpSocketDataProvider::PRE_QUIT, | 
|  | FtpSocketDataProvider::QUIT, | 
|  | "221 Foo\r\n" | 
|  | "221 Bar\r\n" | 
|  | "221 Trombones\r\n"); | 
|  | ExecuteTransaction(&ctrl_socket, "ftp://host/", ERR_INVALID_RESPONSE); | 
|  | } | 
|  |  | 
|  | // Test case for https://crbug.com/633841 - similar to the ExtraQuitResponses | 
|  | // test case, but with an empty response. | 
|  | TEST_P(FtpNetworkTransactionTest, EmptyQuitResponse) { | 
|  | FtpSocketDataProviderDirectoryListing ctrl_socket; | 
|  | ctrl_socket.InjectFailure(FtpSocketDataProvider::PRE_QUIT, | 
|  | FtpSocketDataProvider::QUIT, ""); | 
|  | ExecuteTransaction(&ctrl_socket, "ftp://host/", OK); | 
|  | } | 
|  |  | 
|  | TEST_P(FtpNetworkTransactionTest, InvalidRemoteDirectory) { | 
|  | FtpSocketDataProviderFileDownload ctrl_socket; | 
|  | TransactionFailHelper( | 
|  | &ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_PWD, | 
|  | FtpSocketDataProvider::PRE_QUIT, | 
|  | "257 \"foo\rbar\" is your current location\r\n", ERR_INVALID_RESPONSE); | 
|  | } | 
|  |  | 
|  | TEST_P(FtpNetworkTransactionTest, InvalidRemoteDirectory2) { | 
|  | FtpSocketDataProviderFileDownload ctrl_socket; | 
|  | TransactionFailHelper( | 
|  | &ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_PWD, | 
|  | FtpSocketDataProvider::PRE_QUIT, | 
|  | "257 \"foo\nbar\" is your current location\r\n", ERR_INVALID_RESPONSE); | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_CASE_P(Ftp, | 
|  | FtpNetworkTransactionTest, | 
|  | ::testing::Values(AF_INET, AF_INET6)); | 
|  |  | 
|  | }  // namespace net |