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_network_session.h"
23 #include "net/ftp/ftp_request_info.h"
24 #include "net/ftp/ftp_util.h"
25 #include "net/log/net_log.h"
26 #include "net/socket/client_socket_factory.h"
27 #include "net/socket/stream_socket.h"
28 #include "url/url_constants.h"
34 const char kCRLF
[] = "\r\n";
36 const int kCtrlBufLen
= 1024;
38 // Returns true if |input| can be safely used as a part of FTP command.
39 bool IsValidFTPCommandString(const std::string
& input
) {
40 // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
41 // characters in the command if the request path contains them. To be
42 // compatible, we do the same and allow non-ASCII characters in a command.
44 // Protect agains newline injection attack.
45 if (input
.find_first_of("\r\n") != std::string::npos
)
52 // The requested action was initiated. The client should expect another
53 // reply before issuing the next command.
54 ERROR_CLASS_INITIATED
,
56 // The requested action has been successfully completed.
59 // The command has been accepted, but to complete the operation, more
60 // information must be sent by the client.
61 ERROR_CLASS_INFO_NEEDED
,
63 // The command was not accepted and the requested action did not take place.
64 // This condition is temporary, and the client is encouraged to restart the
66 ERROR_CLASS_TRANSIENT_ERROR
,
68 // The command was not accepted and the requested action did not take place.
69 // This condition is rather permanent, and the client is discouraged from
70 // repeating the exact request.
71 ERROR_CLASS_PERMANENT_ERROR
,
74 // Returns the error class for given response code. Caller should ensure
75 // that |response_code| is in range 100-599.
76 ErrorClass
GetErrorClass(int response_code
) {
77 if (response_code
>= 100 && response_code
<= 199)
78 return ERROR_CLASS_INITIATED
;
80 if (response_code
>= 200 && response_code
<= 299)
81 return ERROR_CLASS_OK
;
83 if (response_code
>= 300 && response_code
<= 399)
84 return ERROR_CLASS_INFO_NEEDED
;
86 if (response_code
>= 400 && response_code
<= 499)
87 return ERROR_CLASS_TRANSIENT_ERROR
;
89 if (response_code
>= 500 && response_code
<= 599)
90 return ERROR_CLASS_PERMANENT_ERROR
;
92 // We should not be called on invalid error codes.
93 NOTREACHED() << response_code
;
94 return ERROR_CLASS_PERMANENT_ERROR
;
97 // Returns network error code for received FTP |response_code|.
98 int GetNetErrorCodeForFtpResponseCode(int response_code
) {
99 switch (response_code
) {
101 return ERR_FTP_SERVICE_UNAVAILABLE
;
103 return ERR_FTP_TRANSFER_ABORTED
;
105 return ERR_FTP_FILE_BUSY
;
108 return ERR_FTP_SYNTAX_ERROR
;
111 return ERR_FTP_COMMAND_NOT_SUPPORTED
;
113 return ERR_FTP_BAD_COMMAND_SEQUENCE
;
115 return ERR_FTP_FAILED
;
119 // From RFC 2428 Section 3:
120 // The text returned in response to the EPSV command MUST be:
121 // <some text> (<d><d><d><tcp-port><d>)
122 // <d> is a delimiter character, ideally to be |
123 bool ExtractPortFromEPSVResponse(const FtpCtrlResponse
& response
, int* port
) {
124 if (response
.lines
.size() != 1)
126 const char* ptr
= response
.lines
[0].c_str();
127 while (*ptr
&& *ptr
!= '(')
132 if (!sep
|| isdigit(sep
) || *(++ptr
) != sep
|| *(++ptr
) != sep
)
134 if (!isdigit(*(++ptr
)))
137 while (isdigit(*(++ptr
))) {
147 // There are two way we can receive IP address and port.
148 // (127,0,0,1,23,21) IP address and port encapsulated in ().
149 // 127,0,0,1,23,21 IP address and port without ().
151 // See RFC 959, Section 4.1.2
152 bool ExtractPortFromPASVResponse(const FtpCtrlResponse
& response
, int* port
) {
153 if (response
.lines
.size() != 1)
156 std::string
line(response
.lines
[0]);
157 if (!base::IsStringASCII(line
))
159 if (line
.length() < 2)
162 size_t paren_pos
= line
.find('(');
163 if (paren_pos
== std::string::npos
) {
164 // Find the first comma and use it to locate the beginning
165 // of the response data.
166 size_t comma_pos
= line
.find(',');
167 if (comma_pos
== std::string::npos
)
170 size_t space_pos
= line
.rfind(' ', comma_pos
);
171 if (space_pos
!= std::string::npos
)
172 line
= line
.substr(space_pos
+ 1);
174 // Remove the parentheses and use the text inside them.
175 size_t closing_paren_pos
= line
.rfind(')');
176 if (closing_paren_pos
== std::string::npos
)
178 if (closing_paren_pos
<= paren_pos
)
181 line
= line
.substr(paren_pos
+ 1, closing_paren_pos
- paren_pos
- 1);
184 // Split the line into comma-separated pieces and extract
186 std::vector
<base::StringPiece
> pieces
= base::SplitStringPiece(
187 line
, ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
188 if (pieces
.size() != 6)
191 // Ignore the IP address supplied in the response. We are always going
192 // to connect back to the same server to prevent FTP PASV port scanning.
194 if (!base::StringToInt(pieces
[4], &p0
))
196 if (!base::StringToInt(pieces
[5], &p1
))
198 *port
= (p0
<< 8) + p1
;
205 FtpNetworkTransaction::FtpNetworkTransaction(
206 FtpNetworkSession
* session
,
207 ClientSocketFactory
* socket_factory
)
208 : command_sent_(COMMAND_NONE
),
209 io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete
,
210 base::Unretained(this))),
213 resolver_(session
->host_resolver()),
214 read_ctrl_buf_(new IOBuffer(kCtrlBufLen
)),
215 read_data_buf_len_(0),
217 system_type_(SYSTEM_TYPE_UNKNOWN
),
218 // Use image (binary) transfer by default. It should always work,
219 // whereas the ascii transfer may damage binary data.
220 data_type_(DATA_TYPE_IMAGE
),
221 resource_type_(RESOURCE_TYPE_UNKNOWN
),
223 data_connection_port_(0),
224 socket_factory_(socket_factory
),
225 next_state_(STATE_NONE
),
226 state_after_data_connect_complete_(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::Start(const FtpRequestInfo
* request_info
,
242 const CompletionCallback
& callback
,
243 const BoundNetLog
& net_log
) {
245 request_
= request_info
;
247 ctrl_response_buffer_
.reset(new FtpCtrlResponseBuffer(net_log_
));
249 if (request_
->url
.has_username()) {
250 base::string16 username
;
251 base::string16 password
;
252 GetIdentityFromURL(request_
->url
, &username
, &password
);
253 credentials_
.Set(username
, password
);
255 credentials_
.Set(base::ASCIIToUTF16("anonymous"),
256 base::ASCIIToUTF16("chrome@example.com"));
261 next_state_
= STATE_CTRL_RESOLVE_HOST
;
263 if (rv
== ERR_IO_PENDING
)
264 user_callback_
= callback
;
268 int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials
& credentials
,
269 const CompletionCallback
& callback
) {
270 ResetStateForRestart();
272 credentials_
= credentials
;
274 next_state_
= STATE_CTRL_RESOLVE_HOST
;
276 if (rv
== ERR_IO_PENDING
)
277 user_callback_
= callback
;
281 int FtpNetworkTransaction::Read(IOBuffer
* buf
,
283 const CompletionCallback
& callback
) {
285 DCHECK_GT(buf_len
, 0);
287 read_data_buf_
= buf
;
288 read_data_buf_len_
= buf_len
;
290 next_state_
= STATE_DATA_READ
;
292 if (rv
== ERR_IO_PENDING
)
293 user_callback_
= callback
;
297 const FtpResponseInfo
* FtpNetworkTransaction::GetResponseInfo() const {
301 LoadState
FtpNetworkTransaction::GetLoadState() const {
302 if (next_state_
== STATE_CTRL_RESOLVE_HOST_COMPLETE
)
303 return LOAD_STATE_RESOLVING_HOST
;
305 if (next_state_
== STATE_CTRL_CONNECT_COMPLETE
||
306 next_state_
== STATE_DATA_CONNECT_COMPLETE
)
307 return LOAD_STATE_CONNECTING
;
309 if (next_state_
== STATE_DATA_READ_COMPLETE
)
310 return LOAD_STATE_READING_RESPONSE
;
312 if (command_sent_
== COMMAND_RETR
&& read_data_buf_
.get())
313 return LOAD_STATE_READING_RESPONSE
;
315 if (command_sent_
== COMMAND_QUIT
)
316 return LOAD_STATE_IDLE
;
318 if (command_sent_
!= COMMAND_NONE
)
319 return LOAD_STATE_SENDING_REQUEST
;
321 return LOAD_STATE_IDLE
;
324 uint64
FtpNetworkTransaction::GetUploadProgress() const {
328 void FtpNetworkTransaction::ResetStateForRestart() {
329 command_sent_
= COMMAND_NONE
;
330 user_callback_
.Reset();
331 response_
= FtpResponseInfo();
332 read_ctrl_buf_
= new IOBuffer(kCtrlBufLen
);
333 ctrl_response_buffer_
.reset(new FtpCtrlResponseBuffer(net_log_
));
334 read_data_buf_
= NULL
;
335 read_data_buf_len_
= 0;
336 if (write_buf_
.get())
337 write_buf_
->SetOffset(0);
339 data_connection_port_
= 0;
340 ctrl_socket_
.reset();
341 data_socket_
.reset();
342 next_state_
= STATE_NONE
;
343 state_after_data_connect_complete_
= STATE_NONE
;
346 void FtpNetworkTransaction::EstablishDataConnection(State state_after_connect
) {
347 DCHECK(state_after_connect
== STATE_CTRL_WRITE_RETR
||
348 state_after_connect
== STATE_CTRL_WRITE_LIST
);
349 state_after_data_connect_complete_
= state_after_connect
;
350 next_state_
= use_epsv_
? STATE_CTRL_WRITE_EPSV
: STATE_CTRL_WRITE_PASV
;
353 void FtpNetworkTransaction::DoCallback(int rv
) {
354 DCHECK(rv
!= ERR_IO_PENDING
);
355 DCHECK(!user_callback_
.is_null());
357 // Since Run may result in Read being called, clear callback_ up front.
358 CompletionCallback c
= user_callback_
;
359 user_callback_
.Reset();
363 void FtpNetworkTransaction::OnIOComplete(int result
) {
364 int rv
= DoLoop(result
);
365 if (rv
!= ERR_IO_PENDING
)
369 int FtpNetworkTransaction::ProcessCtrlResponse() {
370 FtpCtrlResponse response
= ctrl_response_buffer_
->PopResponse();
373 switch (command_sent_
) {
375 // TODO(phajdan.jr): Check for errors in the welcome message.
376 next_state_
= STATE_CTRL_WRITE_USER
;
379 rv
= ProcessResponseUSER(response
);
382 rv
= ProcessResponsePASS(response
);
385 rv
= ProcessResponseSYST(response
);
388 rv
= ProcessResponsePWD(response
);
391 rv
= ProcessResponseTYPE(response
);
394 rv
= ProcessResponseEPSV(response
);
397 rv
= ProcessResponsePASV(response
);
400 rv
= ProcessResponseSIZE(response
);
403 rv
= ProcessResponseRETR(response
);
406 rv
= ProcessResponseCWD(response
);
409 rv
= ProcessResponseLIST(response
);
412 rv
= ProcessResponseQUIT(response
);
415 LOG(DFATAL
) << "Unexpected value of command_sent_: " << command_sent_
;
416 return ERR_UNEXPECTED
;
419 // We may get multiple responses for some commands,
420 // see http://crbug.com/18036.
421 while (ctrl_response_buffer_
->ResponseAvailable() && rv
== OK
) {
422 response
= ctrl_response_buffer_
->PopResponse();
424 switch (command_sent_
) {
426 rv
= ProcessResponseRETR(response
);
429 rv
= ProcessResponseLIST(response
);
432 // Multiple responses for other commands are invalid.
433 return Stop(ERR_INVALID_RESPONSE
);
440 // Used to prepare and send FTP command.
441 int FtpNetworkTransaction::SendFtpCommand(const std::string
& command
,
442 const std::string
& command_for_log
,
444 // If we send a new command when we still have unprocessed responses
445 // for previous commands, the response receiving code will have no way to know
446 // which responses are for which command.
447 DCHECK(!ctrl_response_buffer_
->ResponseAvailable());
449 DCHECK(!write_command_buf_
.get());
450 DCHECK(!write_buf_
.get());
452 if (!IsValidFTPCommandString(command
)) {
453 // Callers should validate the command themselves and return a more specific
456 return Stop(ERR_UNEXPECTED
);
461 write_command_buf_
= new IOBufferWithSize(command
.length() + 2);
462 write_buf_
= new DrainableIOBuffer(write_command_buf_
.get(),
463 write_command_buf_
->size());
464 memcpy(write_command_buf_
->data(), command
.data(), command
.length());
465 memcpy(write_command_buf_
->data() + command
.length(), kCRLF
, 2);
467 net_log_
.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT
,
468 NetLog::StringCallback("command", &command_for_log
));
470 next_state_
= STATE_CTRL_WRITE
;
474 std::string
FtpNetworkTransaction::GetRequestPathForFtpCommand(
475 bool is_directory
) const {
476 std::string
path(current_remote_directory_
);
477 if (request_
->url
.has_path()) {
478 std::string
gurl_path(request_
->url
.path());
480 // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
481 std::string::size_type pos
= gurl_path
.rfind(';');
482 if (pos
!= std::string::npos
)
483 gurl_path
.resize(pos
);
485 path
.append(gurl_path
);
487 // Make sure that if the path is expected to be a file, it won't end
488 // with a trailing slash.
489 if (!is_directory
&& path
.length() > 1 && path
[path
.length() - 1] == '/')
490 path
.erase(path
.length() - 1);
491 UnescapeRule::Type unescape_rules
= UnescapeRule::SPACES
|
492 UnescapeRule::URL_SPECIAL_CHARS
;
493 // This may unescape to non-ASCII characters, but we allow that. See the
494 // comment for IsValidFTPCommandString.
495 path
= UnescapeURLComponent(path
, unescape_rules
);
497 if (system_type_
== SYSTEM_TYPE_VMS
) {
499 path
= FtpUtil::UnixDirectoryPathToVMS(path
);
501 path
= FtpUtil::UnixFilePathToVMS(path
);
504 DCHECK(IsValidFTPCommandString(path
));
508 void FtpNetworkTransaction::DetectTypecode() {
509 if (!request_
->url
.has_path())
511 std::string
gurl_path(request_
->url
.path());
513 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
514 std::string::size_type pos
= gurl_path
.rfind(';');
515 if (pos
== std::string::npos
)
517 std::string
typecode_string(gurl_path
.substr(pos
));
518 if (typecode_string
== ";type=a") {
519 data_type_
= DATA_TYPE_ASCII
;
520 resource_type_
= RESOURCE_TYPE_FILE
;
521 } else if (typecode_string
== ";type=i") {
522 data_type_
= DATA_TYPE_IMAGE
;
523 resource_type_
= RESOURCE_TYPE_FILE
;
524 } else if (typecode_string
== ";type=d") {
525 resource_type_
= RESOURCE_TYPE_DIRECTORY
;
529 int FtpNetworkTransaction::DoLoop(int result
) {
530 DCHECK(next_state_
!= STATE_NONE
);
534 State state
= next_state_
;
535 next_state_
= STATE_NONE
;
537 case STATE_CTRL_RESOLVE_HOST
:
539 rv
= DoCtrlResolveHost();
541 case STATE_CTRL_RESOLVE_HOST_COMPLETE
:
542 rv
= DoCtrlResolveHostComplete(rv
);
544 case STATE_CTRL_CONNECT
:
546 rv
= DoCtrlConnect();
548 case STATE_CTRL_CONNECT_COMPLETE
:
549 rv
= DoCtrlConnectComplete(rv
);
551 case STATE_CTRL_READ
:
555 case STATE_CTRL_READ_COMPLETE
:
556 rv
= DoCtrlReadComplete(rv
);
558 case STATE_CTRL_WRITE
:
562 case STATE_CTRL_WRITE_COMPLETE
:
563 rv
= DoCtrlWriteComplete(rv
);
565 case STATE_CTRL_WRITE_USER
:
567 rv
= DoCtrlWriteUSER();
569 case STATE_CTRL_WRITE_PASS
:
571 rv
= DoCtrlWritePASS();
573 case STATE_CTRL_WRITE_SYST
:
575 rv
= DoCtrlWriteSYST();
577 case STATE_CTRL_WRITE_PWD
:
579 rv
= DoCtrlWritePWD();
581 case STATE_CTRL_WRITE_TYPE
:
583 rv
= DoCtrlWriteTYPE();
585 case STATE_CTRL_WRITE_EPSV
:
587 rv
= DoCtrlWriteEPSV();
589 case STATE_CTRL_WRITE_PASV
:
591 rv
= DoCtrlWritePASV();
593 case STATE_CTRL_WRITE_RETR
:
595 rv
= DoCtrlWriteRETR();
597 case STATE_CTRL_WRITE_SIZE
:
599 rv
= DoCtrlWriteSIZE();
601 case STATE_CTRL_WRITE_CWD
:
603 rv
= DoCtrlWriteCWD();
605 case STATE_CTRL_WRITE_LIST
:
607 rv
= DoCtrlWriteLIST();
609 case STATE_CTRL_WRITE_QUIT
:
611 rv
= DoCtrlWriteQUIT();
613 case STATE_DATA_CONNECT
:
615 rv
= DoDataConnect();
617 case STATE_DATA_CONNECT_COMPLETE
:
618 rv
= DoDataConnectComplete(rv
);
620 case STATE_DATA_READ
:
624 case STATE_DATA_READ_COMPLETE
:
625 rv
= DoDataReadComplete(rv
);
628 NOTREACHED() << "bad state";
632 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
636 int FtpNetworkTransaction::DoCtrlResolveHost() {
637 next_state_
= STATE_CTRL_RESOLVE_HOST_COMPLETE
;
639 HostResolver::RequestInfo
info(HostPortPair::FromURL(request_
->url
));
640 // No known referrer.
641 return resolver_
.Resolve(
645 base::Bind(&FtpNetworkTransaction::OnIOComplete
, base::Unretained(this)),
649 int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result
) {
651 next_state_
= STATE_CTRL_CONNECT
;
655 int FtpNetworkTransaction::DoCtrlConnect() {
656 next_state_
= STATE_CTRL_CONNECT_COMPLETE
;
657 ctrl_socket_
= socket_factory_
->CreateTransportClientSocket(
658 addresses_
, net_log_
.net_log(), net_log_
.source());
660 NetLog::TYPE_FTP_CONTROL_CONNECTION
,
661 ctrl_socket_
->NetLog().source().ToEventParametersCallback());
662 return ctrl_socket_
->Connect(io_callback_
);
665 int FtpNetworkTransaction::DoCtrlConnectComplete(int result
) {
667 // Put the peer's IP address and port into the response.
668 IPEndPoint ip_endpoint
;
669 result
= ctrl_socket_
->GetPeerAddress(&ip_endpoint
);
671 response_
.socket_address
= HostPortPair::FromIPEndPoint(ip_endpoint
);
672 next_state_
= STATE_CTRL_READ
;
674 if (ip_endpoint
.GetFamily() == ADDRESS_FAMILY_IPV4
) {
675 // Do not use EPSV for IPv4 connections. Some servers become confused
676 // and we time out while waiting to connect. PASV is perfectly fine for
677 // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
678 // whitelisting IPv6 to use it, to make the code more future-proof:
679 // all future protocols should just use EPSV.
687 int FtpNetworkTransaction::DoCtrlRead() {
688 next_state_
= STATE_CTRL_READ_COMPLETE
;
689 return ctrl_socket_
->Read(read_ctrl_buf_
.get(), kCtrlBufLen
, io_callback_
);
692 int FtpNetworkTransaction::DoCtrlReadComplete(int result
) {
694 // Some servers (for example Pure-FTPd) apparently close the control
695 // connection when anonymous login is not permitted. For more details
696 // see http://crbug.com/25023.
697 if (command_sent_
== COMMAND_USER
&&
698 credentials_
.username() == base::ASCIIToUTF16("anonymous")) {
699 response_
.needs_auth
= true;
701 return Stop(ERR_EMPTY_RESPONSE
);
706 ctrl_response_buffer_
->ConsumeData(read_ctrl_buf_
->data(), result
);
708 if (!ctrl_response_buffer_
->ResponseAvailable()) {
709 // Read more data from the control socket.
710 next_state_
= STATE_CTRL_READ
;
714 return ProcessCtrlResponse();
717 int FtpNetworkTransaction::DoCtrlWrite() {
718 next_state_
= STATE_CTRL_WRITE_COMPLETE
;
720 return ctrl_socket_
->Write(
721 write_buf_
.get(), write_buf_
->BytesRemaining(), io_callback_
);
724 int FtpNetworkTransaction::DoCtrlWriteComplete(int result
) {
728 write_buf_
->DidConsume(result
);
729 if (write_buf_
->BytesRemaining() == 0) {
730 // Clear the write buffer.
732 write_command_buf_
= NULL
;
734 next_state_
= STATE_CTRL_READ
;
736 next_state_
= STATE_CTRL_WRITE
;
741 // FTP Commands and responses
744 int FtpNetworkTransaction::DoCtrlWriteUSER() {
745 std::string command
= "USER " + base::UTF16ToUTF8(credentials_
.username());
747 if (!IsValidFTPCommandString(command
))
748 return Stop(ERR_MALFORMED_IDENTITY
);
750 next_state_
= STATE_CTRL_READ
;
751 return SendFtpCommand(command
, "USER ***", COMMAND_USER
);
754 int FtpNetworkTransaction::ProcessResponseUSER(
755 const FtpCtrlResponse
& response
) {
756 switch (GetErrorClass(response
.status_code
)) {
758 next_state_
= STATE_CTRL_WRITE_SYST
;
760 case ERROR_CLASS_INFO_NEEDED
:
761 next_state_
= STATE_CTRL_WRITE_PASS
;
763 case ERROR_CLASS_TRANSIENT_ERROR
:
764 case ERROR_CLASS_PERMANENT_ERROR
:
765 response_
.needs_auth
= true;
766 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
769 return Stop(ERR_UNEXPECTED
);
775 int FtpNetworkTransaction::DoCtrlWritePASS() {
776 std::string command
= "PASS " + base::UTF16ToUTF8(credentials_
.password());
778 if (!IsValidFTPCommandString(command
))
779 return Stop(ERR_MALFORMED_IDENTITY
);
781 next_state_
= STATE_CTRL_READ
;
782 return SendFtpCommand(command
, "PASS ***", COMMAND_PASS
);
785 int FtpNetworkTransaction::ProcessResponsePASS(
786 const FtpCtrlResponse
& response
) {
787 switch (GetErrorClass(response
.status_code
)) {
789 next_state_
= STATE_CTRL_WRITE_SYST
;
791 case ERROR_CLASS_INFO_NEEDED
:
792 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
793 case ERROR_CLASS_TRANSIENT_ERROR
:
794 case ERROR_CLASS_PERMANENT_ERROR
:
795 response_
.needs_auth
= true;
796 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
799 return Stop(ERR_UNEXPECTED
);
805 int FtpNetworkTransaction::DoCtrlWriteSYST() {
806 std::string command
= "SYST";
807 next_state_
= STATE_CTRL_READ
;
808 return SendFtpCommand(command
, command
, COMMAND_SYST
);
811 int FtpNetworkTransaction::ProcessResponseSYST(
812 const FtpCtrlResponse
& response
) {
813 switch (GetErrorClass(response
.status_code
)) {
814 case ERROR_CLASS_INITIATED
:
815 return Stop(ERR_INVALID_RESPONSE
);
816 case ERROR_CLASS_OK
: {
817 // All important info should be on the first line.
818 std::string line
= response
.lines
[0];
819 // The response should be ASCII, which allows us to do case-insensitive
820 // comparisons easily. If it is not ASCII, we leave the system type
822 if (base::IsStringASCII(line
)) {
823 line
= base::StringToLowerASCII(line
);
825 // Remove all whitespace, to correctly handle cases like fancy "V M S"
826 // response instead of "VMS".
827 base::RemoveChars(line
, base::kWhitespaceASCII
, &line
);
829 // The "magic" strings we test for below have been gathered by an
830 // empirical study. VMS needs to come first because some VMS systems
831 // also respond with "UNIX emulation", which is not perfect. It is much
832 // more reliable to talk to these servers in their native language.
833 if (line
.find("vms") != std::string::npos
) {
834 system_type_
= SYSTEM_TYPE_VMS
;
835 } else if (line
.find("l8") != std::string::npos
||
836 line
.find("unix") != std::string::npos
||
837 line
.find("bsd") != std::string::npos
) {
838 system_type_
= SYSTEM_TYPE_UNIX
;
839 } else if (line
.find("win32") != std::string::npos
||
840 line
.find("windows") != std::string::npos
) {
841 system_type_
= SYSTEM_TYPE_WINDOWS
;
842 } else if (line
.find("os/2") != std::string::npos
) {
843 system_type_
= SYSTEM_TYPE_OS2
;
846 next_state_
= STATE_CTRL_WRITE_PWD
;
849 case ERROR_CLASS_INFO_NEEDED
:
850 return Stop(ERR_INVALID_RESPONSE
);
851 case ERROR_CLASS_TRANSIENT_ERROR
:
852 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
853 case ERROR_CLASS_PERMANENT_ERROR
:
854 // Server does not recognize the SYST command so proceed.
855 next_state_
= STATE_CTRL_WRITE_PWD
;
859 return Stop(ERR_UNEXPECTED
);
865 int FtpNetworkTransaction::DoCtrlWritePWD() {
866 std::string command
= "PWD";
867 next_state_
= STATE_CTRL_READ
;
868 return SendFtpCommand(command
, command
, COMMAND_PWD
);
871 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse
& response
) {
872 switch (GetErrorClass(response
.status_code
)) {
873 case ERROR_CLASS_INITIATED
:
874 return Stop(ERR_INVALID_RESPONSE
);
875 case ERROR_CLASS_OK
: {
876 // The info we look for should be on the first line.
877 std::string line
= response
.lines
[0];
879 return Stop(ERR_INVALID_RESPONSE
);
880 std::string::size_type quote_pos
= line
.find('"');
881 if (quote_pos
!= std::string::npos
) {
882 line
= line
.substr(quote_pos
+ 1);
883 quote_pos
= line
.find('"');
884 if (quote_pos
== std::string::npos
)
885 return Stop(ERR_INVALID_RESPONSE
);
886 line
= line
.substr(0, quote_pos
);
888 if (system_type_
== SYSTEM_TYPE_VMS
)
889 line
= FtpUtil::VMSPathToUnix(line
);
890 if (line
.length() && line
[line
.length() - 1] == '/')
891 line
.erase(line
.length() - 1);
892 current_remote_directory_
= line
;
893 next_state_
= STATE_CTRL_WRITE_TYPE
;
896 case ERROR_CLASS_INFO_NEEDED
:
897 return Stop(ERR_INVALID_RESPONSE
);
898 case ERROR_CLASS_TRANSIENT_ERROR
:
899 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
900 case ERROR_CLASS_PERMANENT_ERROR
:
901 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
904 return Stop(ERR_UNEXPECTED
);
910 int FtpNetworkTransaction::DoCtrlWriteTYPE() {
911 std::string command
= "TYPE ";
912 if (data_type_
== DATA_TYPE_ASCII
) {
914 } else if (data_type_
== DATA_TYPE_IMAGE
) {
918 return Stop(ERR_UNEXPECTED
);
920 next_state_
= STATE_CTRL_READ
;
921 return SendFtpCommand(command
, command
, COMMAND_TYPE
);
924 int FtpNetworkTransaction::ProcessResponseTYPE(
925 const FtpCtrlResponse
& response
) {
926 switch (GetErrorClass(response
.status_code
)) {
927 case ERROR_CLASS_INITIATED
:
928 return Stop(ERR_INVALID_RESPONSE
);
930 next_state_
= STATE_CTRL_WRITE_SIZE
;
932 case ERROR_CLASS_INFO_NEEDED
:
933 return Stop(ERR_INVALID_RESPONSE
);
934 case ERROR_CLASS_TRANSIENT_ERROR
:
935 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
936 case ERROR_CLASS_PERMANENT_ERROR
:
937 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
940 return Stop(ERR_UNEXPECTED
);
946 int FtpNetworkTransaction::DoCtrlWriteEPSV() {
947 const std::string command
= "EPSV";
948 next_state_
= STATE_CTRL_READ
;
949 return SendFtpCommand(command
, command
, COMMAND_EPSV
);
952 int FtpNetworkTransaction::ProcessResponseEPSV(
953 const FtpCtrlResponse
& response
) {
954 switch (GetErrorClass(response
.status_code
)) {
955 case ERROR_CLASS_INITIATED
:
956 return Stop(ERR_INVALID_RESPONSE
);
957 case ERROR_CLASS_OK
: {
959 if (!ExtractPortFromEPSVResponse(response
, &port
))
960 return Stop(ERR_INVALID_RESPONSE
);
961 if (IsWellKnownPort(port
) ||
962 !IsPortAllowedForScheme(port
, url::kFtpScheme
)) {
963 return Stop(ERR_UNSAFE_PORT
);
965 data_connection_port_
= static_cast<uint16
>(port
);
966 next_state_
= STATE_DATA_CONNECT
;
969 case ERROR_CLASS_INFO_NEEDED
:
970 return Stop(ERR_INVALID_RESPONSE
);
971 case ERROR_CLASS_TRANSIENT_ERROR
:
972 case ERROR_CLASS_PERMANENT_ERROR
:
974 next_state_
= STATE_CTRL_WRITE_PASV
;
978 return Stop(ERR_UNEXPECTED
);
984 int FtpNetworkTransaction::DoCtrlWritePASV() {
985 std::string command
= "PASV";
986 next_state_
= STATE_CTRL_READ
;
987 return SendFtpCommand(command
, command
, COMMAND_PASV
);
990 int FtpNetworkTransaction::ProcessResponsePASV(
991 const FtpCtrlResponse
& response
) {
992 switch (GetErrorClass(response
.status_code
)) {
993 case ERROR_CLASS_INITIATED
:
994 return Stop(ERR_INVALID_RESPONSE
);
995 case ERROR_CLASS_OK
: {
997 if (!ExtractPortFromPASVResponse(response
, &port
))
998 return Stop(ERR_INVALID_RESPONSE
);
999 if (IsWellKnownPort(port
) ||
1000 !IsPortAllowedForScheme(port
, url::kFtpScheme
)) {
1001 return Stop(ERR_UNSAFE_PORT
);
1003 data_connection_port_
= static_cast<uint16
>(port
);
1004 next_state_
= STATE_DATA_CONNECT
;
1007 case ERROR_CLASS_INFO_NEEDED
:
1008 return Stop(ERR_INVALID_RESPONSE
);
1009 case ERROR_CLASS_TRANSIENT_ERROR
:
1010 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1011 case ERROR_CLASS_PERMANENT_ERROR
:
1012 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1015 return Stop(ERR_UNEXPECTED
);
1021 int FtpNetworkTransaction::DoCtrlWriteRETR() {
1022 std::string command
= "RETR " + GetRequestPathForFtpCommand(false);
1023 next_state_
= STATE_CTRL_READ
;
1024 return SendFtpCommand(command
, command
, COMMAND_RETR
);
1027 int FtpNetworkTransaction::ProcessResponseRETR(
1028 const FtpCtrlResponse
& response
) {
1029 // Resource type should be either filled in by DetectTypecode() or
1030 // detected with CWD. RETR is sent only when the resource is a file.
1031 DCHECK_EQ(RESOURCE_TYPE_FILE
, resource_type_
);
1033 switch (GetErrorClass(response
.status_code
)) {
1034 case ERROR_CLASS_INITIATED
:
1035 // We want the client to start reading the response at this point.
1036 // It got here either through Start or RestartWithAuth. We want that
1037 // method to complete. Not setting next state here will make DoLoop exit
1038 // and in turn make Start/RestartWithAuth complete.
1040 case ERROR_CLASS_OK
:
1041 next_state_
= STATE_CTRL_WRITE_QUIT
;
1043 case ERROR_CLASS_INFO_NEEDED
:
1044 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1045 case ERROR_CLASS_TRANSIENT_ERROR
:
1046 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1047 case ERROR_CLASS_PERMANENT_ERROR
:
1048 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1051 return Stop(ERR_UNEXPECTED
);
1058 int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1059 std::string command
= "SIZE " + GetRequestPathForFtpCommand(false);
1060 next_state_
= STATE_CTRL_READ
;
1061 return SendFtpCommand(command
, command
, COMMAND_SIZE
);
1064 int FtpNetworkTransaction::ProcessResponseSIZE(
1065 const FtpCtrlResponse
& response
) {
1066 switch (GetErrorClass(response
.status_code
)) {
1067 case ERROR_CLASS_INITIATED
:
1069 case ERROR_CLASS_OK
:
1070 if (response
.lines
.size() != 1)
1071 return Stop(ERR_INVALID_RESPONSE
);
1073 if (!base::StringToInt64(response
.lines
[0], &size
))
1074 return Stop(ERR_INVALID_RESPONSE
);
1076 return Stop(ERR_INVALID_RESPONSE
);
1078 // A successful response to SIZE does not mean the resource is a file.
1079 // Some FTP servers (for example, the qnx one) send a SIZE even for
1081 response_
.expected_content_size
= size
;
1083 case ERROR_CLASS_INFO_NEEDED
:
1085 case ERROR_CLASS_TRANSIENT_ERROR
:
1087 case ERROR_CLASS_PERMANENT_ERROR
:
1088 // It's possible that SIZE failed because the path is a directory.
1089 // TODO(xunjieli): Add a test for this case.
1090 if (resource_type_
== RESOURCE_TYPE_UNKNOWN
&&
1091 response
.status_code
!= 550) {
1092 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1097 return Stop(ERR_UNEXPECTED
);
1100 // If the resource is known beforehand to be a file, RETR should be issued,
1101 // otherwise do CWD which will detect the resource type.
1102 if (resource_type_
== RESOURCE_TYPE_FILE
)
1103 EstablishDataConnection(STATE_CTRL_WRITE_RETR
);
1105 next_state_
= STATE_CTRL_WRITE_CWD
;
1110 int FtpNetworkTransaction::DoCtrlWriteCWD() {
1111 std::string command
= "CWD " + GetRequestPathForFtpCommand(true);
1112 next_state_
= STATE_CTRL_READ
;
1113 return SendFtpCommand(command
, command
, COMMAND_CWD
);
1116 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse
& response
) {
1117 // CWD should be invoked only when the resource is not a file.
1118 DCHECK_NE(RESOURCE_TYPE_FILE
, resource_type_
);
1120 switch (GetErrorClass(response
.status_code
)) {
1121 case ERROR_CLASS_INITIATED
:
1122 return Stop(ERR_INVALID_RESPONSE
);
1123 case ERROR_CLASS_OK
:
1124 resource_type_
= RESOURCE_TYPE_DIRECTORY
;
1125 EstablishDataConnection(STATE_CTRL_WRITE_LIST
);
1127 case ERROR_CLASS_INFO_NEEDED
:
1128 return Stop(ERR_INVALID_RESPONSE
);
1129 case ERROR_CLASS_TRANSIENT_ERROR
:
1130 // Some FTP servers send response 451 (not a valid CWD response according
1131 // to RFC 959) instead of 550.
1132 if (response
.status_code
== 451)
1133 return ProcessResponseCWDNotADirectory();
1135 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1136 case ERROR_CLASS_PERMANENT_ERROR
:
1137 if (response
.status_code
== 550)
1138 return ProcessResponseCWDNotADirectory();
1140 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1143 return Stop(ERR_UNEXPECTED
);
1149 int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1150 if (resource_type_
== RESOURCE_TYPE_DIRECTORY
) {
1151 // We're assuming that the resource is a directory, but the server
1152 // says it's not true. The most probable interpretation is that it
1153 // doesn't exist (with FTP we can't be sure).
1154 return Stop(ERR_FILE_NOT_FOUND
);
1157 // If it is not a directory, it is probably a file.
1158 resource_type_
= RESOURCE_TYPE_FILE
;
1160 EstablishDataConnection(STATE_CTRL_WRITE_RETR
);
1165 int FtpNetworkTransaction::DoCtrlWriteLIST() {
1166 // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1167 // forces LIST output instead of NLST (which would be ambiguous for us
1169 std::string
command("LIST -l");
1170 if (system_type_
== SYSTEM_TYPE_VMS
)
1171 command
= "LIST *.*;0";
1173 next_state_
= STATE_CTRL_READ
;
1174 return SendFtpCommand(command
, command
, COMMAND_LIST
);
1177 int FtpNetworkTransaction::ProcessResponseLIST(
1178 const FtpCtrlResponse
& response
) {
1179 // Resource type should be either filled in by DetectTypecode() or
1180 // detected with CWD. LIST is sent only when the resource is a directory.
1181 DCHECK_EQ(RESOURCE_TYPE_DIRECTORY
, resource_type_
);
1183 switch (GetErrorClass(response
.status_code
)) {
1184 case ERROR_CLASS_INITIATED
:
1185 // We want the client to start reading the response at this point.
1186 // It got here either through Start or RestartWithAuth. We want that
1187 // method to complete. Not setting next state here will make DoLoop exit
1188 // and in turn make Start/RestartWithAuth complete.
1189 response_
.is_directory_listing
= true;
1191 case ERROR_CLASS_OK
:
1192 response_
.is_directory_listing
= true;
1193 next_state_
= STATE_CTRL_WRITE_QUIT
;
1195 case ERROR_CLASS_INFO_NEEDED
:
1196 return Stop(ERR_INVALID_RESPONSE
);
1197 case ERROR_CLASS_TRANSIENT_ERROR
:
1198 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1199 case ERROR_CLASS_PERMANENT_ERROR
:
1200 return Stop(GetNetErrorCodeForFtpResponseCode(response
.status_code
));
1203 return Stop(ERR_UNEXPECTED
);
1209 int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1210 std::string command
= "QUIT";
1211 next_state_
= STATE_CTRL_READ
;
1212 return SendFtpCommand(command
, command
, COMMAND_QUIT
);
1215 int FtpNetworkTransaction::ProcessResponseQUIT(
1216 const FtpCtrlResponse
& response
) {
1217 ctrl_socket_
->Disconnect();
1223 int FtpNetworkTransaction::DoDataConnect() {
1224 next_state_
= STATE_DATA_CONNECT_COMPLETE
;
1225 IPEndPoint ip_endpoint
;
1226 AddressList data_address
;
1227 // Connect to the same host as the control socket to prevent PASV port
1228 // scanning attacks.
1229 int rv
= ctrl_socket_
->GetPeerAddress(&ip_endpoint
);
1232 data_address
= AddressList::CreateFromIPAddress(
1233 ip_endpoint
.address(), data_connection_port_
);
1234 data_socket_
= socket_factory_
->CreateTransportClientSocket(
1235 data_address
, net_log_
.net_log(), net_log_
.source());
1237 NetLog::TYPE_FTP_DATA_CONNECTION
,
1238 data_socket_
->NetLog().source().ToEventParametersCallback());
1239 return data_socket_
->Connect(io_callback_
);
1242 int FtpNetworkTransaction::DoDataConnectComplete(int result
) {
1243 if (result
!= OK
&& use_epsv_
) {
1244 // It's possible we hit a broken server, sadly. They can break in different
1245 // ways. Some time out, some reset a connection. Fall back to PASV.
1246 // TODO(phajdan.jr): remember it for future transactions with this server.
1247 // TODO(phajdan.jr): write a test for this code path.
1249 next_state_
= STATE_CTRL_WRITE_PASV
;
1253 // Only record the connection error after we've applied all our fallbacks.
1254 // We want to capture the final error, one we're not going to recover from.
1255 RecordDataConnectionError(result
);
1258 return Stop(result
);
1260 next_state_
= state_after_data_connect_complete_
;
1264 int FtpNetworkTransaction::DoDataRead() {
1265 DCHECK(read_data_buf_
.get());
1266 DCHECK_GT(read_data_buf_len_
, 0);
1268 if (data_socket_
== NULL
|| !data_socket_
->IsConnected()) {
1269 // If we don't destroy the data socket completely, some servers will wait
1270 // for us (http://crbug.com/21127). The half-closed TCP connection needs
1271 // to be closed on our side too.
1272 data_socket_
.reset();
1274 if (ctrl_socket_
->IsConnected()) {
1275 // Wait for the server's response, we should get it before sending QUIT.
1276 next_state_
= STATE_CTRL_READ
;
1280 // We are no longer connected to the server, so just finish the transaction.
1284 next_state_
= STATE_DATA_READ_COMPLETE
;
1285 read_data_buf_
->data()[0] = 0;
1286 return data_socket_
->Read(
1287 read_data_buf_
.get(), read_data_buf_len_
, io_callback_
);
1290 int FtpNetworkTransaction::DoDataReadComplete(int result
) {
1294 // We're using a histogram as a group of counters, with one bucket for each
1295 // enumeration value. We're only interested in the values of the counters.
1296 // Ignore the shape, average, and standard deviation of the histograms because
1297 // they are meaningless.
1299 // We use two histograms. In the first histogram we tally whether the user has
1300 // seen an error of that type during the session. In the second histogram we
1301 // tally the total number of times the users sees each errer.
1302 void FtpNetworkTransaction::RecordDataConnectionError(int result
) {
1303 // Gather data for http://crbug.com/3073. See how many users have trouble
1304 // establishing FTP data connection in passive FTP mode.
1306 // Data connection successful.
1309 // Local firewall blocked the connection.
1310 NET_ERROR_ACCESS_DENIED
= 1,
1312 // Connection timed out.
1313 NET_ERROR_TIMED_OUT
= 2,
1315 // Connection has been estabilished, but then got broken (either reset
1317 NET_ERROR_CONNECTION_BROKEN
= 3,
1319 // Connection has been refused.
1320 NET_ERROR_CONNECTION_REFUSED
= 4,
1322 // No connection to the internet.
1323 NET_ERROR_INTERNET_DISCONNECTED
= 5,
1325 // Could not reach the destination address.
1326 NET_ERROR_ADDRESS_UNREACHABLE
= 6,
1328 // A programming error in our network stack.
1329 NET_ERROR_UNEXPECTED
= 7,
1331 // Other kind of error.
1332 NET_ERROR_OTHER
= 20,
1334 NUM_OF_NET_ERROR_TYPES
1338 type
= NET_ERROR_OK
;
1340 case ERR_ACCESS_DENIED
:
1341 case ERR_NETWORK_ACCESS_DENIED
:
1342 type
= NET_ERROR_ACCESS_DENIED
;
1345 type
= NET_ERROR_TIMED_OUT
;
1347 case ERR_CONNECTION_ABORTED
:
1348 case ERR_CONNECTION_RESET
:
1349 case ERR_CONNECTION_CLOSED
:
1350 type
= NET_ERROR_CONNECTION_BROKEN
;
1352 case ERR_CONNECTION_FAILED
:
1353 case ERR_CONNECTION_REFUSED
:
1354 type
= NET_ERROR_CONNECTION_REFUSED
;
1356 case ERR_INTERNET_DISCONNECTED
:
1357 type
= NET_ERROR_INTERNET_DISCONNECTED
;
1359 case ERR_ADDRESS_INVALID
:
1360 case ERR_ADDRESS_UNREACHABLE
:
1361 type
= NET_ERROR_ADDRESS_UNREACHABLE
;
1363 case ERR_UNEXPECTED
:
1364 type
= NET_ERROR_UNEXPECTED
;
1367 type
= NET_ERROR_OTHER
;
1370 static bool had_error_type
[NUM_OF_NET_ERROR_TYPES
];
1372 DCHECK(type
>= 0 && type
< NUM_OF_NET_ERROR_TYPES
);
1373 if (!had_error_type
[type
]) {
1374 had_error_type
[type
] = true;
1375 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1376 type
, NUM_OF_NET_ERROR_TYPES
);
1378 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1379 type
, NUM_OF_NET_ERROR_TYPES
);