1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/ftp/ftp_network_transaction.h"
8 #include "base/bind_helpers.h"
9 #include "base/compiler_specific.h"
10 #include "base/metrics/histogram.h"
11 #include "base/string_number_conversions.h"
12 #include "base/string_split.h"
13 #include "base/string_util.h"
14 #include "base/utf_string_conversions.h"
15 #include "net/base/address_list.h"
16 #include "net/base/connection_type_histograms.h"
17 #include "net/base/escape.h"
18 #include "net/base/net_errors.h"
19 #include "net/base/net_log.h"
20 #include "net/base/net_util.h"
21 #include "net/ftp/ftp_network_session.h"
22 #include "net/ftp/ftp_request_info.h"
23 #include "net/ftp/ftp_util.h"
24 #include "net/socket/client_socket_factory.h"
25 #include "net/socket/stream_socket.h"
27 const char kCRLF
[] = "\r\n";
29 const int kCtrlBufLen
= 1024;
33 // Returns true if |input| can be safely used as a part of FTP command.
34 bool IsValidFTPCommandString(const std::string
& input
) {
35 // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
36 // characters in the command if the request path contains them. To be
37 // compatible, we do the same and allow non-ASCII characters in a command.
39 // Protect agains newline injection attack.
40 if (input
.find_first_of("\r\n") != std::string::npos
)
47 // The requested action was initiated. The client should expect another
48 // reply before issuing the next command.
49 ERROR_CLASS_INITIATED
,
51 // The requested action has been successfully completed.
54 // The command has been accepted, but to complete the operation, more
55 // information must be sent by the client.
56 ERROR_CLASS_INFO_NEEDED
,
58 // The command was not accepted and the requested action did not take place.
59 // This condition is temporary, and the client is encouraged to restart the
61 ERROR_CLASS_TRANSIENT_ERROR
,
63 // The command was not accepted and the requested action did not take place.
64 // This condition is rather permanent, and the client is discouraged from
65 // repeating the exact request.
66 ERROR_CLASS_PERMANENT_ERROR
,
69 // Returns the error class for given response code. Caller should ensure
70 // that |response_code| is in range 100-599.
71 ErrorClass
GetErrorClass(int response_code
) {
72 if (response_code
>= 100 && response_code
<= 199)
73 return ERROR_CLASS_INITIATED
;
75 if (response_code
>= 200 && response_code
<= 299)
76 return ERROR_CLASS_OK
;
78 if (response_code
>= 300 && response_code
<= 399)
79 return ERROR_CLASS_INFO_NEEDED
;
81 if (response_code
>= 400 && response_code
<= 499)
82 return ERROR_CLASS_TRANSIENT_ERROR
;
84 if (response_code
>= 500 && response_code
<= 599)
85 return ERROR_CLASS_PERMANENT_ERROR
;
87 // We should not be called on invalid error codes.
88 NOTREACHED() << response_code
;
89 return ERROR_CLASS_PERMANENT_ERROR
;
92 // Returns network error code for received FTP |response_code|.
93 int GetNetErrorCodeForFtpResponseCode(int response_code
) {
94 switch (response_code
) {
96 return net::ERR_FTP_SERVICE_UNAVAILABLE
;
98 return net::ERR_FTP_TRANSFER_ABORTED
;
100 return net::ERR_FTP_FILE_BUSY
;
103 return net::ERR_FTP_SYNTAX_ERROR
;
106 return net::ERR_FTP_COMMAND_NOT_SUPPORTED
;
108 return net::ERR_FTP_BAD_COMMAND_SEQUENCE
;
110 return net::ERR_FTP_FAILED
;
114 // From RFC 2428 Section 3:
115 // The text returned in response to the EPSV command MUST be:
116 // <some text> (<d><d><d><tcp-port><d>)
117 // <d> is a delimiter character, ideally to be |
118 bool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse
& response
,
120 if (response
.lines
.size() != 1)
122 const char* ptr
= response
.lines
[0].c_str();
123 while (*ptr
&& *ptr
!= '(')
128 if (!sep
|| isdigit(sep
) || *(++ptr
) != sep
|| *(++ptr
) != sep
)
130 if (!isdigit(*(++ptr
)))
133 while (isdigit(*(++ptr
))) {
143 // There are two way we can receive IP address and port.
144 // (127,0,0,1,23,21) IP address and port encapsulated in ().
145 // 127,0,0,1,23,21 IP address and port without ().
147 // See RFC 959, Section 4.1.2
148 bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse
& response
,
150 if (response
.lines
.size() != 1)
153 std::string
line(response
.lines
[0]);
154 if (!IsStringASCII(line
))
156 if (line
.length() < 2)
159 size_t paren_pos
= line
.find('(');
160 if (paren_pos
== std::string::npos
) {
161 // Find the first comma and use it to locate the beginning
162 // of the response data.
163 size_t comma_pos
= line
.find(',');
164 if (comma_pos
== std::string::npos
)
167 size_t space_pos
= line
.rfind(' ', comma_pos
);
168 if (space_pos
!= std::string::npos
)
169 line
= line
.substr(space_pos
+ 1);
171 // Remove the parentheses and use the text inside them.
172 size_t closing_paren_pos
= line
.rfind(')');
173 if (closing_paren_pos
== std::string::npos
)
175 if (closing_paren_pos
<= paren_pos
)
178 line
= line
.substr(paren_pos
+ 1, closing_paren_pos
- paren_pos
- 1);
181 // Split the line into comma-separated pieces and extract
183 std::vector
<std::string
> pieces
;
184 base::SplitString(line
, ',', &pieces
);
185 if (pieces
.size() != 6)
188 // Ignore the IP address supplied in the response. We are always going
189 // to connect back to the same server to prevent FTP PASV port scanning.
191 if (!base::StringToInt(pieces
[4], &p0
))
193 if (!base::StringToInt(pieces
[5], &p1
))
195 *port
= (p0
<< 8) + p1
;
204 FtpNetworkTransaction::FtpNetworkTransaction(
205 FtpNetworkSession
* session
,
206 ClientSocketFactory
* socket_factory
)
207 : command_sent_(COMMAND_NONE
),
208 ALLOW_THIS_IN_INITIALIZER_LIST(
209 io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete
,
210 base::Unretained(this)))),
213 resolver_(session
->host_resolver()),
214 read_ctrl_buf_(new IOBuffer(kCtrlBufLen
)),
215 ctrl_response_buffer_(new FtpCtrlResponseBuffer()),
216 read_data_buf_len_(0),
218 system_type_(SYSTEM_TYPE_UNKNOWN
),
219 // Use image (binary) transfer by default. It should always work,
220 // whereas the ascii transfer may damage binary data.
221 data_type_(DATA_TYPE_IMAGE
),
222 resource_type_(RESOURCE_TYPE_UNKNOWN
),
224 data_connection_port_(0),
225 socket_factory_(socket_factory
),
226 next_state_(STATE_NONE
) {
229 FtpNetworkTransaction::~FtpNetworkTransaction() {
232 int FtpNetworkTransaction::Stop(int error
) {
233 if (command_sent_
== COMMAND_QUIT
)
236 next_state_
= STATE_CTRL_WRITE_QUIT
;
241 int FtpNetworkTransaction::RestartIgnoringLastError(
242 const CompletionCallback
& callback
) {
243 return ERR_NOT_IMPLEMENTED
;
246 int FtpNetworkTransaction::Start(const FtpRequestInfo
* request_info
,
247 const CompletionCallback
& callback
,
248 const BoundNetLog
& net_log
) {
250 request_
= request_info
;
252 if (request_
->url
.has_username()) {
255 GetIdentityFromURL(request_
->url
, &username
, &password
);
256 credentials_
.Set(username
, password
);
258 credentials_
.Set(ASCIIToUTF16("anonymous"),
259 ASCIIToUTF16("chrome@example.com"));
264 next_state_
= STATE_CTRL_RESOLVE_HOST
;
266 if (rv
== ERR_IO_PENDING
)
267 user_callback_
= callback
;
271 int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials
& credentials
,
272 const CompletionCallback
& callback
) {
273 ResetStateForRestart();
275 credentials_
= credentials
;
277 next_state_
= STATE_CTRL_RESOLVE_HOST
;
279 if (rv
== ERR_IO_PENDING
)
280 user_callback_
= callback
;
284 int FtpNetworkTransaction::Read(IOBuffer
* buf
,
286 const CompletionCallback
& callback
) {
288 DCHECK_GT(buf_len
, 0);
290 read_data_buf_
= buf
;
291 read_data_buf_len_
= buf_len
;
293 next_state_
= STATE_DATA_READ
;
295 if (rv
== ERR_IO_PENDING
)
296 user_callback_
= callback
;
300 const FtpResponseInfo
* FtpNetworkTransaction::GetResponseInfo() const {
304 LoadState
FtpNetworkTransaction::GetLoadState() const {
305 if (next_state_
== STATE_CTRL_RESOLVE_HOST_COMPLETE
)
306 return LOAD_STATE_RESOLVING_HOST
;
308 if (next_state_
== STATE_CTRL_CONNECT_COMPLETE
||
309 next_state_
== STATE_DATA_CONNECT_COMPLETE
)
310 return LOAD_STATE_CONNECTING
;
312 if (next_state_
== STATE_DATA_READ_COMPLETE
)
313 return LOAD_STATE_READING_RESPONSE
;
315 if (command_sent_
== COMMAND_RETR
&& read_data_buf_
.get())
316 return LOAD_STATE_READING_RESPONSE
;
318 if (command_sent_
== COMMAND_QUIT
)
319 return LOAD_STATE_IDLE
;
321 if (command_sent_
!= COMMAND_NONE
)
322 return LOAD_STATE_SENDING_REQUEST
;
324 return LOAD_STATE_IDLE
;
327 uint64
FtpNetworkTransaction::GetUploadProgress() const {
331 void FtpNetworkTransaction::ResetStateForRestart() {
332 command_sent_
= COMMAND_NONE
;
333 user_callback_
.Reset();
334 response_
= FtpResponseInfo();
335 read_ctrl_buf_
= new IOBuffer(kCtrlBufLen
);
336 ctrl_response_buffer_
.reset(new FtpCtrlResponseBuffer());
337 read_data_buf_
= NULL
;
338 read_data_buf_len_
= 0;
340 write_buf_
->SetOffset(0);
342 data_connection_port_
= 0;
343 ctrl_socket_
.reset();
344 data_socket_
.reset();
345 next_state_
= STATE_NONE
;
348 void FtpNetworkTransaction::DoCallback(int rv
) {
349 DCHECK(rv
!= ERR_IO_PENDING
);
350 DCHECK(!user_callback_
.is_null());
352 // Since Run may result in Read being called, clear callback_ up front.
353 CompletionCallback c
= user_callback_
;
354 user_callback_
.Reset();
358 void FtpNetworkTransaction::OnIOComplete(int result
) {
359 int rv
= DoLoop(result
);
360 if (rv
!= ERR_IO_PENDING
)
364 int FtpNetworkTransaction::ProcessCtrlResponse() {
365 FtpCtrlResponse response
= ctrl_response_buffer_
->PopResponse();
368 switch (command_sent_
) {
370 // TODO(phajdan.jr): Check for errors in the welcome message.
371 next_state_
= STATE_CTRL_WRITE_USER
;
374 rv
= ProcessResponseUSER(response
);
377 rv
= ProcessResponsePASS(response
);
380 rv
= ProcessResponseSYST(response
);
383 rv
= ProcessResponsePWD(response
);
386 rv
= ProcessResponseTYPE(response
);
389 rv
= ProcessResponseEPSV(response
);
392 rv
= ProcessResponsePASV(response
);
395 rv
= ProcessResponseSIZE(response
);
398 rv
= ProcessResponseRETR(response
);
401 rv
= ProcessResponseCWD(response
);
404 rv
= ProcessResponseLIST(response
);
407 rv
= ProcessResponseQUIT(response
);
410 LOG(DFATAL
) << "Unexpected value of command_sent_: " << command_sent_
;
411 return ERR_UNEXPECTED
;
414 // We may get multiple responses for some commands,
415 // see http://crbug.com/18036.
416 while (ctrl_response_buffer_
->ResponseAvailable() && rv
== OK
) {
417 response
= ctrl_response_buffer_
->PopResponse();
419 switch (command_sent_
) {
421 rv
= ProcessResponseRETR(response
);
424 rv
= ProcessResponseLIST(response
);
427 // Multiple responses for other commands are invalid.
428 return Stop(ERR_INVALID_RESPONSE
);
435 // Used to prepare and send FTP command.
436 int FtpNetworkTransaction::SendFtpCommand(const std::string
& command
,
438 // If we send a new command when we still have unprocessed responses
439 // for previous commands, the response receiving code will have no way to know
440 // which responses are for which command.
441 DCHECK(!ctrl_response_buffer_
->ResponseAvailable());
443 DCHECK(!write_command_buf_
);
446 if (!IsValidFTPCommandString(command
)) {
447 // Callers should validate the command themselves and return a more specific
450 return Stop(ERR_UNEXPECTED
);
455 write_command_buf_
= new IOBufferWithSize(command
.length() + 2);
456 write_buf_
= new DrainableIOBuffer(write_command_buf_
,
457 write_command_buf_
->size());
458 memcpy(write_command_buf_
->data(), command
.data(), command
.length());
459 memcpy(write_command_buf_
->data() + command
.length(), kCRLF
, 2);
461 next_state_
= STATE_CTRL_WRITE
;
465 std::string
FtpNetworkTransaction::GetRequestPathForFtpCommand(
466 bool is_directory
) const {
467 std::string
path(current_remote_directory_
);
468 if (request_
->url
.has_path()) {
469 std::string
gurl_path(request_
->url
.path());
471 // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
472 std::string::size_type pos
= gurl_path
.rfind(';');
473 if (pos
!= std::string::npos
)
474 gurl_path
.resize(pos
);
476 path
.append(gurl_path
);
478 // Make sure that if the path is expected to be a file, it won't end
479 // with a trailing slash.
480 if (!is_directory
&& path
.length() > 1 && path
[path
.length() - 1] == '/')
481 path
.erase(path
.length() - 1);
482 UnescapeRule::Type unescape_rules
= UnescapeRule::SPACES
|
483 UnescapeRule::URL_SPECIAL_CHARS
;
484 // This may unescape to non-ASCII characters, but we allow that. See the
485 // comment for IsValidFTPCommandString.
486 path
= net::UnescapeURLComponent(path
, unescape_rules
);
488 if (system_type_
== SYSTEM_TYPE_VMS
) {
490 path
= FtpUtil::UnixDirectoryPathToVMS(path
);
492 path
= FtpUtil::UnixFilePathToVMS(path
);
495 DCHECK(IsValidFTPCommandString(path
));
499 void FtpNetworkTransaction::DetectTypecode() {
500 if (!request_
->url
.has_path())
502 std::string
gurl_path(request_
->url
.path());
504 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
505 std::string::size_type pos
= gurl_path
.rfind(';');
506 if (pos
== std::string::npos
)
508 std::string
typecode_string(gurl_path
.substr(pos
));
509 if (typecode_string
== ";type=a") {
510 data_type_
= DATA_TYPE_ASCII
;
511 resource_type_
= RESOURCE_TYPE_FILE
;
512 } else if (typecode_string
== ";type=i") {
513 data_type_
= DATA_TYPE_IMAGE
;
514 resource_type_
= RESOURCE_TYPE_FILE
;
515 } else if (typecode_string
== ";type=d") {
516 resource_type_
= RESOURCE_TYPE_DIRECTORY
;
520 int FtpNetworkTransaction::DoLoop(int result
) {
521 DCHECK(next_state_
!= STATE_NONE
);
525 State state
= next_state_
;
526 next_state_
= STATE_NONE
;
528 case STATE_CTRL_RESOLVE_HOST
:
530 rv
= DoCtrlResolveHost();
532 case STATE_CTRL_RESOLVE_HOST_COMPLETE
:
533 rv
= DoCtrlResolveHostComplete(rv
);
535 case STATE_CTRL_CONNECT
:
537 rv
= DoCtrlConnect();
539 case STATE_CTRL_CONNECT_COMPLETE
:
540 rv
= DoCtrlConnectComplete(rv
);
542 case STATE_CTRL_READ
:
546 case STATE_CTRL_READ_COMPLETE
:
547 rv
= DoCtrlReadComplete(rv
);
549 case STATE_CTRL_WRITE
:
553 case STATE_CTRL_WRITE_COMPLETE
:
554 rv
= DoCtrlWriteComplete(rv
);
556 case STATE_CTRL_WRITE_USER
:
558 rv
= DoCtrlWriteUSER();
560 case STATE_CTRL_WRITE_PASS
:
562 rv
= DoCtrlWritePASS();
564 case STATE_CTRL_WRITE_SYST
:
566 rv
= DoCtrlWriteSYST();
568 case STATE_CTRL_WRITE_PWD
:
570 rv
= DoCtrlWritePWD();
572 case STATE_CTRL_WRITE_TYPE
:
574 rv
= DoCtrlWriteTYPE();
576 case STATE_CTRL_WRITE_EPSV
:
578 rv
= DoCtrlWriteEPSV();
580 case STATE_CTRL_WRITE_PASV
:
582 rv
= DoCtrlWritePASV();
584 case STATE_CTRL_WRITE_RETR
:
586 rv
= DoCtrlWriteRETR();
588 case STATE_CTRL_WRITE_SIZE
:
590 rv
= DoCtrlWriteSIZE();
592 case STATE_CTRL_WRITE_CWD
:
594 rv
= DoCtrlWriteCWD();
596 case STATE_CTRL_WRITE_LIST
:
598 rv
= DoCtrlWriteLIST();
600 case STATE_CTRL_WRITE_QUIT
:
602 rv
= DoCtrlWriteQUIT();
604 case STATE_DATA_CONNECT
:
606 rv
= DoDataConnect();
608 case STATE_DATA_CONNECT_COMPLETE
:
609 rv
= DoDataConnectComplete(rv
);
611 case STATE_DATA_READ
:
615 case STATE_DATA_READ_COMPLETE
:
616 rv
= DoDataReadComplete(rv
);
619 NOTREACHED() << "bad state";
623 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
627 int FtpNetworkTransaction::DoCtrlResolveHost() {
628 next_state_
= STATE_CTRL_RESOLVE_HOST_COMPLETE
;
630 HostResolver::RequestInfo
info(HostPortPair::FromURL(request_
->url
));
631 // No known referrer.
632 return resolver_
.Resolve(
634 base::Bind(&FtpNetworkTransaction::OnIOComplete
, base::Unretained(this)),
638 int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result
) {
640 next_state_
= STATE_CTRL_CONNECT
;
644 int FtpNetworkTransaction::DoCtrlConnect() {
645 next_state_
= STATE_CTRL_CONNECT_COMPLETE
;
646 ctrl_socket_
.reset(socket_factory_
->CreateTransportClientSocket(
647 addresses_
, net_log_
.net_log(), net_log_
.source()));
648 return ctrl_socket_
->Connect(io_callback_
);
651 int FtpNetworkTransaction::DoCtrlConnectComplete(int result
) {
653 // Put the peer's IP address and port into the response.
655 result
= ctrl_socket_
->GetPeerAddress(&address
);
657 response_
.socket_address
= HostPortPair::FromAddrInfo(address
.head());
658 next_state_
= STATE_CTRL_READ
;
664 int FtpNetworkTransaction::DoCtrlRead() {
665 next_state_
= STATE_CTRL_READ_COMPLETE
;
666 return ctrl_socket_
->Read(read_ctrl_buf_
, kCtrlBufLen
, io_callback_
);
669 int FtpNetworkTransaction::DoCtrlReadComplete(int result
) {
671 // Some servers (for example Pure-FTPd) apparently close the control
672 // connection when anonymous login is not permitted. For more details
673 // see http://crbug.com/25023.
674 if (command_sent_
== COMMAND_USER
&&
675 credentials_
.username() == ASCIIToUTF16("anonymous")) {
676 response_
.needs_auth
= true;
678 return Stop(ERR_EMPTY_RESPONSE
);
683 ctrl_response_buffer_
->ConsumeData(read_ctrl_buf_
->data(), result
);
685 if (!ctrl_response_buffer_
->ResponseAvailable()) {
686 // Read more data from the control socket.
687 next_state_
= STATE_CTRL_READ
;
691 return ProcessCtrlResponse();
694 int FtpNetworkTransaction::DoCtrlWrite() {
695 next_state_
= STATE_CTRL_WRITE_COMPLETE
;
697 return ctrl_socket_
->Write(write_buf_
,
698 write_buf_
->BytesRemaining(),
702 int FtpNetworkTransaction::DoCtrlWriteComplete(int result
) {
706 write_buf_
->DidConsume(result
);
707 if (write_buf_
->BytesRemaining() == 0) {
708 // Clear the write buffer.
710 write_command_buf_
= NULL
;
712 next_state_
= STATE_CTRL_READ
;
714 next_state_
= STATE_CTRL_WRITE
;
719 // FTP Commands and responses
722 int FtpNetworkTransaction::DoCtrlWriteUSER() {
723 std::string command
= "USER " + UTF16ToUTF8(credentials_
.username());
725 if (!IsValidFTPCommandString(command
))
726 return Stop(ERR_MALFORMED_IDENTITY
);
728 next_state_
= STATE_CTRL_READ
;
729 return SendFtpCommand(command
, COMMAND_USER
);
732 int FtpNetworkTransaction::ProcessResponseUSER(
733 const FtpCtrlResponse
& response
) {
734 switch (GetErrorClass(response
.status_code
)) {
736 next_state_
= STATE_CTRL_WRITE_SYST
;
738 case ERROR_CLASS_INFO_NEEDED
:
739 next_state_
= STATE_CTRL_WRITE_PASS
;
741 case ERROR_CLASS_TRANSIENT_ERROR
:
742 case ERROR_CLASS_PERMANENT_ERROR
:
743 response_
.needs_auth
= true;
744 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
747 return Stop(ERR_UNEXPECTED
);
753 int FtpNetworkTransaction::DoCtrlWritePASS() {
754 std::string command
= "PASS " + UTF16ToUTF8(credentials_
.password());
756 if (!IsValidFTPCommandString(command
))
757 return Stop(ERR_MALFORMED_IDENTITY
);
759 next_state_
= STATE_CTRL_READ
;
760 return SendFtpCommand(command
, COMMAND_PASS
);
763 int FtpNetworkTransaction::ProcessResponsePASS(
764 const FtpCtrlResponse
& response
) {
765 switch (GetErrorClass(response
.status_code
)) {
767 next_state_
= STATE_CTRL_WRITE_SYST
;
769 case ERROR_CLASS_INFO_NEEDED
:
770 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
771 case ERROR_CLASS_TRANSIENT_ERROR
:
772 case ERROR_CLASS_PERMANENT_ERROR
:
773 response_
.needs_auth
= true;
774 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
777 return Stop(ERR_UNEXPECTED
);
783 int FtpNetworkTransaction::DoCtrlWriteSYST() {
784 std::string command
= "SYST";
785 next_state_
= STATE_CTRL_READ
;
786 return SendFtpCommand(command
, COMMAND_SYST
);
789 int FtpNetworkTransaction::ProcessResponseSYST(
790 const FtpCtrlResponse
& response
) {
791 switch (GetErrorClass(response
.status_code
)) {
792 case ERROR_CLASS_INITIATED
:
793 return Stop(ERR_INVALID_RESPONSE
);
794 case ERROR_CLASS_OK
: {
795 // All important info should be on the first line.
796 std::string line
= response
.lines
[0];
797 // The response should be ASCII, which allows us to do case-insensitive
798 // comparisons easily. If it is not ASCII, we leave the system type
800 if (IsStringASCII(line
)) {
801 line
= StringToLowerASCII(line
);
802 // The "magic" strings we test for below have been gathered by an
804 if (line
.find("l8") != std::string::npos
||
805 line
.find("unix") != std::string::npos
||
806 line
.find("bsd") != std::string::npos
) {
807 system_type_
= SYSTEM_TYPE_UNIX
;
808 } else if (line
.find("win32") != std::string::npos
||
809 line
.find("windows") != std::string::npos
) {
810 system_type_
= SYSTEM_TYPE_WINDOWS
;
811 } else if (line
.find("os/2") != std::string::npos
) {
812 system_type_
= SYSTEM_TYPE_OS2
;
813 } else if (line
.find("vms") != std::string::npos
) {
814 system_type_
= SYSTEM_TYPE_VMS
;
817 next_state_
= STATE_CTRL_WRITE_PWD
;
820 case ERROR_CLASS_INFO_NEEDED
:
821 return Stop(ERR_INVALID_RESPONSE
);
822 case ERROR_CLASS_TRANSIENT_ERROR
:
823 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
824 case ERROR_CLASS_PERMANENT_ERROR
:
825 // Server does not recognize the SYST command so proceed.
826 next_state_
= STATE_CTRL_WRITE_PWD
;
830 return Stop(ERR_UNEXPECTED
);
836 int FtpNetworkTransaction::DoCtrlWritePWD() {
837 std::string command
= "PWD";
838 next_state_
= STATE_CTRL_READ
;
839 return SendFtpCommand(command
, COMMAND_PWD
);
842 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse
& response
) {
843 switch (GetErrorClass(response
.status_code
)) {
844 case ERROR_CLASS_INITIATED
:
845 return Stop(ERR_INVALID_RESPONSE
);
846 case ERROR_CLASS_OK
: {
847 // The info we look for should be on the first line.
848 std::string line
= response
.lines
[0];
850 return Stop(ERR_INVALID_RESPONSE
);
851 std::string::size_type quote_pos
= line
.find('"');
852 if (quote_pos
!= std::string::npos
) {
853 line
= line
.substr(quote_pos
+ 1);
854 quote_pos
= line
.find('"');
855 if (quote_pos
== std::string::npos
)
856 return Stop(ERR_INVALID_RESPONSE
);
857 line
= line
.substr(0, quote_pos
);
859 if (system_type_
== SYSTEM_TYPE_VMS
)
860 line
= FtpUtil::VMSPathToUnix(line
);
861 if (line
.length() && line
[line
.length() - 1] == '/')
862 line
.erase(line
.length() - 1);
863 current_remote_directory_
= line
;
864 next_state_
= STATE_CTRL_WRITE_TYPE
;
867 case ERROR_CLASS_INFO_NEEDED
:
868 return Stop(ERR_INVALID_RESPONSE
);
869 case ERROR_CLASS_TRANSIENT_ERROR
:
870 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
871 case ERROR_CLASS_PERMANENT_ERROR
:
872 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
875 return Stop(ERR_UNEXPECTED
);
881 int FtpNetworkTransaction::DoCtrlWriteTYPE() {
882 std::string command
= "TYPE ";
883 if (data_type_
== DATA_TYPE_ASCII
) {
885 } else if (data_type_
== DATA_TYPE_IMAGE
) {
889 return Stop(ERR_UNEXPECTED
);
891 next_state_
= STATE_CTRL_READ
;
892 return SendFtpCommand(command
, COMMAND_TYPE
);
895 int FtpNetworkTransaction::ProcessResponseTYPE(
896 const FtpCtrlResponse
& response
) {
897 switch (GetErrorClass(response
.status_code
)) {
898 case ERROR_CLASS_INITIATED
:
899 return Stop(ERR_INVALID_RESPONSE
);
901 next_state_
= use_epsv_
? STATE_CTRL_WRITE_EPSV
: STATE_CTRL_WRITE_PASV
;
903 case ERROR_CLASS_INFO_NEEDED
:
904 return Stop(ERR_INVALID_RESPONSE
);
905 case ERROR_CLASS_TRANSIENT_ERROR
:
906 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
907 case ERROR_CLASS_PERMANENT_ERROR
:
908 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
911 return Stop(ERR_UNEXPECTED
);
917 int FtpNetworkTransaction::DoCtrlWriteEPSV() {
918 const std::string command
= "EPSV";
919 next_state_
= STATE_CTRL_READ
;
920 return SendFtpCommand(command
, COMMAND_EPSV
);
923 int FtpNetworkTransaction::ProcessResponseEPSV(
924 const FtpCtrlResponse
& response
) {
925 switch (GetErrorClass(response
.status_code
)) {
926 case ERROR_CLASS_INITIATED
:
927 return Stop(ERR_INVALID_RESPONSE
);
929 if (!ExtractPortFromEPSVResponse( response
, &data_connection_port_
))
930 return Stop(ERR_INVALID_RESPONSE
);
931 if (data_connection_port_
< 1024 ||
932 !IsPortAllowedByFtp(data_connection_port_
))
933 return Stop(ERR_UNSAFE_PORT
);
934 next_state_
= STATE_DATA_CONNECT
;
936 case ERROR_CLASS_INFO_NEEDED
:
937 return Stop(ERR_INVALID_RESPONSE
);
938 case ERROR_CLASS_TRANSIENT_ERROR
:
939 case ERROR_CLASS_PERMANENT_ERROR
:
941 next_state_
= STATE_CTRL_WRITE_PASV
;
945 return Stop(ERR_UNEXPECTED
);
951 int FtpNetworkTransaction::DoCtrlWritePASV() {
952 std::string command
= "PASV";
953 next_state_
= STATE_CTRL_READ
;
954 return SendFtpCommand(command
, COMMAND_PASV
);
957 int FtpNetworkTransaction::ProcessResponsePASV(
958 const FtpCtrlResponse
& response
) {
959 switch (GetErrorClass(response
.status_code
)) {
960 case ERROR_CLASS_INITIATED
:
961 return Stop(ERR_INVALID_RESPONSE
);
963 if (!ExtractPortFromPASVResponse(response
, &data_connection_port_
))
964 return Stop(ERR_INVALID_RESPONSE
);
965 if (data_connection_port_
< 1024 ||
966 !IsPortAllowedByFtp(data_connection_port_
))
967 return Stop(ERR_UNSAFE_PORT
);
968 next_state_
= STATE_DATA_CONNECT
;
970 case ERROR_CLASS_INFO_NEEDED
:
971 return Stop(ERR_INVALID_RESPONSE
);
972 case ERROR_CLASS_TRANSIENT_ERROR
:
973 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
974 case ERROR_CLASS_PERMANENT_ERROR
:
975 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
978 return Stop(ERR_UNEXPECTED
);
984 int FtpNetworkTransaction::DoCtrlWriteRETR() {
985 std::string command
= "RETR " + GetRequestPathForFtpCommand(false);
986 next_state_
= STATE_CTRL_READ
;
987 return SendFtpCommand(command
, COMMAND_RETR
);
990 int FtpNetworkTransaction::ProcessResponseRETR(
991 const FtpCtrlResponse
& response
) {
992 switch (GetErrorClass(response
.status_code
)) {
993 case ERROR_CLASS_INITIATED
:
994 // We want the client to start reading the response at this point.
995 // It got here either through Start or RestartWithAuth. We want that
996 // method to complete. Not setting next state here will make DoLoop exit
997 // and in turn make Start/RestartWithAuth complete.
998 resource_type_
= RESOURCE_TYPE_FILE
;
1000 case ERROR_CLASS_OK
:
1001 resource_type_
= RESOURCE_TYPE_FILE
;
1002 next_state_
= STATE_CTRL_WRITE_QUIT
;
1004 case ERROR_CLASS_INFO_NEEDED
:
1005 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1006 case ERROR_CLASS_TRANSIENT_ERROR
:
1007 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1008 case ERROR_CLASS_PERMANENT_ERROR
:
1009 // Code 550 means "Failed to open file". Other codes are unrelated,
1010 // like "Not logged in" etc.
1011 if (response
.status_code
!= 550 || resource_type_
== RESOURCE_TYPE_FILE
)
1012 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1014 // It's possible that RETR failed because the path is a directory.
1015 resource_type_
= RESOURCE_TYPE_DIRECTORY
;
1017 // We're going to try CWD next, but first send a PASV one more time,
1018 // because some FTP servers, including FileZilla, require that.
1019 // See http://crbug.com/25316.
1020 next_state_
= use_epsv_
? STATE_CTRL_WRITE_EPSV
: STATE_CTRL_WRITE_PASV
;
1024 return Stop(ERR_UNEXPECTED
);
1027 // We should be sure about our resource type now. Otherwise we risk
1028 // an infinite loop (RETR can later send CWD, and CWD can later send RETR).
1029 DCHECK_NE(RESOURCE_TYPE_UNKNOWN
, resource_type_
);
1035 int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1036 std::string command
= "SIZE " + GetRequestPathForFtpCommand(false);
1037 next_state_
= STATE_CTRL_READ
;
1038 return SendFtpCommand(command
, COMMAND_SIZE
);
1041 int FtpNetworkTransaction::ProcessResponseSIZE(
1042 const FtpCtrlResponse
& response
) {
1043 switch (GetErrorClass(response
.status_code
)) {
1044 case ERROR_CLASS_INITIATED
:
1046 case ERROR_CLASS_OK
:
1047 if (response
.lines
.size() != 1)
1048 return Stop(ERR_INVALID_RESPONSE
);
1050 if (!base::StringToInt64(response
.lines
[0], &size
))
1051 return Stop(ERR_INVALID_RESPONSE
);
1053 return Stop(ERR_INVALID_RESPONSE
);
1055 // A successful response to SIZE does not mean the resource is a file.
1056 // Some FTP servers (for example, the qnx one) send a SIZE even for
1058 response_
.expected_content_size
= size
;
1060 case ERROR_CLASS_INFO_NEEDED
:
1062 case ERROR_CLASS_TRANSIENT_ERROR
:
1064 case ERROR_CLASS_PERMANENT_ERROR
:
1065 // It's possible that SIZE failed because the path is a directory.
1066 if (resource_type_
== RESOURCE_TYPE_UNKNOWN
&&
1067 response
.status_code
!= 550) {
1068 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1073 return Stop(ERR_UNEXPECTED
);
1076 if (resource_type_
== RESOURCE_TYPE_FILE
)
1077 next_state_
= STATE_CTRL_WRITE_RETR
;
1079 next_state_
= STATE_CTRL_WRITE_CWD
;
1085 int FtpNetworkTransaction::DoCtrlWriteCWD() {
1086 std::string command
= "CWD " + GetRequestPathForFtpCommand(true);
1087 next_state_
= STATE_CTRL_READ
;
1088 return SendFtpCommand(command
, COMMAND_CWD
);
1091 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse
& response
) {
1092 // We should never issue CWD if we know the target resource is a file.
1093 DCHECK_NE(RESOURCE_TYPE_FILE
, resource_type_
);
1095 switch (GetErrorClass(response
.status_code
)) {
1096 case ERROR_CLASS_INITIATED
:
1097 return Stop(ERR_INVALID_RESPONSE
);
1098 case ERROR_CLASS_OK
:
1099 next_state_
= STATE_CTRL_WRITE_LIST
;
1101 case ERROR_CLASS_INFO_NEEDED
:
1102 return Stop(ERR_INVALID_RESPONSE
);
1103 case ERROR_CLASS_TRANSIENT_ERROR
:
1104 // Some FTP servers send response 451 (not a valid CWD response according
1105 // to RFC 959) instead of 550.
1106 if (response
.status_code
== 451)
1107 return ProcessResponseCWDNotADirectory();
1109 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1110 case ERROR_CLASS_PERMANENT_ERROR
:
1111 if (response
.status_code
== 550)
1112 return ProcessResponseCWDNotADirectory();
1114 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1117 return Stop(ERR_UNEXPECTED
);
1123 int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1124 if (resource_type_
== RESOURCE_TYPE_DIRECTORY
) {
1125 // We're assuming that the resource is a directory, but the server
1126 // says it's not true. The most probable interpretation is that it
1127 // doesn't exist (with FTP we can't be sure).
1128 return Stop(ERR_FILE_NOT_FOUND
);
1131 // We are here because SIZE failed and we are not sure what the resource
1132 // type is. It could still be file, and SIZE could fail because of
1133 // an access error (http://crbug.com/56734). Try RETR just to be sure.
1134 resource_type_
= RESOURCE_TYPE_FILE
;
1135 next_state_
= STATE_CTRL_WRITE_RETR
;
1141 int FtpNetworkTransaction::DoCtrlWriteLIST() {
1142 std::string
command(system_type_
== SYSTEM_TYPE_VMS
? "LIST *.*;0" : "LIST");
1143 next_state_
= STATE_CTRL_READ
;
1144 return SendFtpCommand(command
, COMMAND_LIST
);
1147 int FtpNetworkTransaction::ProcessResponseLIST(
1148 const FtpCtrlResponse
& response
) {
1149 switch (GetErrorClass(response
.status_code
)) {
1150 case ERROR_CLASS_INITIATED
:
1151 // We want the client to start reading the response at this point.
1152 // It got here either through Start or RestartWithAuth. We want that
1153 // method to complete. Not setting next state here will make DoLoop exit
1154 // and in turn make Start/RestartWithAuth complete.
1155 response_
.is_directory_listing
= true;
1157 case ERROR_CLASS_OK
:
1158 response_
.is_directory_listing
= true;
1159 next_state_
= STATE_CTRL_WRITE_QUIT
;
1161 case ERROR_CLASS_INFO_NEEDED
:
1162 return Stop(ERR_INVALID_RESPONSE
);
1163 case ERROR_CLASS_TRANSIENT_ERROR
:
1164 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1165 case ERROR_CLASS_PERMANENT_ERROR
:
1166 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1169 return Stop(ERR_UNEXPECTED
);
1175 int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1176 std::string command
= "QUIT";
1177 next_state_
= STATE_CTRL_READ
;
1178 return SendFtpCommand(command
, COMMAND_QUIT
);
1181 int FtpNetworkTransaction::ProcessResponseQUIT(
1182 const FtpCtrlResponse
& response
) {
1183 ctrl_socket_
->Disconnect();
1189 int FtpNetworkTransaction::DoDataConnect() {
1190 next_state_
= STATE_DATA_CONNECT_COMPLETE
;
1191 AddressList data_address
;
1192 // Connect to the same host as the control socket to prevent PASV port
1193 // scanning attacks.
1194 int rv
= ctrl_socket_
->GetPeerAddress(&data_address
);
1197 data_address
.SetPort(data_connection_port_
);
1198 data_socket_
.reset(socket_factory_
->CreateTransportClientSocket(
1199 data_address
, net_log_
.net_log(), net_log_
.source()));
1200 return data_socket_
->Connect(io_callback_
);
1203 int FtpNetworkTransaction::DoDataConnectComplete(int result
) {
1204 if (result
!= OK
&& use_epsv_
) {
1205 // It's possible we hit a broken server, sadly. They can break in different
1206 // ways. Some time out, some reset a connection. Fall back to PASV.
1207 // TODO(phajdan.jr): remember it for future transactions with this server.
1208 // TODO(phajdan.jr): write a test for this code path.
1210 next_state_
= STATE_CTRL_WRITE_PASV
;
1214 // Only record the connection error after we've applied all our fallbacks.
1215 // We want to capture the final error, one we're not going to recover from.
1216 RecordDataConnectionError(result
);
1219 return Stop(result
);
1221 next_state_
= STATE_CTRL_WRITE_SIZE
;
1225 int FtpNetworkTransaction::DoDataRead() {
1226 DCHECK(read_data_buf_
);
1227 DCHECK_GT(read_data_buf_len_
, 0);
1229 if (data_socket_
== NULL
|| !data_socket_
->IsConnected()) {
1230 // If we don't destroy the data socket completely, some servers will wait
1231 // for us (http://crbug.com/21127). The half-closed TCP connection needs
1232 // to be closed on our side too.
1233 data_socket_
.reset();
1235 if (ctrl_socket_
->IsConnected()) {
1236 // Wait for the server's response, we should get it before sending QUIT.
1237 next_state_
= STATE_CTRL_READ
;
1241 // We are no longer connected to the server, so just finish the transaction.
1245 next_state_
= STATE_DATA_READ_COMPLETE
;
1246 read_data_buf_
->data()[0] = 0;
1247 return data_socket_
->Read(read_data_buf_
, read_data_buf_len_
, io_callback_
);
1250 int FtpNetworkTransaction::DoDataReadComplete(int result
) {
1254 // We're using a histogram as a group of counters, with one bucket for each
1255 // enumeration value. We're only interested in the values of the counters.
1256 // Ignore the shape, average, and standard deviation of the histograms because
1257 // they are meaningless.
1259 // We use two histograms. In the first histogram we tally whether the user has
1260 // seen an error of that type during the session. In the second histogram we
1261 // tally the total number of times the users sees each errer.
1262 void FtpNetworkTransaction::RecordDataConnectionError(int result
) {
1263 // Gather data for http://crbug.com/3073. See how many users have trouble
1264 // establishing FTP data connection in passive FTP mode.
1266 // Data connection successful.
1269 // Local firewall blocked the connection.
1270 NET_ERROR_ACCESS_DENIED
= 1,
1272 // Connection timed out.
1273 NET_ERROR_TIMED_OUT
= 2,
1275 // Connection has been estabilished, but then got broken (either reset
1277 NET_ERROR_CONNECTION_BROKEN
= 3,
1279 // Connection has been refused.
1280 NET_ERROR_CONNECTION_REFUSED
= 4,
1282 // No connection to the internet.
1283 NET_ERROR_INTERNET_DISCONNECTED
= 5,
1285 // Could not reach the destination address.
1286 NET_ERROR_ADDRESS_UNREACHABLE
= 6,
1288 // A programming error in our network stack.
1289 NET_ERROR_UNEXPECTED
= 7,
1291 // Other kind of error.
1292 NET_ERROR_OTHER
= 20,
1294 NUM_OF_NET_ERROR_TYPES
1298 type
= NET_ERROR_OK
;
1300 case ERR_ACCESS_DENIED
:
1301 case ERR_NETWORK_ACCESS_DENIED
:
1302 type
= NET_ERROR_ACCESS_DENIED
;
1305 type
= NET_ERROR_TIMED_OUT
;
1307 case ERR_CONNECTION_ABORTED
:
1308 case ERR_CONNECTION_RESET
:
1309 case ERR_CONNECTION_CLOSED
:
1310 type
= NET_ERROR_CONNECTION_BROKEN
;
1312 case ERR_CONNECTION_FAILED
:
1313 case ERR_CONNECTION_REFUSED
:
1314 type
= NET_ERROR_CONNECTION_REFUSED
;
1316 case ERR_INTERNET_DISCONNECTED
:
1317 type
= NET_ERROR_INTERNET_DISCONNECTED
;
1319 case ERR_ADDRESS_INVALID
:
1320 case ERR_ADDRESS_UNREACHABLE
:
1321 type
= NET_ERROR_ADDRESS_UNREACHABLE
;
1323 case ERR_UNEXPECTED
:
1324 type
= NET_ERROR_UNEXPECTED
;
1327 type
= NET_ERROR_OTHER
;
1330 static bool had_error_type
[NUM_OF_NET_ERROR_TYPES
];
1332 DCHECK(type
>= 0 && type
< NUM_OF_NET_ERROR_TYPES
);
1333 if (!had_error_type
[type
]) {
1334 had_error_type
[type
] = true;
1335 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1336 type
, NUM_OF_NET_ERROR_TYPES
);
1338 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1339 type
, NUM_OF_NET_ERROR_TYPES
);