Enable Enhanced Bookmark on Android Tablet
[chromium-blink-merge.git] / net / ftp / ftp_network_transaction.cc
blob17d5754b6c01fb9f98c3ae30891bf6f43b64703d
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"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/compiler_specific.h"
10 #include "base/metrics/histogram.h"
11 #include "base/profiler/scoped_tracker.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "net/base/address_list.h"
18 #include "net/base/connection_type_histograms.h"
19 #include "net/base/escape.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/net_log.h"
22 #include "net/base/net_util.h"
23 #include "net/ftp/ftp_network_session.h"
24 #include "net/ftp/ftp_request_info.h"
25 #include "net/ftp/ftp_util.h"
26 #include "net/socket/client_socket_factory.h"
27 #include "net/socket/stream_socket.h"
29 const char kCRLF[] = "\r\n";
31 const int kCtrlBufLen = 1024;
33 namespace {
35 // Returns true if |input| can be safely used as a part of FTP command.
36 bool IsValidFTPCommandString(const std::string& input) {
37 // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
38 // characters in the command if the request path contains them. To be
39 // compatible, we do the same and allow non-ASCII characters in a command.
41 // Protect agains newline injection attack.
42 if (input.find_first_of("\r\n") != std::string::npos)
43 return false;
45 return true;
48 enum ErrorClass {
49 // The requested action was initiated. The client should expect another
50 // reply before issuing the next command.
51 ERROR_CLASS_INITIATED,
53 // The requested action has been successfully completed.
54 ERROR_CLASS_OK,
56 // The command has been accepted, but to complete the operation, more
57 // information must be sent by the client.
58 ERROR_CLASS_INFO_NEEDED,
60 // The command was not accepted and the requested action did not take place.
61 // This condition is temporary, and the client is encouraged to restart the
62 // command sequence.
63 ERROR_CLASS_TRANSIENT_ERROR,
65 // The command was not accepted and the requested action did not take place.
66 // This condition is rather permanent, and the client is discouraged from
67 // repeating the exact request.
68 ERROR_CLASS_PERMANENT_ERROR,
71 // Returns the error class for given response code. Caller should ensure
72 // that |response_code| is in range 100-599.
73 ErrorClass GetErrorClass(int response_code) {
74 if (response_code >= 100 && response_code <= 199)
75 return ERROR_CLASS_INITIATED;
77 if (response_code >= 200 && response_code <= 299)
78 return ERROR_CLASS_OK;
80 if (response_code >= 300 && response_code <= 399)
81 return ERROR_CLASS_INFO_NEEDED;
83 if (response_code >= 400 && response_code <= 499)
84 return ERROR_CLASS_TRANSIENT_ERROR;
86 if (response_code >= 500 && response_code <= 599)
87 return ERROR_CLASS_PERMANENT_ERROR;
89 // We should not be called on invalid error codes.
90 NOTREACHED() << response_code;
91 return ERROR_CLASS_PERMANENT_ERROR;
94 // Returns network error code for received FTP |response_code|.
95 int GetNetErrorCodeForFtpResponseCode(int response_code) {
96 switch (response_code) {
97 case 421:
98 return net::ERR_FTP_SERVICE_UNAVAILABLE;
99 case 426:
100 return net::ERR_FTP_TRANSFER_ABORTED;
101 case 450:
102 return net::ERR_FTP_FILE_BUSY;
103 case 500:
104 case 501:
105 return net::ERR_FTP_SYNTAX_ERROR;
106 case 502:
107 case 504:
108 return net::ERR_FTP_COMMAND_NOT_SUPPORTED;
109 case 503:
110 return net::ERR_FTP_BAD_COMMAND_SEQUENCE;
111 default:
112 return net::ERR_FTP_FAILED;
116 // From RFC 2428 Section 3:
117 // The text returned in response to the EPSV command MUST be:
118 // <some text> (<d><d><d><tcp-port><d>)
119 // <d> is a delimiter character, ideally to be |
120 bool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response,
121 int* port) {
122 if (response.lines.size() != 1)
123 return false;
124 const char* ptr = response.lines[0].c_str();
125 while (*ptr && *ptr != '(')
126 ++ptr;
127 if (!*ptr)
128 return false;
129 char sep = *(++ptr);
130 if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
131 return false;
132 if (!isdigit(*(++ptr)))
133 return false;
134 *port = *ptr - '0';
135 while (isdigit(*(++ptr))) {
136 *port *= 10;
137 *port += *ptr - '0';
139 if (*ptr != sep)
140 return false;
142 return true;
145 // There are two way we can receive IP address and port.
146 // (127,0,0,1,23,21) IP address and port encapsulated in ().
147 // 127,0,0,1,23,21 IP address and port without ().
149 // See RFC 959, Section 4.1.2
150 bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
151 int* port) {
152 if (response.lines.size() != 1)
153 return false;
155 std::string line(response.lines[0]);
156 if (!base::IsStringASCII(line))
157 return false;
158 if (line.length() < 2)
159 return false;
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)
167 return false;
169 size_t space_pos = line.rfind(' ', comma_pos);
170 if (space_pos != std::string::npos)
171 line = line.substr(space_pos + 1);
172 } else {
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)
176 return false;
177 if (closing_paren_pos <= paren_pos)
178 return false;
180 line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1);
183 // Split the line into comma-separated pieces and extract
184 // the last two.
185 std::vector<std::string> pieces;
186 base::SplitString(line, ',', &pieces);
187 if (pieces.size() != 6)
188 return false;
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.
192 int p0, p1;
193 if (!base::StringToInt(pieces[4], &p0))
194 return false;
195 if (!base::StringToInt(pieces[5], &p1))
196 return false;
197 *port = (p0 << 8) + p1;
199 return true;
202 } // namespace
204 namespace net {
206 FtpNetworkTransaction::FtpNetworkTransaction(
207 FtpNetworkSession* session,
208 ClientSocketFactory* socket_factory)
209 : command_sent_(COMMAND_NONE),
210 io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
211 base::Unretained(this))),
212 session_(session),
213 request_(NULL),
214 resolver_(session->host_resolver()),
215 read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
216 read_data_buf_len_(0),
217 last_error_(OK),
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),
223 use_epsv_(true),
224 data_connection_port_(0),
225 socket_factory_(socket_factory),
226 next_state_(STATE_NONE),
227 state_after_data_connect_complete_(STATE_CTRL_WRITE_SIZE) {}
229 FtpNetworkTransaction::~FtpNetworkTransaction() {
232 int FtpNetworkTransaction::Stop(int error) {
233 if (command_sent_ == COMMAND_QUIT)
234 return error;
236 next_state_ = STATE_CTRL_WRITE_QUIT;
237 last_error_ = error;
238 return OK;
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) {
249 net_log_ = net_log;
250 request_ = request_info;
252 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
254 if (request_->url.has_username()) {
255 base::string16 username;
256 base::string16 password;
257 GetIdentityFromURL(request_->url, &username, &password);
258 credentials_.Set(username, password);
259 } else {
260 credentials_.Set(base::ASCIIToUTF16("anonymous"),
261 base::ASCIIToUTF16("chrome@example.com"));
264 DetectTypecode();
266 next_state_ = STATE_CTRL_RESOLVE_HOST;
267 int rv = DoLoop(OK);
268 if (rv == ERR_IO_PENDING)
269 user_callback_ = callback;
270 return rv;
273 int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
274 const CompletionCallback& callback) {
275 ResetStateForRestart();
277 credentials_ = credentials;
279 next_state_ = STATE_CTRL_RESOLVE_HOST;
280 int rv = DoLoop(OK);
281 if (rv == ERR_IO_PENDING)
282 user_callback_ = callback;
283 return rv;
286 int FtpNetworkTransaction::Read(IOBuffer* buf,
287 int buf_len,
288 const CompletionCallback& callback) {
289 DCHECK(buf);
290 DCHECK_GT(buf_len, 0);
292 read_data_buf_ = buf;
293 read_data_buf_len_ = buf_len;
295 next_state_ = STATE_DATA_READ;
296 int rv = DoLoop(OK);
297 if (rv == ERR_IO_PENDING)
298 user_callback_ = callback;
299 return rv;
302 const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
303 return &response_;
306 LoadState FtpNetworkTransaction::GetLoadState() const {
307 if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
308 return LOAD_STATE_RESOLVING_HOST;
310 if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
311 next_state_ == STATE_DATA_CONNECT_COMPLETE)
312 return LOAD_STATE_CONNECTING;
314 if (next_state_ == STATE_DATA_READ_COMPLETE)
315 return LOAD_STATE_READING_RESPONSE;
317 if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
318 return LOAD_STATE_READING_RESPONSE;
320 if (command_sent_ == COMMAND_QUIT)
321 return LOAD_STATE_IDLE;
323 if (command_sent_ != COMMAND_NONE)
324 return LOAD_STATE_SENDING_REQUEST;
326 return LOAD_STATE_IDLE;
329 uint64 FtpNetworkTransaction::GetUploadProgress() const {
330 return 0;
333 void FtpNetworkTransaction::ResetStateForRestart() {
334 command_sent_ = COMMAND_NONE;
335 user_callback_.Reset();
336 response_ = FtpResponseInfo();
337 read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
338 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
339 read_data_buf_ = NULL;
340 read_data_buf_len_ = 0;
341 if (write_buf_.get())
342 write_buf_->SetOffset(0);
343 last_error_ = OK;
344 data_connection_port_ = 0;
345 ctrl_socket_.reset();
346 data_socket_.reset();
347 next_state_ = STATE_NONE;
348 state_after_data_connect_complete_ = STATE_CTRL_WRITE_SIZE;
351 void FtpNetworkTransaction::ResetDataConnectionAfterError(State next_state) {
352 // The server _might_ have reset the data connection
353 // (see RFC 959 3.2. ESTABLISHING DATA CONNECTIONS:
354 // "The server MUST close the data connection under the following
355 // conditions:
356 // ...
357 // 5. An irrecoverable error condition occurs.")
359 // It is ambiguous what an irrecoverable error condition is,
360 // so we take no chances.
361 state_after_data_connect_complete_ = next_state;
362 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
365 void FtpNetworkTransaction::DoCallback(int rv) {
366 DCHECK(rv != ERR_IO_PENDING);
367 DCHECK(!user_callback_.is_null());
369 // Since Run may result in Read being called, clear callback_ up front.
370 CompletionCallback c = user_callback_;
371 user_callback_.Reset();
372 c.Run(rv);
375 void FtpNetworkTransaction::OnIOComplete(int result) {
376 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed.
377 tracked_objects::ScopedTracker tracking_profile(
378 FROM_HERE_WITH_EXPLICIT_FUNCTION(
379 "436634 FtpNetworkTransaction::OnIOComplete"));
381 int rv = DoLoop(result);
382 if (rv != ERR_IO_PENDING)
383 DoCallback(rv);
386 int FtpNetworkTransaction::ProcessCtrlResponse() {
387 FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
389 int rv = OK;
390 switch (command_sent_) {
391 case COMMAND_NONE:
392 // TODO(phajdan.jr): Check for errors in the welcome message.
393 next_state_ = STATE_CTRL_WRITE_USER;
394 break;
395 case COMMAND_USER:
396 rv = ProcessResponseUSER(response);
397 break;
398 case COMMAND_PASS:
399 rv = ProcessResponsePASS(response);
400 break;
401 case COMMAND_SYST:
402 rv = ProcessResponseSYST(response);
403 break;
404 case COMMAND_PWD:
405 rv = ProcessResponsePWD(response);
406 break;
407 case COMMAND_TYPE:
408 rv = ProcessResponseTYPE(response);
409 break;
410 case COMMAND_EPSV:
411 rv = ProcessResponseEPSV(response);
412 break;
413 case COMMAND_PASV:
414 rv = ProcessResponsePASV(response);
415 break;
416 case COMMAND_SIZE:
417 rv = ProcessResponseSIZE(response);
418 break;
419 case COMMAND_RETR:
420 rv = ProcessResponseRETR(response);
421 break;
422 case COMMAND_CWD:
423 rv = ProcessResponseCWD(response);
424 break;
425 case COMMAND_LIST:
426 rv = ProcessResponseLIST(response);
427 break;
428 case COMMAND_QUIT:
429 rv = ProcessResponseQUIT(response);
430 break;
431 default:
432 LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
433 return ERR_UNEXPECTED;
436 // We may get multiple responses for some commands,
437 // see http://crbug.com/18036.
438 while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
439 response = ctrl_response_buffer_->PopResponse();
441 switch (command_sent_) {
442 case COMMAND_RETR:
443 rv = ProcessResponseRETR(response);
444 break;
445 case COMMAND_LIST:
446 rv = ProcessResponseLIST(response);
447 break;
448 default:
449 // Multiple responses for other commands are invalid.
450 return Stop(ERR_INVALID_RESPONSE);
454 return rv;
457 // Used to prepare and send FTP command.
458 int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
459 const std::string& command_for_log,
460 Command cmd) {
461 // If we send a new command when we still have unprocessed responses
462 // for previous commands, the response receiving code will have no way to know
463 // which responses are for which command.
464 DCHECK(!ctrl_response_buffer_->ResponseAvailable());
466 DCHECK(!write_command_buf_.get());
467 DCHECK(!write_buf_.get());
469 if (!IsValidFTPCommandString(command)) {
470 // Callers should validate the command themselves and return a more specific
471 // error code.
472 NOTREACHED();
473 return Stop(ERR_UNEXPECTED);
476 command_sent_ = cmd;
478 write_command_buf_ = new IOBufferWithSize(command.length() + 2);
479 write_buf_ = new DrainableIOBuffer(write_command_buf_.get(),
480 write_command_buf_->size());
481 memcpy(write_command_buf_->data(), command.data(), command.length());
482 memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
484 net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
485 NetLog::StringCallback("command", &command_for_log));
487 next_state_ = STATE_CTRL_WRITE;
488 return OK;
491 std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
492 bool is_directory) const {
493 std::string path(current_remote_directory_);
494 if (request_->url.has_path()) {
495 std::string gurl_path(request_->url.path());
497 // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
498 std::string::size_type pos = gurl_path.rfind(';');
499 if (pos != std::string::npos)
500 gurl_path.resize(pos);
502 path.append(gurl_path);
504 // Make sure that if the path is expected to be a file, it won't end
505 // with a trailing slash.
506 if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
507 path.erase(path.length() - 1);
508 UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
509 UnescapeRule::URL_SPECIAL_CHARS;
510 // This may unescape to non-ASCII characters, but we allow that. See the
511 // comment for IsValidFTPCommandString.
512 path = net::UnescapeURLComponent(path, unescape_rules);
514 if (system_type_ == SYSTEM_TYPE_VMS) {
515 if (is_directory)
516 path = FtpUtil::UnixDirectoryPathToVMS(path);
517 else
518 path = FtpUtil::UnixFilePathToVMS(path);
521 DCHECK(IsValidFTPCommandString(path));
522 return path;
525 void FtpNetworkTransaction::DetectTypecode() {
526 if (!request_->url.has_path())
527 return;
528 std::string gurl_path(request_->url.path());
530 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
531 std::string::size_type pos = gurl_path.rfind(';');
532 if (pos == std::string::npos)
533 return;
534 std::string typecode_string(gurl_path.substr(pos));
535 if (typecode_string == ";type=a") {
536 data_type_ = DATA_TYPE_ASCII;
537 resource_type_ = RESOURCE_TYPE_FILE;
538 } else if (typecode_string == ";type=i") {
539 data_type_ = DATA_TYPE_IMAGE;
540 resource_type_ = RESOURCE_TYPE_FILE;
541 } else if (typecode_string == ";type=d") {
542 resource_type_ = RESOURCE_TYPE_DIRECTORY;
546 int FtpNetworkTransaction::DoLoop(int result) {
547 DCHECK(next_state_ != STATE_NONE);
549 int rv = result;
550 do {
551 State state = next_state_;
552 next_state_ = STATE_NONE;
553 switch (state) {
554 case STATE_CTRL_RESOLVE_HOST:
555 DCHECK(rv == OK);
556 rv = DoCtrlResolveHost();
557 break;
558 case STATE_CTRL_RESOLVE_HOST_COMPLETE:
559 rv = DoCtrlResolveHostComplete(rv);
560 break;
561 case STATE_CTRL_CONNECT:
562 DCHECK(rv == OK);
563 rv = DoCtrlConnect();
564 break;
565 case STATE_CTRL_CONNECT_COMPLETE:
566 rv = DoCtrlConnectComplete(rv);
567 break;
568 case STATE_CTRL_READ:
569 DCHECK(rv == OK);
570 rv = DoCtrlRead();
571 break;
572 case STATE_CTRL_READ_COMPLETE:
573 rv = DoCtrlReadComplete(rv);
574 break;
575 case STATE_CTRL_WRITE:
576 DCHECK(rv == OK);
577 rv = DoCtrlWrite();
578 break;
579 case STATE_CTRL_WRITE_COMPLETE:
580 rv = DoCtrlWriteComplete(rv);
581 break;
582 case STATE_CTRL_WRITE_USER:
583 DCHECK(rv == OK);
584 rv = DoCtrlWriteUSER();
585 break;
586 case STATE_CTRL_WRITE_PASS:
587 DCHECK(rv == OK);
588 rv = DoCtrlWritePASS();
589 break;
590 case STATE_CTRL_WRITE_SYST:
591 DCHECK(rv == OK);
592 rv = DoCtrlWriteSYST();
593 break;
594 case STATE_CTRL_WRITE_PWD:
595 DCHECK(rv == OK);
596 rv = DoCtrlWritePWD();
597 break;
598 case STATE_CTRL_WRITE_TYPE:
599 DCHECK(rv == OK);
600 rv = DoCtrlWriteTYPE();
601 break;
602 case STATE_CTRL_WRITE_EPSV:
603 DCHECK(rv == OK);
604 rv = DoCtrlWriteEPSV();
605 break;
606 case STATE_CTRL_WRITE_PASV:
607 DCHECK(rv == OK);
608 rv = DoCtrlWritePASV();
609 break;
610 case STATE_CTRL_WRITE_RETR:
611 DCHECK(rv == OK);
612 rv = DoCtrlWriteRETR();
613 break;
614 case STATE_CTRL_WRITE_SIZE:
615 DCHECK(rv == OK);
616 rv = DoCtrlWriteSIZE();
617 break;
618 case STATE_CTRL_WRITE_CWD:
619 DCHECK(rv == OK);
620 rv = DoCtrlWriteCWD();
621 break;
622 case STATE_CTRL_WRITE_LIST:
623 DCHECK(rv == OK);
624 rv = DoCtrlWriteLIST();
625 break;
626 case STATE_CTRL_WRITE_QUIT:
627 DCHECK(rv == OK);
628 rv = DoCtrlWriteQUIT();
629 break;
630 case STATE_DATA_CONNECT:
631 DCHECK(rv == OK);
632 rv = DoDataConnect();
633 break;
634 case STATE_DATA_CONNECT_COMPLETE:
635 rv = DoDataConnectComplete(rv);
636 break;
637 case STATE_DATA_READ:
638 DCHECK(rv == OK);
639 rv = DoDataRead();
640 break;
641 case STATE_DATA_READ_COMPLETE:
642 rv = DoDataReadComplete(rv);
643 break;
644 default:
645 NOTREACHED() << "bad state";
646 rv = ERR_UNEXPECTED;
647 break;
649 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
650 return rv;
653 int FtpNetworkTransaction::DoCtrlResolveHost() {
654 next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
656 HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
657 // No known referrer.
658 return resolver_.Resolve(
659 info,
660 DEFAULT_PRIORITY,
661 &addresses_,
662 base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
663 net_log_);
666 int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
667 if (result == OK)
668 next_state_ = STATE_CTRL_CONNECT;
669 return result;
672 int FtpNetworkTransaction::DoCtrlConnect() {
673 next_state_ = STATE_CTRL_CONNECT_COMPLETE;
674 ctrl_socket_ = socket_factory_->CreateTransportClientSocket(
675 addresses_, net_log_.net_log(), net_log_.source());
676 net_log_.AddEvent(
677 NetLog::TYPE_FTP_CONTROL_CONNECTION,
678 ctrl_socket_->NetLog().source().ToEventParametersCallback());
679 return ctrl_socket_->Connect(io_callback_);
682 int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
683 if (result == OK) {
684 // Put the peer's IP address and port into the response.
685 IPEndPoint ip_endpoint;
686 result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
687 if (result == OK) {
688 response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
689 next_state_ = STATE_CTRL_READ;
691 if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
692 // Do not use EPSV for IPv4 connections. Some servers become confused
693 // and we time out while waiting to connect. PASV is perfectly fine for
694 // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
695 // whitelisting IPv6 to use it, to make the code more future-proof:
696 // all future protocols should just use EPSV.
697 use_epsv_ = false;
701 return result;
704 int FtpNetworkTransaction::DoCtrlRead() {
705 next_state_ = STATE_CTRL_READ_COMPLETE;
706 return ctrl_socket_->Read(read_ctrl_buf_.get(), kCtrlBufLen, io_callback_);
709 int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
710 if (result == 0) {
711 // Some servers (for example Pure-FTPd) apparently close the control
712 // connection when anonymous login is not permitted. For more details
713 // see http://crbug.com/25023.
714 if (command_sent_ == COMMAND_USER &&
715 credentials_.username() == base::ASCIIToUTF16("anonymous")) {
716 response_.needs_auth = true;
718 return Stop(ERR_EMPTY_RESPONSE);
720 if (result < 0)
721 return Stop(result);
723 ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
725 if (!ctrl_response_buffer_->ResponseAvailable()) {
726 // Read more data from the control socket.
727 next_state_ = STATE_CTRL_READ;
728 return OK;
731 return ProcessCtrlResponse();
734 int FtpNetworkTransaction::DoCtrlWrite() {
735 next_state_ = STATE_CTRL_WRITE_COMPLETE;
737 return ctrl_socket_->Write(
738 write_buf_.get(), write_buf_->BytesRemaining(), io_callback_);
741 int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
742 if (result < 0)
743 return result;
745 write_buf_->DidConsume(result);
746 if (write_buf_->BytesRemaining() == 0) {
747 // Clear the write buffer.
748 write_buf_ = NULL;
749 write_command_buf_ = NULL;
751 next_state_ = STATE_CTRL_READ;
752 } else {
753 next_state_ = STATE_CTRL_WRITE;
755 return OK;
758 // FTP Commands and responses
760 // USER Command.
761 int FtpNetworkTransaction::DoCtrlWriteUSER() {
762 std::string command = "USER " + base::UTF16ToUTF8(credentials_.username());
764 if (!IsValidFTPCommandString(command))
765 return Stop(ERR_MALFORMED_IDENTITY);
767 next_state_ = STATE_CTRL_READ;
768 return SendFtpCommand(command, "USER ***", COMMAND_USER);
771 int FtpNetworkTransaction::ProcessResponseUSER(
772 const FtpCtrlResponse& response) {
773 switch (GetErrorClass(response.status_code)) {
774 case ERROR_CLASS_OK:
775 next_state_ = STATE_CTRL_WRITE_SYST;
776 break;
777 case ERROR_CLASS_INFO_NEEDED:
778 next_state_ = STATE_CTRL_WRITE_PASS;
779 break;
780 case ERROR_CLASS_TRANSIENT_ERROR:
781 case ERROR_CLASS_PERMANENT_ERROR:
782 response_.needs_auth = true;
783 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
784 default:
785 NOTREACHED();
786 return Stop(ERR_UNEXPECTED);
788 return OK;
791 // PASS command.
792 int FtpNetworkTransaction::DoCtrlWritePASS() {
793 std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password());
795 if (!IsValidFTPCommandString(command))
796 return Stop(ERR_MALFORMED_IDENTITY);
798 next_state_ = STATE_CTRL_READ;
799 return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
802 int FtpNetworkTransaction::ProcessResponsePASS(
803 const FtpCtrlResponse& response) {
804 switch (GetErrorClass(response.status_code)) {
805 case ERROR_CLASS_OK:
806 next_state_ = STATE_CTRL_WRITE_SYST;
807 break;
808 case ERROR_CLASS_INFO_NEEDED:
809 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
810 case ERROR_CLASS_TRANSIENT_ERROR:
811 case ERROR_CLASS_PERMANENT_ERROR:
812 response_.needs_auth = true;
813 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
814 default:
815 NOTREACHED();
816 return Stop(ERR_UNEXPECTED);
818 return OK;
821 // SYST command.
822 int FtpNetworkTransaction::DoCtrlWriteSYST() {
823 std::string command = "SYST";
824 next_state_ = STATE_CTRL_READ;
825 return SendFtpCommand(command, command, COMMAND_SYST);
828 int FtpNetworkTransaction::ProcessResponseSYST(
829 const FtpCtrlResponse& response) {
830 switch (GetErrorClass(response.status_code)) {
831 case ERROR_CLASS_INITIATED:
832 return Stop(ERR_INVALID_RESPONSE);
833 case ERROR_CLASS_OK: {
834 // All important info should be on the first line.
835 std::string line = response.lines[0];
836 // The response should be ASCII, which allows us to do case-insensitive
837 // comparisons easily. If it is not ASCII, we leave the system type
838 // as unknown.
839 if (base::IsStringASCII(line)) {
840 line = base::StringToLowerASCII(line);
842 // Remove all whitespace, to correctly handle cases like fancy "V M S"
843 // response instead of "VMS".
844 base::RemoveChars(line, base::kWhitespaceASCII, &line);
846 // The "magic" strings we test for below have been gathered by an
847 // empirical study. VMS needs to come first because some VMS systems
848 // also respond with "UNIX emulation", which is not perfect. It is much
849 // more reliable to talk to these servers in their native language.
850 if (line.find("vms") != std::string::npos) {
851 system_type_ = SYSTEM_TYPE_VMS;
852 } else if (line.find("l8") != std::string::npos ||
853 line.find("unix") != std::string::npos ||
854 line.find("bsd") != std::string::npos) {
855 system_type_ = SYSTEM_TYPE_UNIX;
856 } else if (line.find("win32") != std::string::npos ||
857 line.find("windows") != std::string::npos) {
858 system_type_ = SYSTEM_TYPE_WINDOWS;
859 } else if (line.find("os/2") != std::string::npos) {
860 system_type_ = SYSTEM_TYPE_OS2;
863 next_state_ = STATE_CTRL_WRITE_PWD;
864 break;
866 case ERROR_CLASS_INFO_NEEDED:
867 return Stop(ERR_INVALID_RESPONSE);
868 case ERROR_CLASS_TRANSIENT_ERROR:
869 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
870 case ERROR_CLASS_PERMANENT_ERROR:
871 // Server does not recognize the SYST command so proceed.
872 next_state_ = STATE_CTRL_WRITE_PWD;
873 break;
874 default:
875 NOTREACHED();
876 return Stop(ERR_UNEXPECTED);
878 return OK;
881 // PWD command.
882 int FtpNetworkTransaction::DoCtrlWritePWD() {
883 std::string command = "PWD";
884 next_state_ = STATE_CTRL_READ;
885 return SendFtpCommand(command, command, COMMAND_PWD);
888 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
889 switch (GetErrorClass(response.status_code)) {
890 case ERROR_CLASS_INITIATED:
891 return Stop(ERR_INVALID_RESPONSE);
892 case ERROR_CLASS_OK: {
893 // The info we look for should be on the first line.
894 std::string line = response.lines[0];
895 if (line.empty())
896 return Stop(ERR_INVALID_RESPONSE);
897 std::string::size_type quote_pos = line.find('"');
898 if (quote_pos != std::string::npos) {
899 line = line.substr(quote_pos + 1);
900 quote_pos = line.find('"');
901 if (quote_pos == std::string::npos)
902 return Stop(ERR_INVALID_RESPONSE);
903 line = line.substr(0, quote_pos);
905 if (system_type_ == SYSTEM_TYPE_VMS)
906 line = FtpUtil::VMSPathToUnix(line);
907 if (line.length() && line[line.length() - 1] == '/')
908 line.erase(line.length() - 1);
909 current_remote_directory_ = line;
910 next_state_ = STATE_CTRL_WRITE_TYPE;
911 break;
913 case ERROR_CLASS_INFO_NEEDED:
914 return Stop(ERR_INVALID_RESPONSE);
915 case ERROR_CLASS_TRANSIENT_ERROR:
916 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
917 case ERROR_CLASS_PERMANENT_ERROR:
918 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
919 default:
920 NOTREACHED();
921 return Stop(ERR_UNEXPECTED);
923 return OK;
926 // TYPE command.
927 int FtpNetworkTransaction::DoCtrlWriteTYPE() {
928 std::string command = "TYPE ";
929 if (data_type_ == DATA_TYPE_ASCII) {
930 command += "A";
931 } else if (data_type_ == DATA_TYPE_IMAGE) {
932 command += "I";
933 } else {
934 NOTREACHED();
935 return Stop(ERR_UNEXPECTED);
937 next_state_ = STATE_CTRL_READ;
938 return SendFtpCommand(command, command, COMMAND_TYPE);
941 int FtpNetworkTransaction::ProcessResponseTYPE(
942 const FtpCtrlResponse& response) {
943 switch (GetErrorClass(response.status_code)) {
944 case ERROR_CLASS_INITIATED:
945 return Stop(ERR_INVALID_RESPONSE);
946 case ERROR_CLASS_OK:
947 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
948 break;
949 case ERROR_CLASS_INFO_NEEDED:
950 return Stop(ERR_INVALID_RESPONSE);
951 case ERROR_CLASS_TRANSIENT_ERROR:
952 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
953 case ERROR_CLASS_PERMANENT_ERROR:
954 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
955 default:
956 NOTREACHED();
957 return Stop(ERR_UNEXPECTED);
959 return OK;
962 // EPSV command
963 int FtpNetworkTransaction::DoCtrlWriteEPSV() {
964 const std::string command = "EPSV";
965 next_state_ = STATE_CTRL_READ;
966 return SendFtpCommand(command, command, COMMAND_EPSV);
969 int FtpNetworkTransaction::ProcessResponseEPSV(
970 const FtpCtrlResponse& response) {
971 switch (GetErrorClass(response.status_code)) {
972 case ERROR_CLASS_INITIATED:
973 return Stop(ERR_INVALID_RESPONSE);
974 case ERROR_CLASS_OK: {
975 int port;
976 if (!ExtractPortFromEPSVResponse(response, &port))
977 return Stop(ERR_INVALID_RESPONSE);
978 if (port < 1024 || !IsPortAllowedByFtp(port))
979 return Stop(ERR_UNSAFE_PORT);
980 data_connection_port_ = static_cast<uint16>(port);
981 next_state_ = STATE_DATA_CONNECT;
982 break;
984 case ERROR_CLASS_INFO_NEEDED:
985 return Stop(ERR_INVALID_RESPONSE);
986 case ERROR_CLASS_TRANSIENT_ERROR:
987 case ERROR_CLASS_PERMANENT_ERROR:
988 use_epsv_ = false;
989 next_state_ = STATE_CTRL_WRITE_PASV;
990 return OK;
991 default:
992 NOTREACHED();
993 return Stop(ERR_UNEXPECTED);
995 return OK;
998 // PASV command
999 int FtpNetworkTransaction::DoCtrlWritePASV() {
1000 std::string command = "PASV";
1001 next_state_ = STATE_CTRL_READ;
1002 return SendFtpCommand(command, command, COMMAND_PASV);
1005 int FtpNetworkTransaction::ProcessResponsePASV(
1006 const FtpCtrlResponse& response) {
1007 switch (GetErrorClass(response.status_code)) {
1008 case ERROR_CLASS_INITIATED:
1009 return Stop(ERR_INVALID_RESPONSE);
1010 case ERROR_CLASS_OK: {
1011 int port;
1012 if (!ExtractPortFromPASVResponse(response, &port))
1013 return Stop(ERR_INVALID_RESPONSE);
1014 if (port < 1024 || !IsPortAllowedByFtp(port))
1015 return Stop(ERR_UNSAFE_PORT);
1016 data_connection_port_ = static_cast<uint16>(port);
1017 next_state_ = STATE_DATA_CONNECT;
1018 break;
1020 case ERROR_CLASS_INFO_NEEDED:
1021 return Stop(ERR_INVALID_RESPONSE);
1022 case ERROR_CLASS_TRANSIENT_ERROR:
1023 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1024 case ERROR_CLASS_PERMANENT_ERROR:
1025 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1026 default:
1027 NOTREACHED();
1028 return Stop(ERR_UNEXPECTED);
1030 return OK;
1033 // RETR command
1034 int FtpNetworkTransaction::DoCtrlWriteRETR() {
1035 std::string command = "RETR " + GetRequestPathForFtpCommand(false);
1036 next_state_ = STATE_CTRL_READ;
1037 return SendFtpCommand(command, command, COMMAND_RETR);
1040 int FtpNetworkTransaction::ProcessResponseRETR(
1041 const FtpCtrlResponse& response) {
1042 switch (GetErrorClass(response.status_code)) {
1043 case ERROR_CLASS_INITIATED:
1044 // We want the client to start reading the response at this point.
1045 // It got here either through Start or RestartWithAuth. We want that
1046 // method to complete. Not setting next state here will make DoLoop exit
1047 // and in turn make Start/RestartWithAuth complete.
1048 resource_type_ = RESOURCE_TYPE_FILE;
1049 break;
1050 case ERROR_CLASS_OK:
1051 resource_type_ = RESOURCE_TYPE_FILE;
1052 next_state_ = STATE_CTRL_WRITE_QUIT;
1053 break;
1054 case ERROR_CLASS_INFO_NEEDED:
1055 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1056 case ERROR_CLASS_TRANSIENT_ERROR:
1057 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1058 case ERROR_CLASS_PERMANENT_ERROR:
1059 // Code 550 means "Failed to open file". Other codes are unrelated,
1060 // like "Not logged in" etc.
1061 if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE)
1062 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1064 // It's possible that RETR failed because the path is a directory.
1065 resource_type_ = RESOURCE_TYPE_DIRECTORY;
1067 // We're going to try CWD next, but first send a PASV one more time,
1068 // because some FTP servers, including FileZilla, require that.
1069 // See http://crbug.com/25316.
1070 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
1071 break;
1072 default:
1073 NOTREACHED();
1074 return Stop(ERR_UNEXPECTED);
1077 // We should be sure about our resource type now. Otherwise we risk
1078 // an infinite loop (RETR can later send CWD, and CWD can later send RETR).
1079 DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_);
1081 return OK;
1084 // SIZE command
1085 int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1086 std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
1087 next_state_ = STATE_CTRL_READ;
1088 return SendFtpCommand(command, command, COMMAND_SIZE);
1091 int FtpNetworkTransaction::ProcessResponseSIZE(
1092 const FtpCtrlResponse& response) {
1093 State state_after_size;
1094 if (resource_type_ == RESOURCE_TYPE_FILE)
1095 state_after_size = STATE_CTRL_WRITE_RETR;
1096 else
1097 state_after_size = STATE_CTRL_WRITE_CWD;
1099 switch (GetErrorClass(response.status_code)) {
1100 case ERROR_CLASS_INITIATED:
1101 next_state_ = state_after_size;
1102 break;
1103 case ERROR_CLASS_OK:
1104 if (response.lines.size() != 1)
1105 return Stop(ERR_INVALID_RESPONSE);
1106 int64 size;
1107 if (!base::StringToInt64(response.lines[0], &size))
1108 return Stop(ERR_INVALID_RESPONSE);
1109 if (size < 0)
1110 return Stop(ERR_INVALID_RESPONSE);
1112 // A successful response to SIZE does not mean the resource is a file.
1113 // Some FTP servers (for example, the qnx one) send a SIZE even for
1114 // directories.
1115 response_.expected_content_size = size;
1117 next_state_ = state_after_size;
1118 break;
1119 case ERROR_CLASS_INFO_NEEDED:
1120 next_state_ = state_after_size;
1121 break;
1122 case ERROR_CLASS_TRANSIENT_ERROR:
1123 ResetDataConnectionAfterError(state_after_size);
1124 break;
1125 case ERROR_CLASS_PERMANENT_ERROR:
1126 // It's possible that SIZE failed because the path is a directory.
1127 if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
1128 response.status_code != 550) {
1129 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1132 ResetDataConnectionAfterError(state_after_size);
1133 break;
1134 default:
1135 NOTREACHED();
1136 return Stop(ERR_UNEXPECTED);
1139 return OK;
1142 // CWD command
1143 int FtpNetworkTransaction::DoCtrlWriteCWD() {
1144 std::string command = "CWD " + GetRequestPathForFtpCommand(true);
1145 next_state_ = STATE_CTRL_READ;
1146 return SendFtpCommand(command, command, COMMAND_CWD);
1149 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
1150 // We should never issue CWD if we know the target resource is a file.
1151 DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
1153 switch (GetErrorClass(response.status_code)) {
1154 case ERROR_CLASS_INITIATED:
1155 return Stop(ERR_INVALID_RESPONSE);
1156 case ERROR_CLASS_OK:
1157 next_state_ = STATE_CTRL_WRITE_LIST;
1158 break;
1159 case ERROR_CLASS_INFO_NEEDED:
1160 return Stop(ERR_INVALID_RESPONSE);
1161 case ERROR_CLASS_TRANSIENT_ERROR:
1162 // Some FTP servers send response 451 (not a valid CWD response according
1163 // to RFC 959) instead of 550.
1164 if (response.status_code == 451)
1165 return ProcessResponseCWDNotADirectory();
1167 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1168 case ERROR_CLASS_PERMANENT_ERROR:
1169 if (response.status_code == 550)
1170 return ProcessResponseCWDNotADirectory();
1172 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1173 default:
1174 NOTREACHED();
1175 return Stop(ERR_UNEXPECTED);
1178 return OK;
1181 int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1182 if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
1183 // We're assuming that the resource is a directory, but the server
1184 // says it's not true. The most probable interpretation is that it
1185 // doesn't exist (with FTP we can't be sure).
1186 return Stop(ERR_FILE_NOT_FOUND);
1189 // We are here because SIZE failed and we are not sure what the resource
1190 // type is. It could still be file, and SIZE could fail because of
1191 // an access error (http://crbug.com/56734). Try RETR just to be sure.
1192 resource_type_ = RESOURCE_TYPE_FILE;
1194 ResetDataConnectionAfterError(STATE_CTRL_WRITE_RETR);
1195 return OK;
1198 // LIST command
1199 int FtpNetworkTransaction::DoCtrlWriteLIST() {
1200 // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1201 // forces LIST output instead of NLST (which would be ambiguous for us
1202 // to parse).
1203 std::string command("LIST -l");
1204 if (system_type_ == SYSTEM_TYPE_VMS)
1205 command = "LIST *.*;0";
1207 next_state_ = STATE_CTRL_READ;
1208 return SendFtpCommand(command, command, COMMAND_LIST);
1211 int FtpNetworkTransaction::ProcessResponseLIST(
1212 const FtpCtrlResponse& response) {
1213 switch (GetErrorClass(response.status_code)) {
1214 case ERROR_CLASS_INITIATED:
1215 // We want the client to start reading the response at this point.
1216 // It got here either through Start or RestartWithAuth. We want that
1217 // method to complete. Not setting next state here will make DoLoop exit
1218 // and in turn make Start/RestartWithAuth complete.
1219 response_.is_directory_listing = true;
1220 break;
1221 case ERROR_CLASS_OK:
1222 response_.is_directory_listing = true;
1223 next_state_ = STATE_CTRL_WRITE_QUIT;
1224 break;
1225 case ERROR_CLASS_INFO_NEEDED:
1226 return Stop(ERR_INVALID_RESPONSE);
1227 case ERROR_CLASS_TRANSIENT_ERROR:
1228 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1229 case ERROR_CLASS_PERMANENT_ERROR:
1230 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1231 default:
1232 NOTREACHED();
1233 return Stop(ERR_UNEXPECTED);
1235 return OK;
1238 // QUIT command
1239 int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1240 std::string command = "QUIT";
1241 next_state_ = STATE_CTRL_READ;
1242 return SendFtpCommand(command, command, COMMAND_QUIT);
1245 int FtpNetworkTransaction::ProcessResponseQUIT(
1246 const FtpCtrlResponse& response) {
1247 ctrl_socket_->Disconnect();
1248 return last_error_;
1251 // Data Connection
1253 int FtpNetworkTransaction::DoDataConnect() {
1254 next_state_ = STATE_DATA_CONNECT_COMPLETE;
1255 IPEndPoint ip_endpoint;
1256 AddressList data_address;
1257 // Connect to the same host as the control socket to prevent PASV port
1258 // scanning attacks.
1259 int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
1260 if (rv != OK)
1261 return Stop(rv);
1262 data_address = AddressList::CreateFromIPAddress(
1263 ip_endpoint.address(), data_connection_port_);
1264 data_socket_ = socket_factory_->CreateTransportClientSocket(
1265 data_address, net_log_.net_log(), net_log_.source());
1266 net_log_.AddEvent(
1267 NetLog::TYPE_FTP_DATA_CONNECTION,
1268 data_socket_->NetLog().source().ToEventParametersCallback());
1269 return data_socket_->Connect(io_callback_);
1272 int FtpNetworkTransaction::DoDataConnectComplete(int result) {
1273 if (result != OK && use_epsv_) {
1274 // It's possible we hit a broken server, sadly. They can break in different
1275 // ways. Some time out, some reset a connection. Fall back to PASV.
1276 // TODO(phajdan.jr): remember it for future transactions with this server.
1277 // TODO(phajdan.jr): write a test for this code path.
1278 use_epsv_ = false;
1279 next_state_ = STATE_CTRL_WRITE_PASV;
1280 return OK;
1283 // Only record the connection error after we've applied all our fallbacks.
1284 // We want to capture the final error, one we're not going to recover from.
1285 RecordDataConnectionError(result);
1287 if (result != OK)
1288 return Stop(result);
1290 next_state_ = state_after_data_connect_complete_;
1291 return OK;
1294 int FtpNetworkTransaction::DoDataRead() {
1295 DCHECK(read_data_buf_.get());
1296 DCHECK_GT(read_data_buf_len_, 0);
1298 if (data_socket_ == NULL || !data_socket_->IsConnected()) {
1299 // If we don't destroy the data socket completely, some servers will wait
1300 // for us (http://crbug.com/21127). The half-closed TCP connection needs
1301 // to be closed on our side too.
1302 data_socket_.reset();
1304 if (ctrl_socket_->IsConnected()) {
1305 // Wait for the server's response, we should get it before sending QUIT.
1306 next_state_ = STATE_CTRL_READ;
1307 return OK;
1310 // We are no longer connected to the server, so just finish the transaction.
1311 return Stop(OK);
1314 next_state_ = STATE_DATA_READ_COMPLETE;
1315 read_data_buf_->data()[0] = 0;
1316 return data_socket_->Read(
1317 read_data_buf_.get(), read_data_buf_len_, io_callback_);
1320 int FtpNetworkTransaction::DoDataReadComplete(int result) {
1321 return result;
1324 // We're using a histogram as a group of counters, with one bucket for each
1325 // enumeration value. We're only interested in the values of the counters.
1326 // Ignore the shape, average, and standard deviation of the histograms because
1327 // they are meaningless.
1329 // We use two histograms. In the first histogram we tally whether the user has
1330 // seen an error of that type during the session. In the second histogram we
1331 // tally the total number of times the users sees each errer.
1332 void FtpNetworkTransaction::RecordDataConnectionError(int result) {
1333 // Gather data for http://crbug.com/3073. See how many users have trouble
1334 // establishing FTP data connection in passive FTP mode.
1335 enum {
1336 // Data connection successful.
1337 NET_ERROR_OK = 0,
1339 // Local firewall blocked the connection.
1340 NET_ERROR_ACCESS_DENIED = 1,
1342 // Connection timed out.
1343 NET_ERROR_TIMED_OUT = 2,
1345 // Connection has been estabilished, but then got broken (either reset
1346 // or aborted).
1347 NET_ERROR_CONNECTION_BROKEN = 3,
1349 // Connection has been refused.
1350 NET_ERROR_CONNECTION_REFUSED = 4,
1352 // No connection to the internet.
1353 NET_ERROR_INTERNET_DISCONNECTED = 5,
1355 // Could not reach the destination address.
1356 NET_ERROR_ADDRESS_UNREACHABLE = 6,
1358 // A programming error in our network stack.
1359 NET_ERROR_UNEXPECTED = 7,
1361 // Other kind of error.
1362 NET_ERROR_OTHER = 20,
1364 NUM_OF_NET_ERROR_TYPES
1365 } type;
1366 switch (result) {
1367 case OK:
1368 type = NET_ERROR_OK;
1369 break;
1370 case ERR_ACCESS_DENIED:
1371 case ERR_NETWORK_ACCESS_DENIED:
1372 type = NET_ERROR_ACCESS_DENIED;
1373 break;
1374 case ERR_TIMED_OUT:
1375 type = NET_ERROR_TIMED_OUT;
1376 break;
1377 case ERR_CONNECTION_ABORTED:
1378 case ERR_CONNECTION_RESET:
1379 case ERR_CONNECTION_CLOSED:
1380 type = NET_ERROR_CONNECTION_BROKEN;
1381 break;
1382 case ERR_CONNECTION_FAILED:
1383 case ERR_CONNECTION_REFUSED:
1384 type = NET_ERROR_CONNECTION_REFUSED;
1385 break;
1386 case ERR_INTERNET_DISCONNECTED:
1387 type = NET_ERROR_INTERNET_DISCONNECTED;
1388 break;
1389 case ERR_ADDRESS_INVALID:
1390 case ERR_ADDRESS_UNREACHABLE:
1391 type = NET_ERROR_ADDRESS_UNREACHABLE;
1392 break;
1393 case ERR_UNEXPECTED:
1394 type = NET_ERROR_UNEXPECTED;
1395 break;
1396 default:
1397 type = NET_ERROR_OTHER;
1398 break;
1400 static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
1402 DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
1403 if (!had_error_type[type]) {
1404 had_error_type[type] = true;
1405 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1406 type, NUM_OF_NET_ERROR_TYPES);
1408 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1409 type, NUM_OF_NET_ERROR_TYPES);
1412 } // namespace net