| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/ftp/ftp_network_transaction.h" | 5 #include "net/ftp/ftp_network_transaction.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 9 #include "base/compiler_specific.h" | 9 #include "base/compiler_specific.h" |
| 10 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 #include "url/url_constants.h" | 29 #include "url/url_constants.h" |
| 30 | 30 |
| 31 namespace net { | 31 namespace net { |
| 32 | 32 |
| 33 namespace { | 33 namespace { |
| 34 | 34 |
| 35 const char kCRLF[] = "\r\n"; | 35 const char kCRLF[] = "\r\n"; |
| 36 | 36 |
| 37 const int kCtrlBufLen = 1024; | 37 const int kCtrlBufLen = 1024; |
| 38 | 38 |
| 39 // Returns true if |input| can be safely used as a part of FTP command. | 39 // Returns true if |input| can be safely used as a part of an FTP command. |
| 40 bool IsValidFTPCommandString(const std::string& input) { | 40 bool IsValidFTPCommandSubstring(const std::string& input) { |
| 41 // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII | 41 // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII |
| 42 // characters in the command if the request path contains them. To be | 42 // characters in the command if the request path contains them. To be |
| 43 // compatible, we do the same and allow non-ASCII characters in a command. | 43 // compatible, we do the same and allow non-ASCII characters in a command. |
| 44 | 44 |
| 45 // Protect agains newline injection attack. | 45 // Protect agains newline injection attack. |
| 46 if (input.find_first_of("\r\n") != std::string::npos) | 46 if (input.find_first_of("\r\n") != std::string::npos) |
| 47 return false; | 47 return false; |
| 48 | 48 |
| 49 return true; | 49 return true; |
| 50 } | 50 } |
| (...skipping 404 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 455 const std::string& command_for_log, | 455 const std::string& command_for_log, |
| 456 Command cmd) { | 456 Command cmd) { |
| 457 // If we send a new command when we still have unprocessed responses | 457 // If we send a new command when we still have unprocessed responses |
| 458 // for previous commands, the response receiving code will have no way to know | 458 // for previous commands, the response receiving code will have no way to know |
| 459 // which responses are for which command. | 459 // which responses are for which command. |
| 460 DCHECK(!ctrl_response_buffer_->ResponseAvailable()); | 460 DCHECK(!ctrl_response_buffer_->ResponseAvailable()); |
| 461 | 461 |
| 462 DCHECK(!write_command_buf_.get()); | 462 DCHECK(!write_command_buf_.get()); |
| 463 DCHECK(!write_buf_.get()); | 463 DCHECK(!write_buf_.get()); |
| 464 | 464 |
| 465 if (!IsValidFTPCommandString(command)) { | 465 if (!IsValidFTPCommandSubstring(command)) { |
| 466 // Callers should validate the command themselves and return a more specific | 466 // Callers should validate the command themselves and return a more specific |
| 467 // error code. | 467 // error code. |
| 468 NOTREACHED(); | 468 NOTREACHED(); |
| 469 return Stop(ERR_UNEXPECTED); | 469 return Stop(ERR_UNEXPECTED); |
| 470 } | 470 } |
| 471 | 471 |
| 472 command_sent_ = cmd; | 472 command_sent_ = cmd; |
| 473 | 473 |
| 474 write_command_buf_ = new IOBufferWithSize(command.length() + 2); | 474 write_command_buf_ = new IOBufferWithSize(command.length() + 2); |
| 475 write_buf_ = new DrainableIOBuffer(write_command_buf_.get(), | 475 write_buf_ = new DrainableIOBuffer(write_command_buf_.get(), |
| (...skipping 22 matching lines...) Expand all Loading... |
| 498 path.append(gurl_path); | 498 path.append(gurl_path); |
| 499 } | 499 } |
| 500 // Make sure that if the path is expected to be a file, it won't end | 500 // Make sure that if the path is expected to be a file, it won't end |
| 501 // with a trailing slash. | 501 // with a trailing slash. |
| 502 if (!is_directory && path.length() > 1 && path.back() == '/') | 502 if (!is_directory && path.length() > 1 && path.back() == '/') |
| 503 path.erase(path.length() - 1); | 503 path.erase(path.length() - 1); |
| 504 UnescapeRule::Type unescape_rules = | 504 UnescapeRule::Type unescape_rules = |
| 505 UnescapeRule::SPACES | | 505 UnescapeRule::SPACES | |
| 506 UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS; | 506 UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS; |
| 507 // This may unescape to non-ASCII characters, but we allow that. See the | 507 // This may unescape to non-ASCII characters, but we allow that. See the |
| 508 // comment for IsValidFTPCommandString. | 508 // comment for IsValidFTPCommandSubstring. |
| 509 path = UnescapeURLComponent(path, unescape_rules); | 509 path = UnescapeURLComponent(path, unescape_rules); |
| 510 | 510 |
| 511 if (system_type_ == SYSTEM_TYPE_VMS) { | 511 if (system_type_ == SYSTEM_TYPE_VMS) { |
| 512 if (is_directory) | 512 if (is_directory) |
| 513 path = FtpUtil::UnixDirectoryPathToVMS(path); | 513 path = FtpUtil::UnixDirectoryPathToVMS(path); |
| 514 else | 514 else |
| 515 path = FtpUtil::UnixFilePathToVMS(path); | 515 path = FtpUtil::UnixFilePathToVMS(path); |
| 516 } | 516 } |
| 517 | 517 |
| 518 DCHECK(IsValidFTPCommandString(path)); | 518 DCHECK(IsValidFTPCommandSubstring(path)); |
| 519 return path; | 519 return path; |
| 520 } | 520 } |
| 521 | 521 |
| 522 void FtpNetworkTransaction::DetectTypecode() { | 522 void FtpNetworkTransaction::DetectTypecode() { |
| 523 if (!request_->url.has_path()) | 523 if (!request_->url.has_path()) |
| 524 return; | 524 return; |
| 525 std::string gurl_path(request_->url.path()); | 525 std::string gurl_path(request_->url.path()); |
| 526 | 526 |
| 527 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path. | 527 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path. |
| 528 std::string::size_type pos = gurl_path.rfind(';'); | 528 std::string::size_type pos = gurl_path.rfind(';'); |
| (...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 749 } | 749 } |
| 750 return OK; | 750 return OK; |
| 751 } | 751 } |
| 752 | 752 |
| 753 // FTP Commands and responses | 753 // FTP Commands and responses |
| 754 | 754 |
| 755 // USER Command. | 755 // USER Command. |
| 756 int FtpNetworkTransaction::DoCtrlWriteUSER() { | 756 int FtpNetworkTransaction::DoCtrlWriteUSER() { |
| 757 std::string command = "USER " + base::UTF16ToUTF8(credentials_.username()); | 757 std::string command = "USER " + base::UTF16ToUTF8(credentials_.username()); |
| 758 | 758 |
| 759 if (!IsValidFTPCommandString(command)) | 759 if (!IsValidFTPCommandSubstring(command)) |
| 760 return Stop(ERR_MALFORMED_IDENTITY); | 760 return Stop(ERR_MALFORMED_IDENTITY); |
| 761 | 761 |
| 762 next_state_ = STATE_CTRL_READ; | 762 next_state_ = STATE_CTRL_READ; |
| 763 return SendFtpCommand(command, "USER ***", COMMAND_USER); | 763 return SendFtpCommand(command, "USER ***", COMMAND_USER); |
| 764 } | 764 } |
| 765 | 765 |
| 766 int FtpNetworkTransaction::ProcessResponseUSER( | 766 int FtpNetworkTransaction::ProcessResponseUSER( |
| 767 const FtpCtrlResponse& response) { | 767 const FtpCtrlResponse& response) { |
| 768 switch (GetErrorClass(response.status_code)) { | 768 switch (GetErrorClass(response.status_code)) { |
| 769 case ERROR_CLASS_INITIATED: | 769 case ERROR_CLASS_INITIATED: |
| 770 return Stop(ERR_INVALID_RESPONSE); | 770 return Stop(ERR_INVALID_RESPONSE); |
| 771 case ERROR_CLASS_OK: | 771 case ERROR_CLASS_OK: |
| 772 next_state_ = STATE_CTRL_WRITE_SYST; | 772 next_state_ = STATE_CTRL_WRITE_SYST; |
| 773 break; | 773 break; |
| 774 case ERROR_CLASS_INFO_NEEDED: | 774 case ERROR_CLASS_INFO_NEEDED: |
| 775 next_state_ = STATE_CTRL_WRITE_PASS; | 775 next_state_ = STATE_CTRL_WRITE_PASS; |
| 776 break; | 776 break; |
| 777 case ERROR_CLASS_TRANSIENT_ERROR: | 777 case ERROR_CLASS_TRANSIENT_ERROR: |
| 778 case ERROR_CLASS_PERMANENT_ERROR: | 778 case ERROR_CLASS_PERMANENT_ERROR: |
| 779 response_.needs_auth = true; | 779 response_.needs_auth = true; |
| 780 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); | 780 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
| 781 } | 781 } |
| 782 return OK; | 782 return OK; |
| 783 } | 783 } |
| 784 | 784 |
| 785 // PASS command. | 785 // PASS command. |
| 786 int FtpNetworkTransaction::DoCtrlWritePASS() { | 786 int FtpNetworkTransaction::DoCtrlWritePASS() { |
| 787 std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password()); | 787 std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password()); |
| 788 | 788 |
| 789 if (!IsValidFTPCommandString(command)) | 789 if (!IsValidFTPCommandSubstring(command)) |
| 790 return Stop(ERR_MALFORMED_IDENTITY); | 790 return Stop(ERR_MALFORMED_IDENTITY); |
| 791 | 791 |
| 792 next_state_ = STATE_CTRL_READ; | 792 next_state_ = STATE_CTRL_READ; |
| 793 return SendFtpCommand(command, "PASS ***", COMMAND_PASS); | 793 return SendFtpCommand(command, "PASS ***", COMMAND_PASS); |
| 794 } | 794 } |
| 795 | 795 |
| 796 int FtpNetworkTransaction::ProcessResponsePASS( | 796 int FtpNetworkTransaction::ProcessResponsePASS( |
| 797 const FtpCtrlResponse& response) { | 797 const FtpCtrlResponse& response) { |
| 798 switch (GetErrorClass(response.status_code)) { | 798 switch (GetErrorClass(response.status_code)) { |
| 799 case ERROR_CLASS_INITIATED: | 799 case ERROR_CLASS_INITIATED: |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 889 line = line.substr(quote_pos + 1); | 889 line = line.substr(quote_pos + 1); |
| 890 quote_pos = line.find('"'); | 890 quote_pos = line.find('"'); |
| 891 if (quote_pos == std::string::npos) | 891 if (quote_pos == std::string::npos) |
| 892 return Stop(ERR_INVALID_RESPONSE); | 892 return Stop(ERR_INVALID_RESPONSE); |
| 893 line = line.substr(0, quote_pos); | 893 line = line.substr(0, quote_pos); |
| 894 } | 894 } |
| 895 if (system_type_ == SYSTEM_TYPE_VMS) | 895 if (system_type_ == SYSTEM_TYPE_VMS) |
| 896 line = FtpUtil::VMSPathToUnix(line); | 896 line = FtpUtil::VMSPathToUnix(line); |
| 897 if (!line.empty() && line.back() == '/') | 897 if (!line.empty() && line.back() == '/') |
| 898 line.erase(line.length() - 1); | 898 line.erase(line.length() - 1); |
| 899 // Fail if the "path" contains characters not allowed in commands. |
| 900 // This does mean that files with CRs or LFs in their names aren't |
| 901 // handled, but that's probably for the best. |
| 902 if (!IsValidFTPCommandSubstring(line)) |
| 903 return Stop(ERR_INVALID_RESPONSE); |
| 899 current_remote_directory_ = line; | 904 current_remote_directory_ = line; |
| 900 next_state_ = STATE_CTRL_WRITE_TYPE; | 905 next_state_ = STATE_CTRL_WRITE_TYPE; |
| 901 break; | 906 break; |
| 902 } | 907 } |
| 903 case ERROR_CLASS_INFO_NEEDED: | 908 case ERROR_CLASS_INFO_NEEDED: |
| 904 return Stop(ERR_INVALID_RESPONSE); | 909 return Stop(ERR_INVALID_RESPONSE); |
| 905 case ERROR_CLASS_TRANSIENT_ERROR: | 910 case ERROR_CLASS_TRANSIENT_ERROR: |
| 906 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); | 911 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
| 907 case ERROR_CLASS_PERMANENT_ERROR: | 912 case ERROR_CLASS_PERMANENT_ERROR: |
| 908 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); | 913 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); |
| (...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1356 if (!had_error_type[type]) { | 1361 if (!had_error_type[type]) { |
| 1357 had_error_type[type] = true; | 1362 had_error_type[type] = true; |
| 1358 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened", | 1363 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened", |
| 1359 type, NUM_OF_NET_ERROR_TYPES); | 1364 type, NUM_OF_NET_ERROR_TYPES); |
| 1360 } | 1365 } |
| 1361 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount", | 1366 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount", |
| 1362 type, NUM_OF_NET_ERROR_TYPES); | 1367 type, NUM_OF_NET_ERROR_TYPES); |
| 1363 } | 1368 } |
| 1364 | 1369 |
| 1365 } // namespace net | 1370 } // namespace net |
| OLD | NEW |