summaryrefslogtreecommitdiff
path: root/chromium/net/ftp
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-04-05 14:08:31 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-04-11 07:46:53 +0000
commit6a4cabb866f66d4128a97cdc6d9d08ce074f1247 (patch)
treeab00f70a5e89278d6a0d16ff0c42578dc4d84a2d /chromium/net/ftp
parente733310db58160074f574c429d48f8308c0afe17 (diff)
downloadqtwebengine-chromium-6a4cabb866f66d4128a97cdc6d9d08ce074f1247.tar.gz
BASELINE: Update Chromium to 57.0.2987.144
Change-Id: I29db402ff696c71a04c4dbaec822c2e53efe0267 Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
Diffstat (limited to 'chromium/net/ftp')
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser.cc4
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_ls.cc5
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_unittest.cc8
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_unittest.h9
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_vms.cc41
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc129
-rw-r--r--chromium/net/ftp/ftp_network_session.cc1
-rw-r--r--chromium/net/ftp/ftp_network_transaction.cc109
-rw-r--r--chromium/net/ftp/ftp_network_transaction_unittest.cc268
-rw-r--r--chromium/net/ftp/ftp_util.cc16
-rw-r--r--chromium/net/ftp/ftp_util_unittest.cc53
11 files changed, 403 insertions, 240 deletions
diff --git a/chromium/net/ftp/ftp_directory_listing_parser.cc b/chromium/net/ftp/ftp_directory_listing_parser.cc
index 151c1b9df56..1096ae01f62 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser.cc
@@ -28,7 +28,7 @@ int FillInRawName(const std::string& encoding,
std::vector<FtpDirectoryListingEntry>* entries) {
for (size_t i = 0; i < entries->size(); i++) {
if (!base::UTF16ToCodepage(entries->at(i).name, encoding.c_str(),
- base::OnStringConversionError::FAIL,
+ base::OnStringConversionError::SUBSTITUTE,
&entries->at(i).raw_name)) {
return ERR_ENCODING_CONVERSION_FAILED;
}
@@ -91,7 +91,7 @@ int DecodeAndParse(const std::string& text,
base::string16 converted_text;
if (base::CodepageToUTF16(text, encoding_name,
- base::OnStringConversionError::FAIL,
+ base::OnStringConversionError::SUBSTITUTE,
&converted_text)) {
const char* const kNewlineSeparators[] = {"\n", "\r\n"};
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_ls.cc b/chromium/net/ftp/ftp_directory_listing_parser_ls.cc
index 9d1b7116758..9ce1973756d 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_ls.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_ls.cc
@@ -52,9 +52,8 @@ bool TwoColumnDateListingToTime(const base::string16& date,
if (!time_exploded.HasValidValues())
return false;
- // We don't know the time zone of the server, so just use local time.
- *result = base::Time::FromLocalExploded(time_exploded);
- return true;
+ // We don't know the time zone of the server, so just use UTC.
+ return base::Time::FromUTCExploded(time_exploded, result);
}
// Returns the column index of the end of the date listing and detected
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc b/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc
index 583426d98d7..a7f5bcfb3da 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc
@@ -48,8 +48,9 @@ TEST_P(FtpDirectoryListingParserTest, Parse) {
mock_current_time_exploded.day_of_month = 15;
mock_current_time_exploded.hour = 12;
mock_current_time_exploded.minute = 45;
- base::Time mock_current_time(
- base::Time::FromLocalExploded(mock_current_time_exploded));
+ base::Time mock_current_time;
+ EXPECT_TRUE(base::Time::FromUTCExploded(mock_current_time_exploded,
+ &mock_current_time));
SCOPED_TRACE(base::StringPrintf("Test case: %s", param.name));
@@ -109,7 +110,7 @@ TEST_P(FtpDirectoryListingParserTest, Parse) {
EXPECT_EQ(size, entry.size);
base::Time::Exploded time_exploded;
- entry.last_modified.LocalExplode(&time_exploded);
+ entry.last_modified.UTCExplode(&time_exploded);
EXPECT_EQ(year, time_exploded.year);
EXPECT_EQ(month, time_exploded.month);
EXPECT_EQ(day_of_month, time_exploded.day_of_month);
@@ -157,6 +158,7 @@ const FtpTestParam kTestParams[] = {
{"dir-listing-ls-31", OK},
{"dir-listing-ls-32", OK}, // busybox
{"dir-listing-ls-33", OK},
+ {"dir-listing-ls-34", OK}, // Broken encoding. Should not fail.
{"dir-listing-netware-1", OK},
{"dir-listing-netware-2", OK},
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_unittest.h b/chromium/net/ftp/ftp_directory_listing_parser_unittest.h
index 3cbe17ad2d6..f010299ff5e 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_unittest.h
+++ b/chromium/net/ftp/ftp_directory_listing_parser_unittest.h
@@ -49,7 +49,7 @@ class FtpDirectoryListingParserTest : public testing::Test {
EXPECT_EQ(test_case.size, entry.size);
base::Time::Exploded time_exploded;
- entry.last_modified.LocalExplode(&time_exploded);
+ entry.last_modified.UTCExplode(&time_exploded);
// Only test members displayed on the directory listing.
EXPECT_EQ(test_case.year, time_exploded.year);
@@ -68,11 +68,14 @@ class FtpDirectoryListingParserTest : public testing::Test {
mock_current_time_exploded.day_of_month = 15;
mock_current_time_exploded.hour = 12;
mock_current_time_exploded.minute = 45;
- return base::Time::FromLocalExploded(mock_current_time_exploded);
+
+ base::Time out_time;
+ EXPECT_TRUE(
+ base::Time::FromUTCExploded(mock_current_time_exploded, &out_time));
+ return out_time;
}
};
} // namespace net
#endif // NET_FTP_FTP_DIRECTORY_LISTING_PARSER_UNITTEST_H_
-
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_vms.cc b/chromium/net/ftp/ftp_directory_listing_parser_vms.cc
index 8139ee0bc24..8f1bfe1dd83 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_vms.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_vms.cc
@@ -6,6 +6,7 @@
#include <vector>
+#include "base/numerics/safe_math.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -55,6 +56,24 @@ bool ParseVmsFilename(const base::string16& raw_filename,
return true;
}
+// VMS's directory listing gives file size in blocks. The exact file size is
+// unknown both because it is measured in blocks, but also because the block
+// size is unknown (but assumed to be 512 bytes).
+bool ApproximateFilesizeFromBlockCount(int64_t num_blocks, int64_t* out_size) {
+ if (num_blocks < 0)
+ return false;
+
+ const int kBlockSize = 512;
+ base::CheckedNumeric<int64_t> num_bytes = num_blocks;
+ num_bytes *= kBlockSize;
+
+ if (!num_bytes.IsValid())
+ return false; // Block count is too large.
+
+ *out_size = num_bytes.ValueOrDie();
+ return true;
+}
+
bool ParseVmsFilesize(const base::string16& input, int64_t* size) {
if (base::ContainsOnlyChars(input, base::ASCIIToUTF16("*"))) {
// Response consisting of asterisks means unknown size.
@@ -62,17 +81,9 @@ bool ParseVmsFilesize(const base::string16& input, int64_t* size) {
return true;
}
- // VMS's directory listing gives us file size in blocks. We assume that
- // the block size is 512 bytes. It doesn't give accurate file size, but is the
- // best information we have.
- const int kBlockSize = 512;
-
- if (base::StringToInt64(input, size)) {
- if (*size < 0)
- return false;
- *size *= kBlockSize;
- return true;
- }
+ int64_t num_blocks;
+ if (base::StringToInt64(input, &num_blocks))
+ return ApproximateFilesizeFromBlockCount(num_blocks, size);
std::vector<base::StringPiece16> parts =
base::SplitStringPiece(input, base::ASCIIToUTF16("/"),
@@ -90,8 +101,7 @@ bool ParseVmsFilesize(const base::string16& input, int64_t* size) {
if (blocks_used < 0 || blocks_allocated < 0)
return false;
- *size = blocks_used * kBlockSize;
- return true;
+ return ApproximateFilesizeFromBlockCount(blocks_used, size);
}
bool LooksLikeVmsFileProtectionListingPart(const base::string16& input) {
@@ -192,9 +202,8 @@ bool VmsDateListingToTime(const std::vector<base::string16>& columns,
if (!base::StringToInt(time_parts[1], &time_exploded.minute))
return false;
- // We don't know the time zone of the server, so just use local time.
- *time = base::Time::FromLocalExploded(time_exploded);
- return true;
+ // We don't know the time zone of the server, so just use UTC.
+ return base::Time::FromUTCExploded(time_exploded, time);
}
} // namespace
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc b/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
index f566481b548..24d158876f1 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
@@ -21,36 +21,33 @@ typedef FtpDirectoryListingParserTest FtpDirectoryListingParserVmsTest;
TEST_F(FtpDirectoryListingParserVmsTest, Good) {
const struct SingleLineTestData good_cases[] = {
- { "README.TXT;4 2 18-APR-2000 10:40:39.90",
- FtpDirectoryListingEntry::FILE, "readme.txt", 1024,
- 2000, 4, 18, 10, 40 },
- { ".WELCOME;1 2 13-FEB-2002 23:32:40.47",
- FtpDirectoryListingEntry::FILE, ".welcome", 1024,
- 2002, 2, 13, 23, 32 },
- { "FILE.;1 2 13-FEB-2002 23:32:40.47",
- FtpDirectoryListingEntry::FILE, "file.", 1024,
- 2002, 2, 13, 23, 32 },
- { "EXAMPLE.TXT;1 1 4-NOV-2009 06:02 [JOHNDOE] (RWED,RWED,,)",
- FtpDirectoryListingEntry::FILE, "example.txt", 512,
- 2009, 11, 4, 6, 2 },
- { "ANNOUNCE.TXT;2 1/16 12-MAR-2005 08:44:57 [SYSTEM] (RWED,RWED,RE,RE)",
- FtpDirectoryListingEntry::FILE, "announce.txt", 512,
- 2005, 3, 12, 8, 44 },
- { "TEST.DIR;1 1 4-MAR-1999 22:14:34 [UCX$NOBO,ANONYMOUS] (RWE,RWE,RWE,RWE)",
- FtpDirectoryListingEntry::DIRECTORY, "test", -1,
- 1999, 3, 4, 22, 14 },
- { "ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (,,,)",
- FtpDirectoryListingEntry::FILE, "announce.txt", 512,
- 2005, 3, 12, 8, 44 },
- { "ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (R,RW,RWD,RE)",
- FtpDirectoryListingEntry::FILE, "announce.txt", 512,
- 2005, 3, 12, 8, 44 },
- { "ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (ED,RED,WD,WED)",
- FtpDirectoryListingEntry::FILE, "announce.txt", 512,
- 2005, 3, 12, 8, 44 },
- { "VMS721.ISO;2 ****** 6-MAY-2008 09:29 [ANONY,ANONYMOUS] (RE,RWED,RE,RE)",
- FtpDirectoryListingEntry::FILE, "vms721.iso", -1,
- 2008, 5, 6, 9, 29 },
+ {"README.TXT;4 2 18-APR-2000 10:40:39.90",
+ FtpDirectoryListingEntry::FILE, "readme.txt", 1024, 2000, 4, 18, 10, 40},
+ {".WELCOME;1 2 13-FEB-2002 23:32:40.47",
+ FtpDirectoryListingEntry::FILE, ".welcome", 1024, 2002, 2, 13, 23, 32},
+ {"FILE.;1 2 13-FEB-2002 23:32:40.47", FtpDirectoryListingEntry::FILE,
+ "file.", 1024, 2002, 2, 13, 23, 32},
+ {"EXAMPLE.TXT;1 1 4-NOV-2009 06:02 [JOHNDOE] (RWED,RWED,,)",
+ FtpDirectoryListingEntry::FILE, "example.txt", 512, 2009, 11, 4, 6, 2},
+ {"ANNOUNCE.TXT;2 1/16 12-MAR-2005 08:44:57 [SYSTEM] (RWED,RWED,RE,RE)",
+ FtpDirectoryListingEntry::FILE, "announce.txt", 512, 2005, 3, 12, 8, 44},
+ {"TEST.DIR;1 1 4-MAR-1999 22:14:34 [UCX$NOBO,ANONYMOUS] "
+ "(RWE,RWE,RWE,RWE)",
+ FtpDirectoryListingEntry::DIRECTORY, "test", -1, 1999, 3, 4, 22, 14},
+ {"ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (,,,)",
+ FtpDirectoryListingEntry::FILE, "announce.txt", 512, 2005, 3, 12, 8, 44},
+ {"ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (R,RW,RWD,RE)",
+ FtpDirectoryListingEntry::FILE, "announce.txt", 512, 2005, 3, 12, 8, 44},
+ {"ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (ED,RED,WD,WED)",
+ FtpDirectoryListingEntry::FILE, "announce.txt", 512, 2005, 3, 12, 8, 44},
+ {"VMS721.ISO;2 ****** 6-MAY-2008 09:29 [ANONY,ANONYMOUS] "
+ "(RE,RWED,RE,RE)",
+ FtpDirectoryListingEntry::FILE, "vms721.iso", -1, 2008, 5, 6, 9, 29},
+ // This has an unusually large allocated block size (INT64_MAX), but
+ // shouldn't matter as it is not used.
+ {"ANNOUNCE.TXT;2 1/9223372036854775807 12-MAR-2005 08:44:57 [SYSTEM] "
+ "(RWED,RWED,RE,RE)",
+ FtpDirectoryListingEntry::FILE, "announce.txt", 512, 2005, 3, 12, 8, 44},
};
for (size_t i = 0; i < arraysize(good_cases); i++) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i,
@@ -76,37 +73,47 @@ TEST_F(FtpDirectoryListingParserVmsTest, Good) {
TEST_F(FtpDirectoryListingParserVmsTest, Bad) {
const char* const bad_cases[] = {
- "garbage",
-
- // Missing file version number.
- "README.TXT 2 18-APR-2000 10:40:39",
-
- // Missing extension.
- "README;1 2 18-APR-2000 10:40:39",
-
- // Malformed file size.
- "README.TXT;1 garbage 18-APR-2000 10:40:39",
- "README.TXT;1 -2 18-APR-2000 10:40:39",
-
- // Malformed date.
- "README.TXT;1 2 APR-2000 10:40:39",
- "README.TXT;1 2 -18-APR-2000 10:40:39",
- "README.TXT;1 2 18-APR 10:40:39",
- "README.TXT;1 2 18-APR-2000 10",
- "README.TXT;1 2 18-APR-2000 10:40.25",
- "README.TXT;1 2 18-APR-2000 10:40.25.25",
-
- // Malformed security information.
- "X.TXT;2 1 12-MAR-2005 08:44:57 (RWED,RWED,RE,RE)",
- "X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM]",
- "X.TXT;2 1 12-MAR-2005 08:44:57 (SYSTEM) (RWED,RWED,RE,RE)",
- "X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM] [RWED,RWED,RE,RE]",
- "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED)",
- "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,RE,RE,RE)",
- "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWEDRWED,RE,RE)",
- "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,DEWR,RE,RE)",
- "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,Q,RE)",
- "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RRWWEEDD,RE,RE)",
+ "garbage",
+
+ // Missing file version number.
+ "README.TXT 2 18-APR-2000 10:40:39",
+
+ // Missing extension.
+ "README;1 2 18-APR-2000 10:40:39",
+
+ // Malformed file size.
+ "README.TXT;1 garbage 18-APR-2000 10:40:39",
+ "README.TXT;1 -2 18-APR-2000 10:40:39",
+
+ // Malformed date.
+ "README.TXT;1 2 APR-2000 10:40:39",
+ "README.TXT;1 2 -18-APR-2000 10:40:39", "README.TXT;1 2 18-APR 10:40:39",
+ "README.TXT;1 2 18-APR-2000 10", "README.TXT;1 2 18-APR-2000 10:40.25",
+ "README.TXT;1 2 18-APR-2000 10:40.25.25",
+
+ // Malformed security information.
+ "X.TXT;2 1 12-MAR-2005 08:44:57 (RWED,RWED,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM]",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 (SYSTEM) (RWED,RWED,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM] [RWED,RWED,RE,RE]",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,RE,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWEDRWED,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,DEWR,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,Q,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RRWWEEDD,RE,RE)",
+
+ // Block size (INT64_MAX) is too large -- will overflow when
+ // multiplying by 512 to calculate the file size in bytes.
+ "README.TXT;1 9223372036854775807 18-APR-2000 10:40:39.90",
+ "README.TXT;1 9223372036854775807/9223372036854775807 18-APR-2000 "
+ "10:40:39.90",
+
+ // Block size (larger than INT64_MAX) is too large -- will fail to
+ // parse to an int64_t
+ "README.TXT;1 19223372036854775807 18-APR-2000 10:40:39.90",
+ "README.TXT;1 19223372036854775807/19223372036854775807 18-APR-2000 "
+ "10:40:39.90",
};
for (size_t i = 0; i < arraysize(bad_cases); i++) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i, bad_cases[i]));
diff --git a/chromium/net/ftp/ftp_network_session.cc b/chromium/net/ftp/ftp_network_session.cc
index 65dd218a4d4..37a7cbee74d 100644
--- a/chromium/net/ftp/ftp_network_session.cc
+++ b/chromium/net/ftp/ftp_network_session.cc
@@ -12,4 +12,3 @@ FtpNetworkSession::FtpNetworkSession(HostResolver* host_resolver)
FtpNetworkSession::~FtpNetworkSession() {}
} // namespace net
-
diff --git a/chromium/net/ftp/ftp_network_transaction.cc b/chromium/net/ftp/ftp_network_transaction.cc
index 8ecde3104de..dbe1154f826 100644
--- a/chromium/net/ftp/ftp_network_transaction.cc
+++ b/chromium/net/ftp/ftp_network_transaction.cc
@@ -16,6 +16,7 @@
#include "net/base/address_list.h"
#include "net/base/escape.h"
#include "net/base/net_errors.h"
+#include "net/base/parse_number.h"
#include "net/base/port_util.h"
#include "net/base/url_util.h"
#include "net/ftp/ftp_request_info.h"
@@ -35,8 +36,8 @@ const char kCRLF[] = "\r\n";
const int kCtrlBufLen = 1024;
-// Returns true if |input| can be safely used as a part of FTP command.
-bool IsValidFTPCommandString(const std::string& input) {
+// Returns true if |input| can be safely used as a part of an FTP command.
+bool IsValidFTPCommandSubstring(const std::string& input) {
// RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
// characters in the command if the request path contains them. To be
// compatible, we do the same and allow non-ASCII characters in a command.
@@ -123,25 +124,32 @@ int GetNetErrorCodeForFtpResponseCode(int response_code) {
bool ExtractPortFromEPSVResponse(const FtpCtrlResponse& response, int* port) {
if (response.lines.size() != 1)
return false;
- const char* ptr = response.lines[0].c_str();
- while (*ptr && *ptr != '(')
- ++ptr;
- if (!*ptr)
- return false;
- char sep = *(++ptr);
- if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
+
+ base::StringPiece epsv_line(response.lines[0]);
+ size_t start = epsv_line.find('(');
+ // If the line doesn't have a '(' or doesn't have enough characters after the
+ // first '(', fail.
+ if (start == base::StringPiece::npos || epsv_line.length() - start < 7)
return false;
- if (!isdigit(*(++ptr)))
+
+ char separator = epsv_line[start + 1];
+
+ // Make sure we have "(<d><d><d>...", where <d> is not a number.
+ if ((separator >= '0' && separator <= '9') ||
+ epsv_line[start + 2] != separator || epsv_line[start + 3] != separator) {
return false;
- *port = *ptr - '0';
- while (isdigit(*(++ptr))) {
- *port *= 10;
- *port += *ptr - '0';
}
- if (*ptr != sep)
+
+ // Skip over those characters.
+ start += 4;
+
+ // Make sure there's a terminal <d>.
+ size_t end = epsv_line.find(separator, start);
+ if (end == base::StringPiece::npos)
return false;
- return true;
+ return ParseInt32(epsv_line.substr(start, end - start),
+ ParseIntFormat::NON_NEGATIVE, port);
}
// There are two way we can receive IP address and port.
@@ -190,13 +198,15 @@ bool ExtractPortFromPASVResponse(const FtpCtrlResponse& response, int* port) {
// Ignore the IP address supplied in the response. We are always going
// to connect back to the same server to prevent FTP PASV port scanning.
- int p0, p1;
- if (!base::StringToInt(pieces[4], &p0))
+ uint32_t p0, p1;
+ if (!ParseUint32(pieces[4], &p0))
return false;
- if (!base::StringToInt(pieces[5], &p1))
+ if (!ParseUint32(pieces[5], &p1))
+ return false;
+ if (p0 > 0xFF || p1 > 0xFF)
return false;
- *port = (p0 << 8) + p1;
+ *port = (p0 << 8) + p1;
return true;
}
@@ -417,7 +427,9 @@ int FtpNetworkTransaction::ProcessCtrlResponse() {
}
// We may get multiple responses for some commands,
- // see http://crbug.com/18036.
+ // see http://crbug.com/18036. Consume all responses, regardless of whether
+ // they make sense. On unexpected responses, SendFtpCommand expects all data
+ // to have already been consumed, even when sending the QUIT command.
while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
response = ctrl_response_buffer_->PopResponse();
@@ -430,7 +442,8 @@ int FtpNetworkTransaction::ProcessCtrlResponse() {
break;
default:
// Multiple responses for other commands are invalid.
- return Stop(ERR_INVALID_RESPONSE);
+ rv = Stop(ERR_INVALID_RESPONSE);
+ break;
}
}
@@ -449,7 +462,7 @@ int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
DCHECK(!write_command_buf_.get());
DCHECK(!write_buf_.get());
- if (!IsValidFTPCommandString(command)) {
+ if (!IsValidFTPCommandSubstring(command)) {
// Callers should validate the command themselves and return a more specific
// error code.
NOTREACHED();
@@ -492,7 +505,7 @@ std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
UnescapeRule::SPACES |
UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS;
// This may unescape to non-ASCII characters, but we allow that. See the
- // comment for IsValidFTPCommandString.
+ // comment for IsValidFTPCommandSubstring.
path = UnescapeURLComponent(path, unescape_rules);
if (system_type_ == SYSTEM_TYPE_VMS) {
@@ -502,7 +515,7 @@ std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
path = FtpUtil::UnixFilePathToVMS(path);
}
- DCHECK(IsValidFTPCommandString(path));
+ DCHECK(IsValidFTPCommandSubstring(path));
return path;
}
@@ -743,7 +756,7 @@ int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
int FtpNetworkTransaction::DoCtrlWriteUSER() {
std::string command = "USER " + base::UTF16ToUTF8(credentials_.username());
- if (!IsValidFTPCommandString(command))
+ if (!IsValidFTPCommandSubstring(command))
return Stop(ERR_MALFORMED_IDENTITY);
next_state_ = STATE_CTRL_READ;
@@ -753,6 +766,8 @@ int FtpNetworkTransaction::DoCtrlWriteUSER() {
int FtpNetworkTransaction::ProcessResponseUSER(
const FtpCtrlResponse& response) {
switch (GetErrorClass(response.status_code)) {
+ case ERROR_CLASS_INITIATED:
+ return Stop(ERR_INVALID_RESPONSE);
case ERROR_CLASS_OK:
next_state_ = STATE_CTRL_WRITE_SYST;
break;
@@ -763,9 +778,6 @@ int FtpNetworkTransaction::ProcessResponseUSER(
case ERROR_CLASS_PERMANENT_ERROR:
response_.needs_auth = true;
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
return OK;
}
@@ -774,7 +786,7 @@ int FtpNetworkTransaction::ProcessResponseUSER(
int FtpNetworkTransaction::DoCtrlWritePASS() {
std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password());
- if (!IsValidFTPCommandString(command))
+ if (!IsValidFTPCommandSubstring(command))
return Stop(ERR_MALFORMED_IDENTITY);
next_state_ = STATE_CTRL_READ;
@@ -784,6 +796,8 @@ int FtpNetworkTransaction::DoCtrlWritePASS() {
int FtpNetworkTransaction::ProcessResponsePASS(
const FtpCtrlResponse& response) {
switch (GetErrorClass(response.status_code)) {
+ case ERROR_CLASS_INITIATED:
+ return Stop(ERR_INVALID_RESPONSE);
case ERROR_CLASS_OK:
next_state_ = STATE_CTRL_WRITE_SYST;
break;
@@ -793,9 +807,6 @@ int FtpNetworkTransaction::ProcessResponsePASS(
case ERROR_CLASS_PERMANENT_ERROR:
response_.needs_auth = true;
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
return OK;
}
@@ -853,9 +864,6 @@ int FtpNetworkTransaction::ProcessResponseSYST(
// Server does not recognize the SYST command so proceed.
next_state_ = STATE_CTRL_WRITE_PWD;
break;
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
return OK;
}
@@ -888,6 +896,11 @@ int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
line = FtpUtil::VMSPathToUnix(line);
if (!line.empty() && line.back() == '/')
line.erase(line.length() - 1);
+ // Fail if the "path" contains characters not allowed in commands.
+ // This does mean that files with CRs or LFs in their names aren't
+ // handled, but that's probably for the best.
+ if (!IsValidFTPCommandSubstring(line))
+ return Stop(ERR_INVALID_RESPONSE);
current_remote_directory_ = line;
next_state_ = STATE_CTRL_WRITE_TYPE;
break;
@@ -898,9 +911,6 @@ int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
case ERROR_CLASS_PERMANENT_ERROR:
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
return OK;
}
@@ -934,9 +944,6 @@ int FtpNetworkTransaction::ProcessResponseTYPE(
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
case ERROR_CLASS_PERMANENT_ERROR:
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
return OK;
}
@@ -972,9 +979,6 @@ int FtpNetworkTransaction::ProcessResponseEPSV(
use_epsv_ = false;
next_state_ = STATE_CTRL_WRITE_PASV;
return OK;
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
return OK;
}
@@ -1009,9 +1013,6 @@ int FtpNetworkTransaction::ProcessResponsePASV(
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
case ERROR_CLASS_PERMANENT_ERROR:
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
return OK;
}
@@ -1045,9 +1046,6 @@ int FtpNetworkTransaction::ProcessResponseRETR(
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
case ERROR_CLASS_PERMANENT_ERROR:
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
return OK;
@@ -1091,9 +1089,6 @@ int FtpNetworkTransaction::ProcessResponseSIZE(
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
}
break;
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
// If the resource is known beforehand to be a file, RETR should be issued,
@@ -1137,9 +1132,6 @@ int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
return ProcessResponseCWDNotADirectory();
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
return OK;
@@ -1197,9 +1189,6 @@ int FtpNetworkTransaction::ProcessResponseLIST(
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
case ERROR_CLASS_PERMANENT_ERROR:
return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
- default:
- NOTREACHED();
- return Stop(ERR_UNEXPECTED);
}
return OK;
}
diff --git a/chromium/net/ftp/ftp_network_transaction_unittest.cc b/chromium/net/ftp/ftp_network_transaction_unittest.cc
index 3c75f42fda8..33ae12385d2 100644
--- a/chromium/net/ftp/ftp_network_transaction_unittest.cc
+++ b/chromium/net/ftp/ftp_network_transaction_unittest.cc
@@ -109,13 +109,13 @@ class FtpSocketDataProvider : public SocketDataProvider {
"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");
+ "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,456\r\n");
+ "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.
@@ -841,15 +841,33 @@ class FtpSocketDataProviderCloseConnection : public FtpSocketDataProvider {
DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderCloseConnection);
};
+class BorkedFtpSocketDataProvider : public FtpSocketDataProvider {
+ public:
+ BorkedFtpSocketDataProvider() {}
+ ~BorkedFtpSocketDataProvider() override {}
+
+ MockWriteResult OnWrite(const std::string& data) override {
+ switch (state()) {
+ case PRE_USER:
+ return Verify("USER anonymous\r\n", data, PRE_PASSWD, "957 Spam\r\n");
+ default:
+ return FtpSocketDataProvider::OnWrite(data);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BorkedFtpSocketDataProvider);
+};
+
class FtpNetworkTransactionTest
: public PlatformTest,
public ::testing::WithParamInterface<int> {
public:
- FtpNetworkTransactionTest()
- : host_resolver_(new MockHostResolver),
- transaction_(host_resolver_.get(), &mock_socket_factory_) {
+ FtpNetworkTransactionTest() : host_resolver_(new MockHostResolver) {
+ SetUpTransaction();
+
scoped_refptr<RuleBasedHostResolverProc> rules(
- new RuleBasedHostResolverProc(NULL));
+ new RuleBasedHostResolverProc(nullptr));
if (GetFamily() == AF_INET) {
rules->AddIPLiteralRule("*", "127.0.0.1", "127.0.0.1");
} else if (GetFamily() == AF_INET6) {
@@ -860,6 +878,15 @@ class FtpNetworkTransactionTest
host_resolver_->set_rules(rules.get());
}
+ // 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_.reset(new MockClientSocketFactory());
+ transaction_.reset(new 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).
@@ -879,7 +906,7 @@ class FtpNetworkTransactionTest
// Expect EPSV usage for non-IPv4 control connections.
ctrl_socket->set_use_epsv((GetFamily() != AF_INET));
- mock_socket_factory_.AddSocketDataProvider(ctrl_socket);
+ mock_socket_factory_->AddSocketDataProvider(ctrl_socket);
std::string mock_data("mock-data");
MockRead data_reads[] = {
@@ -892,27 +919,26 @@ class FtpNetworkTransactionTest
std::unique_ptr<StaticSocketDataProvider> data_socket(
new StaticSocketDataProvider(data_reads, arraysize(data_reads), NULL,
0));
- mock_socket_factory_.AddSocketDataProvider(data_socket.get());
+ mock_socket_factory_->AddSocketDataProvider(data_socket.get());
FtpRequestInfo request_info = GetRequestInfo(request);
- EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
+ EXPECT_EQ(LOAD_STATE_IDLE, transaction_->GetLoadState());
ASSERT_EQ(ERR_IO_PENDING,
- transaction_.Start(&request_info, callback_.callback(),
- NetLogWithSource()));
- EXPECT_NE(LOAD_STATE_IDLE, transaction_.GetLoadState());
+ transaction_->Start(&request_info, callback_.callback(),
+ NetLogWithSource()));
+ EXPECT_NE(LOAD_STATE_IDLE, transaction_->GetLoadState());
ASSERT_EQ(expected_result, callback_.WaitForResult());
if (expected_result == OK) {
scoped_refptr<IOBuffer> io_buffer(new IOBuffer(kBufferSize));
memset(io_buffer->data(), 0, kBufferSize);
- ASSERT_EQ(ERR_IO_PENDING,
- transaction_.Read(io_buffer.get(), kBufferSize,
- callback_.callback()));
+ 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());
+ int rv = transaction_->Read(io_buffer.get(), kBufferSize,
+ callback_.callback());
if (rv == ERR_IO_PENDING) {
EXPECT_EQ(0, callback_.WaitForResult());
} else {
@@ -920,7 +946,7 @@ class FtpNetworkTransactionTest
}
}
EXPECT_EQ(FtpSocketDataProvider::QUIT, ctrl_socket->state());
- EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
+ EXPECT_EQ(LOAD_STATE_IDLE, transaction_->GetLoadState());
}
void TransactionFailHelper(FtpSocketDataProvider* ctrl_socket,
@@ -934,8 +960,8 @@ class FtpNetworkTransactionTest
}
std::unique_ptr<MockHostResolver> host_resolver_;
- MockClientSocketFactory mock_socket_factory_;
- FtpNetworkTransaction transaction_;
+ std::unique_ptr<MockClientSocketFactory> mock_socket_factory_;
+ std::unique_ptr<FtpNetworkTransaction> transaction_;
TestCompletionCallback callback_;
};
@@ -946,12 +972,12 @@ TEST_P(FtpNetworkTransactionTest, FailedLookup) {
rules->AddSimulatedFailure("badhost");
host_resolver_->set_rules(rules.get());
- EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
+ EXPECT_EQ(LOAD_STATE_IDLE, transaction_->GetLoadState());
ASSERT_EQ(ERR_IO_PENDING,
- transaction_.Start(&request_info, callback_.callback(),
- NetLogWithSource()));
+ transaction_->Start(&request_info, callback_.callback(),
+ NetLogWithSource()));
ASSERT_THAT(callback_.WaitForResult(), IsError(ERR_NAME_NOT_RESOLVED));
- EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
+ EXPECT_EQ(LOAD_STATE_IDLE, transaction_->GetLoadState());
}
// Check that when determining the host, the square brackets decorating IPv6
@@ -977,27 +1003,27 @@ 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_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());
+ 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);
+ 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);
+ EXPECT_TRUE(transaction_->GetResponseInfo()->is_directory_listing);
+ EXPECT_EQ(-1, transaction_->GetResponseInfo()->expected_content_size);
}
TEST_P(FtpNetworkTransactionTest, DirectoryTransactionMultilineWelcome) {
@@ -1054,10 +1080,10 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransaction) {
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(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());
+ transaction_->GetResponseInfo()->socket_address.host());
+ EXPECT_EQ(21, transaction_->GetResponseInfo()->socket_address.port());
}
TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithPasvFallback) {
@@ -1065,7 +1091,7 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithPasvFallback) {
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(18, transaction_->GetResponseInfo()->expected_content_size);
}
TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeA) {
@@ -1074,7 +1100,7 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeA) {
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);
+ EXPECT_EQ(18, transaction_->GetResponseInfo()->expected_content_size);
}
TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeI) {
@@ -1082,7 +1108,7 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeI) {
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);
+ EXPECT_EQ(18, transaction_->GetResponseInfo()->expected_content_size);
}
TEST_P(FtpNetworkTransactionTest, DownloadTransactionMultilineWelcome) {
@@ -1161,9 +1187,37 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafePort4) {
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,456)\r\n", FtpSocketDataProvider::PRE_RETR);
+ "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[] = {
@@ -1172,21 +1226,21 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafeHost) {
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);
+ 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()));
+ transaction_->Start(&request_info, callback_.callback(),
+ NetLogWithSource()));
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());
+ static_cast<MockTCPClientSocket*>(transaction_->data_socket_.get());
ASSERT_TRUE(data_socket);
ASSERT_TRUE(data_socket->IsConnected());
@@ -1291,6 +1345,16 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvUnsafePort4) {
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)
@@ -1351,7 +1415,7 @@ TEST_P(FtpNetworkTransactionTest,
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);
+ EXPECT_EQ(18, transaction_->GetResponseInfo()->expected_content_size);
}
TEST_P(FtpNetworkTransactionTest, EvilRestartUser) {
@@ -1359,13 +1423,13 @@ TEST_P(FtpNetworkTransactionTest, EvilRestartUser) {
ctrl_socket1.InjectFailure(FtpSocketDataProvider::PRE_PASSWD,
FtpSocketDataProvider::PRE_QUIT,
"530 Login authentication failed\r\n");
- mock_socket_factory_.AddSocketDataProvider(&ctrl_socket1);
+ 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()));
+ transaction_->Start(&request_info, callback_.callback(),
+ NetLogWithSource()));
ASSERT_THAT(callback_.WaitForResult(), IsError(ERR_FTP_FAILED));
MockRead ctrl_reads[] = {
@@ -1378,12 +1442,11 @@ TEST_P(FtpNetworkTransactionTest, EvilRestartUser) {
};
StaticSocketDataProvider ctrl_socket2(ctrl_reads, arraysize(ctrl_reads),
ctrl_writes, arraysize(ctrl_writes));
- mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2);
+ mock_socket_factory_->AddSocketDataProvider(&ctrl_socket2);
ASSERT_EQ(ERR_IO_PENDING,
- transaction_.RestartWithAuth(
- AuthCredentials(
- base::ASCIIToUTF16("foo\nownz0red"),
- base::ASCIIToUTF16("innocent")),
+ transaction_->RestartWithAuth(
+ AuthCredentials(base::ASCIIToUTF16("foo\nownz0red"),
+ base::ASCIIToUTF16("innocent")),
callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsError(ERR_MALFORMED_IDENTITY));
}
@@ -1393,13 +1456,13 @@ TEST_P(FtpNetworkTransactionTest, EvilRestartPassword) {
ctrl_socket1.InjectFailure(FtpSocketDataProvider::PRE_PASSWD,
FtpSocketDataProvider::PRE_QUIT,
"530 Login authentication failed\r\n");
- mock_socket_factory_.AddSocketDataProvider(&ctrl_socket1);
+ 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()));
+ transaction_->Start(&request_info, callback_.callback(),
+ NetLogWithSource()));
ASSERT_THAT(callback_.WaitForResult(), IsError(ERR_FTP_FAILED));
MockRead ctrl_reads[] = {
@@ -1414,9 +1477,9 @@ TEST_P(FtpNetworkTransactionTest, EvilRestartPassword) {
};
StaticSocketDataProvider ctrl_socket2(ctrl_reads, arraysize(ctrl_reads),
ctrl_writes, arraysize(ctrl_writes));
- mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2);
+ mock_socket_factory_->AddSocketDataProvider(&ctrl_socket2);
ASSERT_EQ(ERR_IO_PENDING,
- transaction_.RestartWithAuth(
+ transaction_->RestartWithAuth(
AuthCredentials(base::ASCIIToUTF16("innocent"),
base::ASCIIToUTF16("foo\nownz0red")),
callback_.callback()));
@@ -1446,7 +1509,7 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionBigSize) {
FtpSocketDataProvider::PRE_CWD);
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
EXPECT_EQ(3204427776LL,
- transaction_.GetResponseInfo()->expected_content_size);
+ transaction_->GetResponseInfo()->expected_content_size);
}
// Regression test for http://crbug.com/25023.
@@ -1660,6 +1723,91 @@ TEST_P(FtpNetworkTransactionTest, ZeroLengthDirInPWD) {
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_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));
diff --git a/chromium/net/ftp/ftp_util.cc b/chromium/net/ftp/ftp_util.cc
index 00b23b7c10b..a66845300f4 100644
--- a/chromium/net/ftp/ftp_util.cc
+++ b/chromium/net/ftp/ftp_util.cc
@@ -45,12 +45,12 @@ std::string FtpUtil::UnixFilePathToVMS(const std::string& unix_path) {
// It's an absolute path.
if (tokens.empty()) {
- DCHECK_EQ(1U, unix_path.length());
+ // It's just "/" or a series of slashes, which all mean the same thing.
return "[]";
}
if (tokens.size() == 1)
- return unix_path.substr(1); // Drop the leading slash.
+ return tokens.front(); // Return without leading slashes.
std::string result(tokens[0] + ":[");
if (tokens.size() == 2) {
@@ -272,7 +272,7 @@ bool FtpUtil::LsDateListingToTime(const base::string16& month,
// Guess the year.
base::Time::Exploded current_exploded;
- current_time.LocalExplode(&current_exploded);
+ current_time.UTCExplode(&current_exploded);
// If it's not possible for the parsed date to be in the current year,
// use the previous year.
@@ -285,9 +285,8 @@ bool FtpUtil::LsDateListingToTime(const base::string16& month,
}
}
- // We don't know the time zone of the listing, so just use local time.
- *result = base::Time::FromLocalExploded(time_exploded);
- return true;
+ // We don't know the time zone of the listing, so just use UTC.
+ return base::Time::FromUTCExploded(time_exploded, result);
}
// static
@@ -348,9 +347,8 @@ bool FtpUtil::WindowsDateListingToTime(const base::string16& date,
}
}
- // We don't know the time zone of the server, so just use local time.
- *result = base::Time::FromLocalExploded(time_exploded);
- return true;
+ // We don't know the time zone of the server, so just use UTC.
+ return base::Time::FromUTCExploded(time_exploded, result);
}
// static
diff --git a/chromium/net/ftp/ftp_util_unittest.cc b/chromium/net/ftp/ftp_util_unittest.cc
index a6f2ff019ef..8342fb4e534 100644
--- a/chromium/net/ftp/ftp_util_unittest.cc
+++ b/chromium/net/ftp/ftp_util_unittest.cc
@@ -34,6 +34,11 @@ TEST(FtpUtilTest, UnixFilePathToVMS) {
{ "a/b", "[.a]b" },
{ "a/b/c", "[.a.b]c" },
{ "a/b/c/d", "[.a.b.c]d" },
+ // Extra slashes shouldn't matter.
+ { "/////", "[]" },
+ { "/////a", "a" },
+ { "//a//b///c", "a:[b]c" },
+ { "a//b///c", "[.a.b]c" },
};
for (size_t i = 0; i < arraysize(kTestCases); i++) {
EXPECT_EQ(kTestCases[i].expected_output,
@@ -47,26 +52,30 @@ TEST(FtpUtilTest, UnixDirectoryPathToVMS) {
const char* input;
const char* expected_output;
} kTestCases[] = {
- { "", "" },
- { "/", "" },
- { "/a", "a:[000000]" },
- { "/a/", "a:[000000]" },
- { "/a/b", "a:[b]" },
- { "/a/b/", "a:[b]" },
- { "/a/b/c", "a:[b.c]" },
- { "/a/b/c/", "a:[b.c]" },
- { "/a/b/c/d", "a:[b.c.d]" },
- { "/a/b/c/d/", "a:[b.c.d]" },
- { "/a/b/c/d/e", "a:[b.c.d.e]" },
- { "/a/b/c/d/e/", "a:[b.c.d.e]" },
- { "a", "[.a]" },
- { "a/", "[.a]" },
- { "a/b", "[.a.b]" },
- { "a/b/", "[.a.b]" },
- { "a/b/c", "[.a.b.c]" },
- { "a/b/c/", "[.a.b.c]" },
- { "a/b/c/d", "[.a.b.c.d]" },
- { "a/b/c/d/", "[.a.b.c.d]" },
+ { "", "" },
+ { "/", "" },
+ { "/a", "a:[000000]" },
+ { "/a/", "a:[000000]" },
+ { "/a/b", "a:[b]" },
+ { "/a/b/", "a:[b]" },
+ { "/a/b/c", "a:[b.c]" },
+ { "/a/b/c/", "a:[b.c]" },
+ { "/a/b/c/d", "a:[b.c.d]" },
+ { "/a/b/c/d/", "a:[b.c.d]" },
+ { "/a/b/c/d/e", "a:[b.c.d.e]" },
+ { "/a/b/c/d/e/", "a:[b.c.d.e]" },
+ { "a", "[.a]" },
+ { "a/", "[.a]" },
+ { "a/b", "[.a.b]" },
+ { "a/b/", "[.a.b]" },
+ { "a/b/c", "[.a.b.c]" },
+ { "a/b/c/", "[.a.b.c]" },
+ { "a/b/c/d", "[.a.b.c.d]" },
+ { "a/b/c/d/", "[.a.b.c.d]" },
+ // Extra slashes shouldn't matter.
+ { "/////", "" },
+ { "//a//b///c//", "a:[b.c]" },
+ { "a//b///c//", "[.a.b.c]" },
};
for (size_t i = 0; i < arraysize(kTestCases); i++) {
EXPECT_EQ(kTestCases[i].expected_output,
@@ -175,7 +184,7 @@ TEST(FtpUtilTest, LsDateListingToTime) {
UTF8ToUTF16(kTestCases[i].rest), mock_current_time, &time));
base::Time::Exploded time_exploded;
- time.LocalExplode(&time_exploded);
+ time.UTCExplode(&time_exploded);
EXPECT_EQ(kTestCases[i].expected_year, time_exploded.year);
EXPECT_EQ(kTestCases[i].expected_month, time_exploded.month);
EXPECT_EQ(kTestCases[i].expected_day_of_month, time_exploded.day_of_month);
@@ -215,7 +224,7 @@ TEST(FtpUtilTest, WindowsDateListingToTime) {
&time));
base::Time::Exploded time_exploded;
- time.LocalExplode(&time_exploded);
+ time.UTCExplode(&time_exploded);
EXPECT_EQ(kTestCases[i].expected_year, time_exploded.year);
EXPECT_EQ(kTestCases[i].expected_month, time_exploded.month);
EXPECT_EQ(kTestCases[i].expected_day_of_month, time_exploded.day_of_month);