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
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_macros.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/values.h"
16 #include "net/base/address_list.h"
17 #include "net/base/connection_type_histograms.h"
18 #include "net/base/escape.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/net_util.h"
21 #include "net/base/port_util.h"
22 #include "net/ftp/ftp_request_info.h"
23 #include "net/ftp/ftp_util.h"
24 #include "net/log/net_log.h"
25 #include "net/socket/client_socket_factory.h"
26 #include "net/socket/stream_socket.h"
27 #include "url/url_constants.h"
33 const char kCRLF
[] = "\r\n";
35 const int kCtrlBufLen
= 1024;
37 // Returns true if |input| can be safely used as a part of FTP command.
38 bool IsValidFTPCommandString(const std::string
& input
) {
39 // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
40 // characters in the command if the request path contains them. To be
41 // compatible, we do the same and allow non-ASCII characters in a command.
43 // Protect agains newline injection attack.
44 if (input
.find_first_of("\r\n") != std::string::npos
)
51 // The requested action was initiated. The client should expect another
52 // reply before issuing the next command.
53 ERROR_CLASS_INITIATED
,
55 // The requested action has been successfully completed.
58 // The command has been accepted, but to complete the operation, more
59 // information must be sent by the client.
60 ERROR_CLASS_INFO_NEEDED
,
62 // The command was not accepted and the requested action did not take place.
63 // This condition is temporary, and the client is encouraged to restart the
65 ERROR_CLASS_TRANSIENT_ERROR
,
67 // The command was not accepted and the requested action did not take place.
68 // This condition is rather permanent, and the client is discouraged from
69 // repeating the exact request.
70 ERROR_CLASS_PERMANENT_ERROR
,
73 // Returns the error class for given response code. Caller should ensure
74 // that |response_code| is in range 100-599.
75 ErrorClass
GetErrorClass(int response_code
) {
76 if (response_code
>= 100 && response_code
<= 199)
77 return ERROR_CLASS_INITIATED
;
79 if (response_code
>= 200 && response_code
<= 299)
80 return ERROR_CLASS_OK
;
82 if (response_code
>= 300 && response_code
<= 399)
83 return ERROR_CLASS_INFO_NEEDED
;
85 if (response_code
>= 400 && response_code
<= 499)
86 return ERROR_CLASS_TRANSIENT_ERROR
;
88 if (response_code
>= 500 && response_code
<= 599)
89 return ERROR_CLASS_PERMANENT_ERROR
;
91 // We should not be called on invalid error codes.
92 NOTREACHED() << response_code
;
93 return ERROR_CLASS_PERMANENT_ERROR
;
96 // Returns network error code for received FTP |response_code|.
97 int GetNetErrorCodeForFtpResponseCode(int response_code
) {
98 switch (response_code
) {
100 return ERR_FTP_SERVICE_UNAVAILABLE
;
102 return ERR_FTP_TRANSFER_ABORTED
;
104 return ERR_FTP_FILE_BUSY
;
107 return ERR_FTP_SYNTAX_ERROR
;
110 return ERR_FTP_COMMAND_NOT_SUPPORTED
;
112 return ERR_FTP_BAD_COMMAND_SEQUENCE
;
114 return ERR_FTP_FAILED
;
118 // From RFC 2428 Section 3:
119 // The text returned in response to the EPSV command MUST be:
120 // <some text> (<d><d><d><tcp-port><d>)
121 // <d> is a delimiter character, ideally to be |
122 bool ExtractPortFromEPSVResponse(const FtpCtrlResponse
& response
, int* port
) {
123 if (response
.lines
.size() != 1)
125 const char* ptr
= response
.lines
[0].c_str();
126 while (*ptr
&& *ptr
!= '(')
131 if (!sep
|| isdigit(sep
) || *(++ptr
) != sep
|| *(++ptr
) != sep
)
133 if (!isdigit(*(++ptr
)))
136 while (isdigit(*(++ptr
))) {
146 // There are two way we can receive IP address and port.
147 // (127,0,0,1,23,21) IP address and port encapsulated in ().
148 // 127,0,0,1,23,21 IP address and port without ().
150 // See RFC 959, Section 4.1.2
151 bool ExtractPortFromPASVResponse(const FtpCtrlResponse
& response
, int* port
) {
152 if (response
.lines
.size() != 1)
155 std::string
line(response
.lines
[0]);
156 if (!base::IsStringASCII(line
))
158 if (line
.length() < 2)
161 size_t paren_pos
= line
.find('(');
162 if (paren_pos
== std::string::npos
) {
163 // Find the first comma and use it to locate the beginning
164 // of the response data.
165 size_t comma_pos
= line
.find(',');
166 if (comma_pos
== std::string::npos
)
169 size_t space_pos
= line
.rfind(' ', comma_pos
);
170 if (space_pos
!= std::string::npos
)
171 line
= line
.substr(space_pos
+ 1);
173 // Remove the parentheses and use the text inside them.
174 size_t closing_paren_pos
= line
.rfind(')');
175 if (closing_paren_pos
== std::string::npos
)
177 if (closing_paren_pos
<= paren_pos
)
180 line
= line
.substr(paren_pos
+ 1, closing_paren_pos
- paren_pos
- 1);
183 // Split the line into comma-separated pieces and extract
185 std::vector
<base::StringPiece
> pieces
= base::SplitStringPiece(
186 line
, ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
187 if (pieces
.size() != 6)
190 // Ignore the IP address supplied in the response. We are always going
191 // to connect back to the same server to prevent FTP PASV port scanning.
193 if (!base::StringToInt(pieces
[4], &p0
))
195 if (!base::StringToInt(pieces
[5], &p1
))
197 *port
= (p0
<< 8) + p1
;
204 FtpNetworkTransaction::FtpNetworkTransaction(
205 HostResolver
* resolver
,
206 ClientSocketFactory
* socket_factory
)
207 : command_sent_(COMMAND_NONE
),
208 io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete
,
209 base::Unretained(this))),
212 read_ctrl_buf_(new IOBuffer(kCtrlBufLen
)),
213 read_data_buf_len_(0),
215 system_type_(SYSTEM_TYPE_UNKNOWN
),
216 // Use image (binary) transfer by default. It should always work,
217 // whereas the ascii transfer may damage binary data.
218 data_type_(DATA_TYPE_IMAGE
),
219 resource_type_(RESOURCE_TYPE_UNKNOWN
),
221 data_connection_port_(0),
222 socket_factory_(socket_factory
),
223 next_state_(STATE_NONE
),
224 state_after_data_connect_complete_(STATE_NONE
) {
227 FtpNetworkTransaction::~FtpNetworkTransaction() {
230 int FtpNetworkTransaction::Stop(int error
) {
231 if (command_sent_
== COMMAND_QUIT
)
234 next_state_
= STATE_CTRL_WRITE_QUIT
;
239 int FtpNetworkTransaction::Start(const FtpRequestInfo
* request_info
,
240 const CompletionCallback
& callback
,
241 const BoundNetLog
& net_log
) {
243 request_
= request_info
;
245 ctrl_response_buffer_
.reset(new FtpCtrlResponseBuffer(net_log_
));
247 if (request_
->url
.has_username()) {
248 base::string16 username
;
249 base::string16 password
;
250 GetIdentityFromURL(request_
->url
, &username
, &password
);
251 credentials_
.Set(username
, password
);
253 credentials_
.Set(base::ASCIIToUTF16("anonymous"),
254 base::ASCIIToUTF16("chrome@example.com"));
259 next_state_
= STATE_CTRL_RESOLVE_HOST
;
261 if (rv
== ERR_IO_PENDING
)
262 user_callback_
= callback
;
266 int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials
& credentials
,
267 const CompletionCallback
& callback
) {
268 ResetStateForRestart();
270 credentials_
= credentials
;
272 next_state_
= STATE_CTRL_RESOLVE_HOST
;
274 if (rv
== ERR_IO_PENDING
)
275 user_callback_
= callback
;
279 int FtpNetworkTransaction::Read(IOBuffer
* buf
,
281 const CompletionCallback
& callback
) {
283 DCHECK_GT(buf_len
, 0);
285 read_data_buf_
= buf
;
286 read_data_buf_len_
= buf_len
;
288 next_state_
= STATE_DATA_READ
;
290 if (rv
== ERR_IO_PENDING
)
291 user_callback_
= callback
;
295 const FtpResponseInfo
* FtpNetworkTransaction::GetResponseInfo() const {
299 LoadState
FtpNetworkTransaction::GetLoadState() const {
300 if (next_state_
== STATE_CTRL_RESOLVE_HOST_COMPLETE
)
301 return LOAD_STATE_RESOLVING_HOST
;
303 if (next_state_
== STATE_CTRL_CONNECT_COMPLETE
||
304 next_state_
== STATE_DATA_CONNECT_COMPLETE
)
305 return LOAD_STATE_CONNECTING
;
307 if (next_state_
== STATE_DATA_READ_COMPLETE
)
308 return LOAD_STATE_READING_RESPONSE
;
310 if (command_sent_
== COMMAND_RETR
&& read_data_buf_
.get())
311 return LOAD_STATE_READING_RESPONSE
;
313 if (command_sent_
== COMMAND_QUIT
)
314 return LOAD_STATE_IDLE
;
316 if (command_sent_
!= COMMAND_NONE
)
317 return LOAD_STATE_SENDING_REQUEST
;
319 return LOAD_STATE_IDLE
;
322 uint64
FtpNetworkTransaction::GetUploadProgress() const {
326 void FtpNetworkTransaction::ResetStateForRestart() {
327 command_sent_
= COMMAND_NONE
;
328 user_callback_
.Reset();
329 response_
= FtpResponseInfo();
330 read_ctrl_buf_
= new IOBuffer(kCtrlBufLen
);
331 ctrl_response_buffer_
.reset(new FtpCtrlResponseBuffer(net_log_
));
332 read_data_buf_
= NULL
;
333 read_data_buf_len_
= 0;
334 if (write_buf_
.get())
335 write_buf_
->SetOffset(0);
337 data_connection_port_
= 0;
338 ctrl_socket_
.reset();
339 data_socket_
.reset();
340 next_state_
= STATE_NONE
;
341 state_after_data_connect_complete_
= STATE_NONE
;
344 void FtpNetworkTransaction::EstablishDataConnection(State state_after_connect
) {
345 DCHECK(state_after_connect
== STATE_CTRL_WRITE_RETR
||
346 state_after_connect
== STATE_CTRL_WRITE_LIST
);
347 state_after_data_connect_complete_
= state_after_connect
;
348 next_state_
= use_epsv_
? STATE_CTRL_WRITE_EPSV
: STATE_CTRL_WRITE_PASV
;
351 void FtpNetworkTransaction::DoCallback(int rv
) {
352 DCHECK(rv
!= ERR_IO_PENDING
);
353 DCHECK(!user_callback_
.is_null());
355 // Since Run may result in Read being called, clear callback_ up front.
356 CompletionCallback c
= user_callback_
;
357 user_callback_
.Reset();
361 void FtpNetworkTransaction::OnIOComplete(int result
) {
362 int rv
= DoLoop(result
);
363 if (rv
!= ERR_IO_PENDING
)
367 int FtpNetworkTransaction::ProcessCtrlResponse() {
368 FtpCtrlResponse response
= ctrl_response_buffer_
->PopResponse();
371 switch (command_sent_
) {
373 // TODO(phajdan.jr): https://crbug.com/526721: Check for errors in the
375 next_state_
= STATE_CTRL_WRITE_USER
;
378 rv
= ProcessResponseUSER(response
);
381 rv
= ProcessResponsePASS(response
);
384 rv
= ProcessResponseSYST(response
);
387 rv
= ProcessResponsePWD(response
);
390 rv
= ProcessResponseTYPE(response
);
393 rv
= ProcessResponseEPSV(response
);
396 rv
= ProcessResponsePASV(response
);
399 rv
= ProcessResponseSIZE(response
);
402 rv
= ProcessResponseRETR(response
);
405 rv
= ProcessResponseCWD(response
);
408 rv
= ProcessResponseLIST(response
);
411 rv
= ProcessResponseQUIT(response
);
414 LOG(DFATAL
) << "Unexpected value of command_sent_: " << command_sent_
;
415 return ERR_UNEXPECTED
;
418 // We may get multiple responses for some commands,
419 // see http://crbug.com/18036.
420 while (ctrl_response_buffer_
->ResponseAvailable() && rv
== OK
) {
421 response
= ctrl_response_buffer_
->PopResponse();
423 switch (command_sent_
) {
425 rv
= ProcessResponseRETR(response
);
428 rv
= ProcessResponseLIST(response
);
431 // Multiple responses for other commands are invalid.
432 return Stop(ERR_INVALID_RESPONSE
);
439 // Used to prepare and send FTP command.
440 int FtpNetworkTransaction::SendFtpCommand(const std::string
& command
,
441 const std::string
& command_for_log
,
443 // If we send a new command when we still have unprocessed responses
444 // for previous commands, the response receiving code will have no way to know
445 // which responses are for which command.
446 DCHECK(!ctrl_response_buffer_
->ResponseAvailable());
448 DCHECK(!write_command_buf_
.get());
449 DCHECK(!write_buf_
.get());
451 if (!IsValidFTPCommandString(command
)) {
452 // Callers should validate the command themselves and return a more specific
455 return Stop(ERR_UNEXPECTED
);
460 write_command_buf_
= new IOBufferWithSize(command
.length() + 2);
461 write_buf_
= new DrainableIOBuffer(write_command_buf_
.get(),
462 write_command_buf_
->size());
463 memcpy(write_command_buf_
->data(), command
.data(), command
.length());
464 memcpy(write_command_buf_
->data() + command
.length(), kCRLF
, 2);
466 net_log_
.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT
,
467 NetLog::StringCallback("command", &command_for_log
));
469 next_state_
= STATE_CTRL_WRITE
;
473 std::string
FtpNetworkTransaction::GetRequestPathForFtpCommand(
474 bool is_directory
) const {
475 std::string
path(current_remote_directory_
);
476 if (request_
->url
.has_path()) {
477 std::string
gurl_path(request_
->url
.path());
479 // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
480 std::string::size_type pos
= gurl_path
.rfind(';');
481 if (pos
!= std::string::npos
)
482 gurl_path
.resize(pos
);
484 path
.append(gurl_path
);
486 // Make sure that if the path is expected to be a file, it won't end
487 // with a trailing slash.
488 if (!is_directory
&& path
.length() > 1 && path
[path
.length() - 1] == '/')
489 path
.erase(path
.length() - 1);
490 UnescapeRule::Type unescape_rules
= UnescapeRule::SPACES
|
491 UnescapeRule::URL_SPECIAL_CHARS
;
492 // This may unescape to non-ASCII characters, but we allow that. See the
493 // comment for IsValidFTPCommandString.
494 path
= UnescapeURLComponent(path
, unescape_rules
);
496 if (system_type_
== SYSTEM_TYPE_VMS
) {
498 path
= FtpUtil::UnixDirectoryPathToVMS(path
);
500 path
= FtpUtil::UnixFilePathToVMS(path
);
503 DCHECK(IsValidFTPCommandString(path
));
507 void FtpNetworkTransaction::DetectTypecode() {
508 if (!request_
->url
.has_path())
510 std::string
gurl_path(request_
->url
.path());
512 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
513 std::string::size_type pos
= gurl_path
.rfind(';');
514 if (pos
== std::string::npos
)
516 std::string
typecode_string(gurl_path
.substr(pos
));
517 if (typecode_string
== ";type=a") {
518 data_type_
= DATA_TYPE_ASCII
;
519 resource_type_
= RESOURCE_TYPE_FILE
;
520 } else if (typecode_string
== ";type=i") {
521 data_type_
= DATA_TYPE_IMAGE
;
522 resource_type_
= RESOURCE_TYPE_FILE
;
523 } else if (typecode_string
== ";type=d") {
524 resource_type_
= RESOURCE_TYPE_DIRECTORY
;
528 int FtpNetworkTransaction::DoLoop(int result
) {
529 DCHECK(next_state_
!= STATE_NONE
);
533 State state
= next_state_
;
534 next_state_
= STATE_NONE
;
536 case STATE_CTRL_RESOLVE_HOST
:
538 rv
= DoCtrlResolveHost();
540 case STATE_CTRL_RESOLVE_HOST_COMPLETE
:
541 rv
= DoCtrlResolveHostComplete(rv
);
543 case STATE_CTRL_CONNECT
:
545 rv
= DoCtrlConnect();
547 case STATE_CTRL_CONNECT_COMPLETE
:
548 rv
= DoCtrlConnectComplete(rv
);
550 case STATE_CTRL_READ
:
554 case STATE_CTRL_READ_COMPLETE
:
555 rv
= DoCtrlReadComplete(rv
);
557 case STATE_CTRL_WRITE
:
561 case STATE_CTRL_WRITE_COMPLETE
:
562 rv
= DoCtrlWriteComplete(rv
);
564 case STATE_CTRL_WRITE_USER
:
566 rv
= DoCtrlWriteUSER();
568 case STATE_CTRL_WRITE_PASS
:
570 rv
= DoCtrlWritePASS();
572 case STATE_CTRL_WRITE_SYST
:
574 rv
= DoCtrlWriteSYST();
576 case STATE_CTRL_WRITE_PWD
:
578 rv
= DoCtrlWritePWD();
580 case STATE_CTRL_WRITE_TYPE
:
582 rv
= DoCtrlWriteTYPE();
584 case STATE_CTRL_WRITE_EPSV
:
586 rv
= DoCtrlWriteEPSV();
588 case STATE_CTRL_WRITE_PASV
:
590 rv
= DoCtrlWritePASV();
592 case STATE_CTRL_WRITE_RETR
:
594 rv
= DoCtrlWriteRETR();
596 case STATE_CTRL_WRITE_SIZE
:
598 rv
= DoCtrlWriteSIZE();
600 case STATE_CTRL_WRITE_CWD
:
602 rv
= DoCtrlWriteCWD();
604 case STATE_CTRL_WRITE_LIST
:
606 rv
= DoCtrlWriteLIST();
608 case STATE_CTRL_WRITE_QUIT
:
610 rv
= DoCtrlWriteQUIT();
612 case STATE_DATA_CONNECT
:
614 rv
= DoDataConnect();
616 case STATE_DATA_CONNECT_COMPLETE
:
617 rv
= DoDataConnectComplete(rv
);
619 case STATE_DATA_READ
:
623 case STATE_DATA_READ_COMPLETE
:
624 rv
= DoDataReadComplete(rv
);
627 NOTREACHED() << "bad state";
631 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
635 int FtpNetworkTransaction::DoCtrlResolveHost() {
636 next_state_
= STATE_CTRL_RESOLVE_HOST_COMPLETE
;
638 HostResolver::RequestInfo
info(HostPortPair::FromURL(request_
->url
));
639 // No known referrer.
640 return resolver_
.Resolve(
644 base::Bind(&FtpNetworkTransaction::OnIOComplete
, base::Unretained(this)),
648 int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result
) {
650 next_state_
= STATE_CTRL_CONNECT
;
654 int FtpNetworkTransaction::DoCtrlConnect() {
655 next_state_
= STATE_CTRL_CONNECT_COMPLETE
;
656 ctrl_socket_
= socket_factory_
->CreateTransportClientSocket(
657 addresses_
, net_log_
.net_log(), net_log_
.source());
659 NetLog::TYPE_FTP_CONTROL_CONNECTION
,
660 ctrl_socket_
->NetLog().source().ToEventParametersCallback());
661 return ctrl_socket_
->Connect(io_callback_
);
664 int FtpNetworkTransaction::DoCtrlConnectComplete(int result
) {
666 // Put the peer's IP address and port into the response.
667 IPEndPoint ip_endpoint
;
668 result
= ctrl_socket_
->GetPeerAddress(&ip_endpoint
);
670 response_
.socket_address
= HostPortPair::FromIPEndPoint(ip_endpoint
);
671 next_state_
= STATE_CTRL_READ
;
673 if (ip_endpoint
.GetFamily() == ADDRESS_FAMILY_IPV4
) {
674 // Do not use EPSV for IPv4 connections. Some servers become confused
675 // and we time out while waiting to connect. PASV is perfectly fine for
676 // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
677 // whitelisting IPv6 to use it, to make the code more future-proof:
678 // all future protocols should just use EPSV.
686 int FtpNetworkTransaction::DoCtrlRead() {
687 next_state_
= STATE_CTRL_READ_COMPLETE
;
688 return ctrl_socket_
->Read(read_ctrl_buf_
.get(), kCtrlBufLen
, io_callback_
);
691 int FtpNetworkTransaction::DoCtrlReadComplete(int result
) {
693 // Some servers (for example Pure-FTPd) apparently close the control
694 // connection when anonymous login is not permitted. For more details
695 // see http://crbug.com/25023.
696 if (command_sent_
== COMMAND_USER
&&
697 credentials_
.username() == base::ASCIIToUTF16("anonymous")) {
698 response_
.needs_auth
= true;
700 return Stop(ERR_EMPTY_RESPONSE
);
705 ctrl_response_buffer_
->ConsumeData(read_ctrl_buf_
->data(), result
);
707 if (!ctrl_response_buffer_
->ResponseAvailable()) {
708 // Read more data from the control socket.
709 next_state_
= STATE_CTRL_READ
;
713 return ProcessCtrlResponse();
716 int FtpNetworkTransaction::DoCtrlWrite() {
717 next_state_
= STATE_CTRL_WRITE_COMPLETE
;
719 return ctrl_socket_
->Write(
720 write_buf_
.get(), write_buf_
->BytesRemaining(), io_callback_
);
723 int FtpNetworkTransaction::DoCtrlWriteComplete(int result
) {
727 write_buf_
->DidConsume(result
);
728 if (write_buf_
->BytesRemaining() == 0) {
729 // Clear the write buffer.
731 write_command_buf_
= NULL
;
733 next_state_
= STATE_CTRL_READ
;
735 next_state_
= STATE_CTRL_WRITE
;
740 // FTP Commands and responses
743 int FtpNetworkTransaction::DoCtrlWriteUSER() {
744 std::string command
= "USER " + base::UTF16ToUTF8(credentials_
.username());
746 if (!IsValidFTPCommandString(command
))
747 return Stop(ERR_MALFORMED_IDENTITY
);
749 next_state_
= STATE_CTRL_READ
;
750 return SendFtpCommand(command
, "USER ***", COMMAND_USER
);
753 int FtpNetworkTransaction::ProcessResponseUSER(
754 const FtpCtrlResponse
& response
) {
755 switch (GetErrorClass(response
.status_code
)) {
757 next_state_
= STATE_CTRL_WRITE_SYST
;
759 case ERROR_CLASS_INFO_NEEDED
:
760 next_state_
= STATE_CTRL_WRITE_PASS
;
762 case ERROR_CLASS_TRANSIENT_ERROR
:
763 case ERROR_CLASS_PERMANENT_ERROR
:
764 response_
.needs_auth
= true;
765 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
768 return Stop(ERR_UNEXPECTED
);
774 int FtpNetworkTransaction::DoCtrlWritePASS() {
775 std::string command
= "PASS " + base::UTF16ToUTF8(credentials_
.password());
777 if (!IsValidFTPCommandString(command
))
778 return Stop(ERR_MALFORMED_IDENTITY
);
780 next_state_
= STATE_CTRL_READ
;
781 return SendFtpCommand(command
, "PASS ***", COMMAND_PASS
);
784 int FtpNetworkTransaction::ProcessResponsePASS(
785 const FtpCtrlResponse
& response
) {
786 switch (GetErrorClass(response
.status_code
)) {
788 next_state_
= STATE_CTRL_WRITE_SYST
;
790 case ERROR_CLASS_INFO_NEEDED
:
791 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
792 case ERROR_CLASS_TRANSIENT_ERROR
:
793 case ERROR_CLASS_PERMANENT_ERROR
:
794 response_
.needs_auth
= true;
795 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
798 return Stop(ERR_UNEXPECTED
);
804 int FtpNetworkTransaction::DoCtrlWriteSYST() {
805 std::string command
= "SYST";
806 next_state_
= STATE_CTRL_READ
;
807 return SendFtpCommand(command
, command
, COMMAND_SYST
);
810 int FtpNetworkTransaction::ProcessResponseSYST(
811 const FtpCtrlResponse
& response
) {
812 switch (GetErrorClass(response
.status_code
)) {
813 case ERROR_CLASS_INITIATED
:
814 return Stop(ERR_INVALID_RESPONSE
);
815 case ERROR_CLASS_OK
: {
816 // All important info should be on the first line.
817 std::string line
= response
.lines
[0];
818 // The response should be ASCII, which allows us to do case-insensitive
819 // comparisons easily. If it is not ASCII, we leave the system type
821 if (base::IsStringASCII(line
)) {
822 line
= base::ToLowerASCII(line
);
824 // Remove all whitespace, to correctly handle cases like fancy "V M S"
825 // response instead of "VMS".
826 base::RemoveChars(line
, base::kWhitespaceASCII
, &line
);
828 // The "magic" strings we test for below have been gathered by an
829 // empirical study. VMS needs to come first because some VMS systems
830 // also respond with "UNIX emulation", which is not perfect. It is much
831 // more reliable to talk to these servers in their native language.
832 if (line
.find("vms") != std::string::npos
) {
833 system_type_
= SYSTEM_TYPE_VMS
;
834 } else if (line
.find("l8") != std::string::npos
||
835 line
.find("unix") != std::string::npos
||
836 line
.find("bsd") != std::string::npos
) {
837 system_type_
= SYSTEM_TYPE_UNIX
;
838 } else if (line
.find("win32") != std::string::npos
||
839 line
.find("windows") != std::string::npos
) {
840 system_type_
= SYSTEM_TYPE_WINDOWS
;
841 } else if (line
.find("os/2") != std::string::npos
) {
842 system_type_
= SYSTEM_TYPE_OS2
;
845 next_state_
= STATE_CTRL_WRITE_PWD
;
848 case ERROR_CLASS_INFO_NEEDED
:
849 return Stop(ERR_INVALID_RESPONSE
);
850 case ERROR_CLASS_TRANSIENT_ERROR
:
851 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
852 case ERROR_CLASS_PERMANENT_ERROR
:
853 // Server does not recognize the SYST command so proceed.
854 next_state_
= STATE_CTRL_WRITE_PWD
;
858 return Stop(ERR_UNEXPECTED
);
864 int FtpNetworkTransaction::DoCtrlWritePWD() {
865 std::string command
= "PWD";
866 next_state_
= STATE_CTRL_READ
;
867 return SendFtpCommand(command
, command
, COMMAND_PWD
);
870 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse
& response
) {
871 switch (GetErrorClass(response
.status_code
)) {
872 case ERROR_CLASS_INITIATED
:
873 return Stop(ERR_INVALID_RESPONSE
);
874 case ERROR_CLASS_OK
: {
875 // The info we look for should be on the first line.
876 std::string line
= response
.lines
[0];
878 return Stop(ERR_INVALID_RESPONSE
);
879 std::string::size_type quote_pos
= line
.find('"');
880 if (quote_pos
!= std::string::npos
) {
881 line
= line
.substr(quote_pos
+ 1);
882 quote_pos
= line
.find('"');
883 if (quote_pos
== std::string::npos
)
884 return Stop(ERR_INVALID_RESPONSE
);
885 line
= line
.substr(0, quote_pos
);
887 if (system_type_
== SYSTEM_TYPE_VMS
)
888 line
= FtpUtil::VMSPathToUnix(line
);
889 if (line
.length() && line
[line
.length() - 1] == '/')
890 line
.erase(line
.length() - 1);
891 current_remote_directory_
= line
;
892 next_state_
= STATE_CTRL_WRITE_TYPE
;
895 case ERROR_CLASS_INFO_NEEDED
:
896 return Stop(ERR_INVALID_RESPONSE
);
897 case ERROR_CLASS_TRANSIENT_ERROR
:
898 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
899 case ERROR_CLASS_PERMANENT_ERROR
:
900 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
903 return Stop(ERR_UNEXPECTED
);
909 int FtpNetworkTransaction::DoCtrlWriteTYPE() {
910 std::string command
= "TYPE ";
911 if (data_type_
== DATA_TYPE_ASCII
) {
913 } else if (data_type_
== DATA_TYPE_IMAGE
) {
917 return Stop(ERR_UNEXPECTED
);
919 next_state_
= STATE_CTRL_READ
;
920 return SendFtpCommand(command
, command
, COMMAND_TYPE
);
923 int FtpNetworkTransaction::ProcessResponseTYPE(
924 const FtpCtrlResponse
& response
) {
925 switch (GetErrorClass(response
.status_code
)) {
926 case ERROR_CLASS_INITIATED
:
927 return Stop(ERR_INVALID_RESPONSE
);
929 next_state_
= STATE_CTRL_WRITE_SIZE
;
931 case ERROR_CLASS_INFO_NEEDED
:
932 return Stop(ERR_INVALID_RESPONSE
);
933 case ERROR_CLASS_TRANSIENT_ERROR
:
934 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
935 case ERROR_CLASS_PERMANENT_ERROR
:
936 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
939 return Stop(ERR_UNEXPECTED
);
945 int FtpNetworkTransaction::DoCtrlWriteEPSV() {
946 const std::string command
= "EPSV";
947 next_state_
= STATE_CTRL_READ
;
948 return SendFtpCommand(command
, command
, COMMAND_EPSV
);
951 int FtpNetworkTransaction::ProcessResponseEPSV(
952 const FtpCtrlResponse
& response
) {
953 switch (GetErrorClass(response
.status_code
)) {
954 case ERROR_CLASS_INITIATED
:
955 return Stop(ERR_INVALID_RESPONSE
);
956 case ERROR_CLASS_OK
: {
958 if (!ExtractPortFromEPSVResponse(response
, &port
))
959 return Stop(ERR_INVALID_RESPONSE
);
960 if (IsWellKnownPort(port
) ||
961 !IsPortAllowedForScheme(port
, url::kFtpScheme
)) {
962 return Stop(ERR_UNSAFE_PORT
);
964 data_connection_port_
= static_cast<uint16
>(port
);
965 next_state_
= STATE_DATA_CONNECT
;
968 case ERROR_CLASS_INFO_NEEDED
:
969 return Stop(ERR_INVALID_RESPONSE
);
970 case ERROR_CLASS_TRANSIENT_ERROR
:
971 case ERROR_CLASS_PERMANENT_ERROR
:
973 next_state_
= STATE_CTRL_WRITE_PASV
;
977 return Stop(ERR_UNEXPECTED
);
983 int FtpNetworkTransaction::DoCtrlWritePASV() {
984 std::string command
= "PASV";
985 next_state_
= STATE_CTRL_READ
;
986 return SendFtpCommand(command
, command
, COMMAND_PASV
);
989 int FtpNetworkTransaction::ProcessResponsePASV(
990 const FtpCtrlResponse
& response
) {
991 switch (GetErrorClass(response
.status_code
)) {
992 case ERROR_CLASS_INITIATED
:
993 return Stop(ERR_INVALID_RESPONSE
);
994 case ERROR_CLASS_OK
: {
996 if (!ExtractPortFromPASVResponse(response
, &port
))
997 return Stop(ERR_INVALID_RESPONSE
);
998 if (IsWellKnownPort(port
) ||
999 !IsPortAllowedForScheme(port
, url::kFtpScheme
)) {
1000 return Stop(ERR_UNSAFE_PORT
);
1002 data_connection_port_
= static_cast<uint16
>(port
);
1003 next_state_
= STATE_DATA_CONNECT
;
1006 case ERROR_CLASS_INFO_NEEDED
:
1007 return Stop(ERR_INVALID_RESPONSE
);
1008 case ERROR_CLASS_TRANSIENT_ERROR
:
1009 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1010 case ERROR_CLASS_PERMANENT_ERROR
:
1011 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1014 return Stop(ERR_UNEXPECTED
);
1020 int FtpNetworkTransaction::DoCtrlWriteRETR() {
1021 std::string command
= "RETR " + GetRequestPathForFtpCommand(false);
1022 next_state_
= STATE_CTRL_READ
;
1023 return SendFtpCommand(command
, command
, COMMAND_RETR
);
1026 int FtpNetworkTransaction::ProcessResponseRETR(
1027 const FtpCtrlResponse
& response
) {
1028 // Resource type should be either filled in by DetectTypecode() or
1029 // detected with CWD. RETR is sent only when the resource is a file.
1030 DCHECK_EQ(RESOURCE_TYPE_FILE
, resource_type_
);
1032 switch (GetErrorClass(response
.status_code
)) {
1033 case ERROR_CLASS_INITIATED
:
1034 // We want the client to start reading the response at this point.
1035 // It got here either through Start or RestartWithAuth. We want that
1036 // method to complete. Not setting next state here will make DoLoop exit
1037 // and in turn make Start/RestartWithAuth complete.
1039 case ERROR_CLASS_OK
:
1040 next_state_
= STATE_CTRL_WRITE_QUIT
;
1042 case ERROR_CLASS_INFO_NEEDED
:
1043 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1044 case ERROR_CLASS_TRANSIENT_ERROR
:
1045 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1046 case ERROR_CLASS_PERMANENT_ERROR
:
1047 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1050 return Stop(ERR_UNEXPECTED
);
1057 int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1058 std::string command
= "SIZE " + GetRequestPathForFtpCommand(false);
1059 next_state_
= STATE_CTRL_READ
;
1060 return SendFtpCommand(command
, command
, COMMAND_SIZE
);
1063 int FtpNetworkTransaction::ProcessResponseSIZE(
1064 const FtpCtrlResponse
& response
) {
1065 switch (GetErrorClass(response
.status_code
)) {
1066 case ERROR_CLASS_INITIATED
:
1068 case ERROR_CLASS_OK
:
1069 if (response
.lines
.size() != 1)
1070 return Stop(ERR_INVALID_RESPONSE
);
1072 if (!base::StringToInt64(response
.lines
[0], &size
))
1073 return Stop(ERR_INVALID_RESPONSE
);
1075 return Stop(ERR_INVALID_RESPONSE
);
1077 // A successful response to SIZE does not mean the resource is a file.
1078 // Some FTP servers (for example, the qnx one) send a SIZE even for
1080 response_
.expected_content_size
= size
;
1082 case ERROR_CLASS_INFO_NEEDED
:
1084 case ERROR_CLASS_TRANSIENT_ERROR
:
1086 case ERROR_CLASS_PERMANENT_ERROR
:
1087 // It's possible that SIZE failed because the path is a directory.
1088 // TODO(xunjieli): https://crbug.com/526724: Add a test for this case.
1089 if (resource_type_
== RESOURCE_TYPE_UNKNOWN
&&
1090 response
.status_code
!= 550) {
1091 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1096 return Stop(ERR_UNEXPECTED
);
1099 // If the resource is known beforehand to be a file, RETR should be issued,
1100 // otherwise do CWD which will detect the resource type.
1101 if (resource_type_
== RESOURCE_TYPE_FILE
)
1102 EstablishDataConnection(STATE_CTRL_WRITE_RETR
);
1104 next_state_
= STATE_CTRL_WRITE_CWD
;
1109 int FtpNetworkTransaction::DoCtrlWriteCWD() {
1110 std::string command
= "CWD " + GetRequestPathForFtpCommand(true);
1111 next_state_
= STATE_CTRL_READ
;
1112 return SendFtpCommand(command
, command
, COMMAND_CWD
);
1115 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse
& response
) {
1116 // CWD should be invoked only when the resource is not a file.
1117 DCHECK_NE(RESOURCE_TYPE_FILE
, resource_type_
);
1119 switch (GetErrorClass(response
.status_code
)) {
1120 case ERROR_CLASS_INITIATED
:
1121 return Stop(ERR_INVALID_RESPONSE
);
1122 case ERROR_CLASS_OK
:
1123 resource_type_
= RESOURCE_TYPE_DIRECTORY
;
1124 EstablishDataConnection(STATE_CTRL_WRITE_LIST
);
1126 case ERROR_CLASS_INFO_NEEDED
:
1127 return Stop(ERR_INVALID_RESPONSE
);
1128 case ERROR_CLASS_TRANSIENT_ERROR
:
1129 // Some FTP servers send response 451 (not a valid CWD response according
1130 // to RFC 959) instead of 550.
1131 if (response
.status_code
== 451)
1132 return ProcessResponseCWDNotADirectory();
1134 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1135 case ERROR_CLASS_PERMANENT_ERROR
:
1136 if (response
.status_code
== 550)
1137 return ProcessResponseCWDNotADirectory();
1139 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1142 return Stop(ERR_UNEXPECTED
);
1148 int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1149 if (resource_type_
== RESOURCE_TYPE_DIRECTORY
) {
1150 // We're assuming that the resource is a directory, but the server
1151 // says it's not true. The most probable interpretation is that it
1152 // doesn't exist (with FTP we can't be sure).
1153 return Stop(ERR_FILE_NOT_FOUND
);
1156 // If it is not a directory, it is probably a file.
1157 resource_type_
= RESOURCE_TYPE_FILE
;
1159 EstablishDataConnection(STATE_CTRL_WRITE_RETR
);
1164 int FtpNetworkTransaction::DoCtrlWriteLIST() {
1165 // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1166 // forces LIST output instead of NLST (which would be ambiguous for us
1168 std::string
command("LIST -l");
1169 if (system_type_
== SYSTEM_TYPE_VMS
)
1170 command
= "LIST *.*;0";
1172 next_state_
= STATE_CTRL_READ
;
1173 return SendFtpCommand(command
, command
, COMMAND_LIST
);
1176 int FtpNetworkTransaction::ProcessResponseLIST(
1177 const FtpCtrlResponse
& response
) {
1178 // Resource type should be either filled in by DetectTypecode() or
1179 // detected with CWD. LIST is sent only when the resource is a directory.
1180 DCHECK_EQ(RESOURCE_TYPE_DIRECTORY
, resource_type_
);
1182 switch (GetErrorClass(response
.status_code
)) {
1183 case ERROR_CLASS_INITIATED
:
1184 // We want the client to start reading the response at this point.
1185 // It got here either through Start or RestartWithAuth. We want that
1186 // method to complete. Not setting next state here will make DoLoop exit
1187 // and in turn make Start/RestartWithAuth complete.
1188 response_
.is_directory_listing
= true;
1190 case ERROR_CLASS_OK
:
1191 response_
.is_directory_listing
= true;
1192 next_state_
= STATE_CTRL_WRITE_QUIT
;
1194 case ERROR_CLASS_INFO_NEEDED
:
1195 return Stop(ERR_INVALID_RESPONSE
);
1196 case ERROR_CLASS_TRANSIENT_ERROR
:
1197 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1198 case ERROR_CLASS_PERMANENT_ERROR
:
1199 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1202 return Stop(ERR_UNEXPECTED
);
1208 int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1209 std::string command
= "QUIT";
1210 next_state_
= STATE_CTRL_READ
;
1211 return SendFtpCommand(command
, command
, COMMAND_QUIT
);
1214 int FtpNetworkTransaction::ProcessResponseQUIT(
1215 const FtpCtrlResponse
& response
) {
1216 ctrl_socket_
->Disconnect();
1222 int FtpNetworkTransaction::DoDataConnect() {
1223 next_state_
= STATE_DATA_CONNECT_COMPLETE
;
1224 IPEndPoint ip_endpoint
;
1225 AddressList data_address
;
1226 // Connect to the same host as the control socket to prevent PASV port
1227 // scanning attacks.
1228 int rv
= ctrl_socket_
->GetPeerAddress(&ip_endpoint
);
1231 data_address
= AddressList::CreateFromIPAddress(
1232 ip_endpoint
.address(), data_connection_port_
);
1233 data_socket_
= socket_factory_
->CreateTransportClientSocket(
1234 data_address
, net_log_
.net_log(), net_log_
.source());
1236 NetLog::TYPE_FTP_DATA_CONNECTION
,
1237 data_socket_
->NetLog().source().ToEventParametersCallback());
1238 return data_socket_
->Connect(io_callback_
);
1241 int FtpNetworkTransaction::DoDataConnectComplete(int result
) {
1242 if (result
!= OK
&& use_epsv_
) {
1243 // It's possible we hit a broken server, sadly. They can break in different
1244 // ways. Some time out, some reset a connection. Fall back to PASV.
1245 // TODO(phajdan.jr): https://crbug.com/526723: remember it for future
1246 // transactions with this server.
1248 next_state_
= STATE_CTRL_WRITE_PASV
;
1252 // Only record the connection error after we've applied all our fallbacks.
1253 // We want to capture the final error, one we're not going to recover from.
1254 RecordDataConnectionError(result
);
1257 return Stop(result
);
1259 next_state_
= state_after_data_connect_complete_
;
1263 int FtpNetworkTransaction::DoDataRead() {
1264 DCHECK(read_data_buf_
.get());
1265 DCHECK_GT(read_data_buf_len_
, 0);
1267 if (data_socket_
== NULL
|| !data_socket_
->IsConnected()) {
1268 // If we don't destroy the data socket completely, some servers will wait
1269 // for us (http://crbug.com/21127). The half-closed TCP connection needs
1270 // to be closed on our side too.
1271 data_socket_
.reset();
1273 if (ctrl_socket_
->IsConnected()) {
1274 // Wait for the server's response, we should get it before sending QUIT.
1275 next_state_
= STATE_CTRL_READ
;
1279 // We are no longer connected to the server, so just finish the transaction.
1283 next_state_
= STATE_DATA_READ_COMPLETE
;
1284 read_data_buf_
->data()[0] = 0;
1285 return data_socket_
->Read(
1286 read_data_buf_
.get(), read_data_buf_len_
, io_callback_
);
1289 int FtpNetworkTransaction::DoDataReadComplete(int result
) {
1293 // We're using a histogram as a group of counters, with one bucket for each
1294 // enumeration value. We're only interested in the values of the counters.
1295 // Ignore the shape, average, and standard deviation of the histograms because
1296 // they are meaningless.
1298 // We use two histograms. In the first histogram we tally whether the user has
1299 // seen an error of that type during the session. In the second histogram we
1300 // tally the total number of times the users sees each errer.
1301 void FtpNetworkTransaction::RecordDataConnectionError(int result
) {
1302 // Gather data for http://crbug.com/3073. See how many users have trouble
1303 // establishing FTP data connection in passive FTP mode.
1305 // Data connection successful.
1308 // Local firewall blocked the connection.
1309 NET_ERROR_ACCESS_DENIED
= 1,
1311 // Connection timed out.
1312 NET_ERROR_TIMED_OUT
= 2,
1314 // Connection has been estabilished, but then got broken (either reset
1316 NET_ERROR_CONNECTION_BROKEN
= 3,
1318 // Connection has been refused.
1319 NET_ERROR_CONNECTION_REFUSED
= 4,
1321 // No connection to the internet.
1322 NET_ERROR_INTERNET_DISCONNECTED
= 5,
1324 // Could not reach the destination address.
1325 NET_ERROR_ADDRESS_UNREACHABLE
= 6,
1327 // A programming error in our network stack.
1328 NET_ERROR_UNEXPECTED
= 7,
1330 // Other kind of error.
1331 NET_ERROR_OTHER
= 20,
1333 NUM_OF_NET_ERROR_TYPES
1337 type
= NET_ERROR_OK
;
1339 case ERR_ACCESS_DENIED
:
1340 case ERR_NETWORK_ACCESS_DENIED
:
1341 type
= NET_ERROR_ACCESS_DENIED
;
1344 type
= NET_ERROR_TIMED_OUT
;
1346 case ERR_CONNECTION_ABORTED
:
1347 case ERR_CONNECTION_RESET
:
1348 case ERR_CONNECTION_CLOSED
:
1349 type
= NET_ERROR_CONNECTION_BROKEN
;
1351 case ERR_CONNECTION_FAILED
:
1352 case ERR_CONNECTION_REFUSED
:
1353 type
= NET_ERROR_CONNECTION_REFUSED
;
1355 case ERR_INTERNET_DISCONNECTED
:
1356 type
= NET_ERROR_INTERNET_DISCONNECTED
;
1358 case ERR_ADDRESS_INVALID
:
1359 case ERR_ADDRESS_UNREACHABLE
:
1360 type
= NET_ERROR_ADDRESS_UNREACHABLE
;
1362 case ERR_UNEXPECTED
:
1363 type
= NET_ERROR_UNEXPECTED
;
1366 type
= NET_ERROR_OTHER
;
1369 static bool had_error_type
[NUM_OF_NET_ERROR_TYPES
];
1371 DCHECK(type
>= 0 && type
< NUM_OF_NET_ERROR_TYPES
);
1372 if (!had_error_type
[type
]) {
1373 had_error_type
[type
] = true;
1374 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1375 type
, NUM_OF_NET_ERROR_TYPES
);
1377 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1378 type
, NUM_OF_NET_ERROR_TYPES
);