Re-land: C++ readability review
[chromium-blink-merge.git] / net / ftp / ftp_network_transaction.cc
blob814be921f3eba4c75313536384662d0b46f7af71
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_split.h"
14 #include "base/strings/string_util.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_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"
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_NONE) {
230 FtpNetworkTransaction::~FtpNetworkTransaction() {
233 int FtpNetworkTransaction::Stop(int error) {
234 if (command_sent_ == COMMAND_QUIT)
235 return error;
237 next_state_ = STATE_CTRL_WRITE_QUIT;
238 last_error_ = error;
239 return OK;
242 int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
243 const CompletionCallback& callback,
244 const BoundNetLog& net_log) {
245 net_log_ = net_log;
246 request_ = request_info;
248 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
250 if (request_->url.has_username()) {
251 base::string16 username;
252 base::string16 password;
253 GetIdentityFromURL(request_->url, &username, &password);
254 credentials_.Set(username, password);
255 } else {
256 credentials_.Set(base::ASCIIToUTF16("anonymous"),
257 base::ASCIIToUTF16("chrome@example.com"));
260 DetectTypecode();
262 next_state_ = STATE_CTRL_RESOLVE_HOST;
263 int rv = DoLoop(OK);
264 if (rv == ERR_IO_PENDING)
265 user_callback_ = callback;
266 return rv;
269 int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
270 const CompletionCallback& callback) {
271 ResetStateForRestart();
273 credentials_ = credentials;
275 next_state_ = STATE_CTRL_RESOLVE_HOST;
276 int rv = DoLoop(OK);
277 if (rv == ERR_IO_PENDING)
278 user_callback_ = callback;
279 return rv;
282 int FtpNetworkTransaction::Read(IOBuffer* buf,
283 int buf_len,
284 const CompletionCallback& callback) {
285 DCHECK(buf);
286 DCHECK_GT(buf_len, 0);
288 read_data_buf_ = buf;
289 read_data_buf_len_ = buf_len;
291 next_state_ = STATE_DATA_READ;
292 int rv = DoLoop(OK);
293 if (rv == ERR_IO_PENDING)
294 user_callback_ = callback;
295 return rv;
298 const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
299 return &response_;
302 LoadState FtpNetworkTransaction::GetLoadState() const {
303 if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
304 return LOAD_STATE_RESOLVING_HOST;
306 if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
307 next_state_ == STATE_DATA_CONNECT_COMPLETE)
308 return LOAD_STATE_CONNECTING;
310 if (next_state_ == STATE_DATA_READ_COMPLETE)
311 return LOAD_STATE_READING_RESPONSE;
313 if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
314 return LOAD_STATE_READING_RESPONSE;
316 if (command_sent_ == COMMAND_QUIT)
317 return LOAD_STATE_IDLE;
319 if (command_sent_ != COMMAND_NONE)
320 return LOAD_STATE_SENDING_REQUEST;
322 return LOAD_STATE_IDLE;
325 uint64 FtpNetworkTransaction::GetUploadProgress() const {
326 return 0;
329 void FtpNetworkTransaction::ResetStateForRestart() {
330 command_sent_ = COMMAND_NONE;
331 user_callback_.Reset();
332 response_ = FtpResponseInfo();
333 read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
334 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
335 read_data_buf_ = NULL;
336 read_data_buf_len_ = 0;
337 if (write_buf_.get())
338 write_buf_->SetOffset(0);
339 last_error_ = OK;
340 data_connection_port_ = 0;
341 ctrl_socket_.reset();
342 data_socket_.reset();
343 next_state_ = STATE_NONE;
344 state_after_data_connect_complete_ = STATE_NONE;
347 void FtpNetworkTransaction::EstablishDataConnection(State state_after_connect) {
348 DCHECK(state_after_connect == STATE_CTRL_WRITE_RETR ||
349 state_after_connect == STATE_CTRL_WRITE_LIST);
350 state_after_data_connect_complete_ = state_after_connect;
351 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
354 void FtpNetworkTransaction::DoCallback(int rv) {
355 DCHECK(rv != ERR_IO_PENDING);
356 DCHECK(!user_callback_.is_null());
358 // Since Run may result in Read being called, clear callback_ up front.
359 CompletionCallback c = user_callback_;
360 user_callback_.Reset();
361 c.Run(rv);
364 void FtpNetworkTransaction::OnIOComplete(int result) {
365 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed.
366 tracked_objects::ScopedTracker tracking_profile(
367 FROM_HERE_WITH_EXPLICIT_FUNCTION(
368 "436634 FtpNetworkTransaction::OnIOComplete"));
370 int rv = DoLoop(result);
371 if (rv != ERR_IO_PENDING)
372 DoCallback(rv);
375 int FtpNetworkTransaction::ProcessCtrlResponse() {
376 FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
378 int rv = OK;
379 switch (command_sent_) {
380 case COMMAND_NONE:
381 // TODO(phajdan.jr): Check for errors in the welcome message.
382 next_state_ = STATE_CTRL_WRITE_USER;
383 break;
384 case COMMAND_USER:
385 rv = ProcessResponseUSER(response);
386 break;
387 case COMMAND_PASS:
388 rv = ProcessResponsePASS(response);
389 break;
390 case COMMAND_SYST:
391 rv = ProcessResponseSYST(response);
392 break;
393 case COMMAND_PWD:
394 rv = ProcessResponsePWD(response);
395 break;
396 case COMMAND_TYPE:
397 rv = ProcessResponseTYPE(response);
398 break;
399 case COMMAND_EPSV:
400 rv = ProcessResponseEPSV(response);
401 break;
402 case COMMAND_PASV:
403 rv = ProcessResponsePASV(response);
404 break;
405 case COMMAND_SIZE:
406 rv = ProcessResponseSIZE(response);
407 break;
408 case COMMAND_RETR:
409 rv = ProcessResponseRETR(response);
410 break;
411 case COMMAND_CWD:
412 rv = ProcessResponseCWD(response);
413 break;
414 case COMMAND_LIST:
415 rv = ProcessResponseLIST(response);
416 break;
417 case COMMAND_QUIT:
418 rv = ProcessResponseQUIT(response);
419 break;
420 default:
421 LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
422 return ERR_UNEXPECTED;
425 // We may get multiple responses for some commands,
426 // see http://crbug.com/18036.
427 while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
428 response = ctrl_response_buffer_->PopResponse();
430 switch (command_sent_) {
431 case COMMAND_RETR:
432 rv = ProcessResponseRETR(response);
433 break;
434 case COMMAND_LIST:
435 rv = ProcessResponseLIST(response);
436 break;
437 default:
438 // Multiple responses for other commands are invalid.
439 return Stop(ERR_INVALID_RESPONSE);
443 return rv;
446 // Used to prepare and send FTP command.
447 int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
448 const std::string& command_for_log,
449 Command cmd) {
450 // If we send a new command when we still have unprocessed responses
451 // for previous commands, the response receiving code will have no way to know
452 // which responses are for which command.
453 DCHECK(!ctrl_response_buffer_->ResponseAvailable());
455 DCHECK(!write_command_buf_.get());
456 DCHECK(!write_buf_.get());
458 if (!IsValidFTPCommandString(command)) {
459 // Callers should validate the command themselves and return a more specific
460 // error code.
461 NOTREACHED();
462 return Stop(ERR_UNEXPECTED);
465 command_sent_ = cmd;
467 write_command_buf_ = new IOBufferWithSize(command.length() + 2);
468 write_buf_ = new DrainableIOBuffer(write_command_buf_.get(),
469 write_command_buf_->size());
470 memcpy(write_command_buf_->data(), command.data(), command.length());
471 memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
473 net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
474 NetLog::StringCallback("command", &command_for_log));
476 next_state_ = STATE_CTRL_WRITE;
477 return OK;
480 std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
481 bool is_directory) const {
482 std::string path(current_remote_directory_);
483 if (request_->url.has_path()) {
484 std::string gurl_path(request_->url.path());
486 // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
487 std::string::size_type pos = gurl_path.rfind(';');
488 if (pos != std::string::npos)
489 gurl_path.resize(pos);
491 path.append(gurl_path);
493 // Make sure that if the path is expected to be a file, it won't end
494 // with a trailing slash.
495 if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
496 path.erase(path.length() - 1);
497 UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
498 UnescapeRule::URL_SPECIAL_CHARS;
499 // This may unescape to non-ASCII characters, but we allow that. See the
500 // comment for IsValidFTPCommandString.
501 path = net::UnescapeURLComponent(path, unescape_rules);
503 if (system_type_ == SYSTEM_TYPE_VMS) {
504 if (is_directory)
505 path = FtpUtil::UnixDirectoryPathToVMS(path);
506 else
507 path = FtpUtil::UnixFilePathToVMS(path);
510 DCHECK(IsValidFTPCommandString(path));
511 return path;
514 void FtpNetworkTransaction::DetectTypecode() {
515 if (!request_->url.has_path())
516 return;
517 std::string gurl_path(request_->url.path());
519 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
520 std::string::size_type pos = gurl_path.rfind(';');
521 if (pos == std::string::npos)
522 return;
523 std::string typecode_string(gurl_path.substr(pos));
524 if (typecode_string == ";type=a") {
525 data_type_ = DATA_TYPE_ASCII;
526 resource_type_ = RESOURCE_TYPE_FILE;
527 } else if (typecode_string == ";type=i") {
528 data_type_ = DATA_TYPE_IMAGE;
529 resource_type_ = RESOURCE_TYPE_FILE;
530 } else if (typecode_string == ";type=d") {
531 resource_type_ = RESOURCE_TYPE_DIRECTORY;
535 int FtpNetworkTransaction::DoLoop(int result) {
536 DCHECK(next_state_ != STATE_NONE);
538 int rv = result;
539 do {
540 State state = next_state_;
541 next_state_ = STATE_NONE;
542 switch (state) {
543 case STATE_CTRL_RESOLVE_HOST:
544 DCHECK(rv == OK);
545 rv = DoCtrlResolveHost();
546 break;
547 case STATE_CTRL_RESOLVE_HOST_COMPLETE:
548 rv = DoCtrlResolveHostComplete(rv);
549 break;
550 case STATE_CTRL_CONNECT:
551 DCHECK(rv == OK);
552 rv = DoCtrlConnect();
553 break;
554 case STATE_CTRL_CONNECT_COMPLETE:
555 rv = DoCtrlConnectComplete(rv);
556 break;
557 case STATE_CTRL_READ:
558 DCHECK(rv == OK);
559 rv = DoCtrlRead();
560 break;
561 case STATE_CTRL_READ_COMPLETE:
562 rv = DoCtrlReadComplete(rv);
563 break;
564 case STATE_CTRL_WRITE:
565 DCHECK(rv == OK);
566 rv = DoCtrlWrite();
567 break;
568 case STATE_CTRL_WRITE_COMPLETE:
569 rv = DoCtrlWriteComplete(rv);
570 break;
571 case STATE_CTRL_WRITE_USER:
572 DCHECK(rv == OK);
573 rv = DoCtrlWriteUSER();
574 break;
575 case STATE_CTRL_WRITE_PASS:
576 DCHECK(rv == OK);
577 rv = DoCtrlWritePASS();
578 break;
579 case STATE_CTRL_WRITE_SYST:
580 DCHECK(rv == OK);
581 rv = DoCtrlWriteSYST();
582 break;
583 case STATE_CTRL_WRITE_PWD:
584 DCHECK(rv == OK);
585 rv = DoCtrlWritePWD();
586 break;
587 case STATE_CTRL_WRITE_TYPE:
588 DCHECK(rv == OK);
589 rv = DoCtrlWriteTYPE();
590 break;
591 case STATE_CTRL_WRITE_EPSV:
592 DCHECK(rv == OK);
593 rv = DoCtrlWriteEPSV();
594 break;
595 case STATE_CTRL_WRITE_PASV:
596 DCHECK(rv == OK);
597 rv = DoCtrlWritePASV();
598 break;
599 case STATE_CTRL_WRITE_RETR:
600 DCHECK(rv == OK);
601 rv = DoCtrlWriteRETR();
602 break;
603 case STATE_CTRL_WRITE_SIZE:
604 DCHECK(rv == OK);
605 rv = DoCtrlWriteSIZE();
606 break;
607 case STATE_CTRL_WRITE_CWD:
608 DCHECK(rv == OK);
609 rv = DoCtrlWriteCWD();
610 break;
611 case STATE_CTRL_WRITE_LIST:
612 DCHECK(rv == OK);
613 rv = DoCtrlWriteLIST();
614 break;
615 case STATE_CTRL_WRITE_QUIT:
616 DCHECK(rv == OK);
617 rv = DoCtrlWriteQUIT();
618 break;
619 case STATE_DATA_CONNECT:
620 DCHECK(rv == OK);
621 rv = DoDataConnect();
622 break;
623 case STATE_DATA_CONNECT_COMPLETE:
624 rv = DoDataConnectComplete(rv);
625 break;
626 case STATE_DATA_READ:
627 DCHECK(rv == OK);
628 rv = DoDataRead();
629 break;
630 case STATE_DATA_READ_COMPLETE:
631 rv = DoDataReadComplete(rv);
632 break;
633 default:
634 NOTREACHED() << "bad state";
635 rv = ERR_UNEXPECTED;
636 break;
638 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
639 return rv;
642 int FtpNetworkTransaction::DoCtrlResolveHost() {
643 next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
645 HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
646 // No known referrer.
647 return resolver_.Resolve(
648 info,
649 DEFAULT_PRIORITY,
650 &addresses_,
651 base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
652 net_log_);
655 int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
656 if (result == OK)
657 next_state_ = STATE_CTRL_CONNECT;
658 return result;
661 int FtpNetworkTransaction::DoCtrlConnect() {
662 next_state_ = STATE_CTRL_CONNECT_COMPLETE;
663 ctrl_socket_ = socket_factory_->CreateTransportClientSocket(
664 addresses_, net_log_.net_log(), net_log_.source());
665 net_log_.AddEvent(
666 NetLog::TYPE_FTP_CONTROL_CONNECTION,
667 ctrl_socket_->NetLog().source().ToEventParametersCallback());
668 return ctrl_socket_->Connect(io_callback_);
671 int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
672 if (result == OK) {
673 // Put the peer's IP address and port into the response.
674 IPEndPoint ip_endpoint;
675 result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
676 if (result == OK) {
677 response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
678 next_state_ = STATE_CTRL_READ;
680 if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
681 // Do not use EPSV for IPv4 connections. Some servers become confused
682 // and we time out while waiting to connect. PASV is perfectly fine for
683 // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
684 // whitelisting IPv6 to use it, to make the code more future-proof:
685 // all future protocols should just use EPSV.
686 use_epsv_ = false;
690 return result;
693 int FtpNetworkTransaction::DoCtrlRead() {
694 next_state_ = STATE_CTRL_READ_COMPLETE;
695 return ctrl_socket_->Read(read_ctrl_buf_.get(), kCtrlBufLen, io_callback_);
698 int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
699 if (result == 0) {
700 // Some servers (for example Pure-FTPd) apparently close the control
701 // connection when anonymous login is not permitted. For more details
702 // see http://crbug.com/25023.
703 if (command_sent_ == COMMAND_USER &&
704 credentials_.username() == base::ASCIIToUTF16("anonymous")) {
705 response_.needs_auth = true;
707 return Stop(ERR_EMPTY_RESPONSE);
709 if (result < 0)
710 return Stop(result);
712 ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
714 if (!ctrl_response_buffer_->ResponseAvailable()) {
715 // Read more data from the control socket.
716 next_state_ = STATE_CTRL_READ;
717 return OK;
720 return ProcessCtrlResponse();
723 int FtpNetworkTransaction::DoCtrlWrite() {
724 next_state_ = STATE_CTRL_WRITE_COMPLETE;
726 return ctrl_socket_->Write(
727 write_buf_.get(), write_buf_->BytesRemaining(), io_callback_);
730 int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
731 if (result < 0)
732 return result;
734 write_buf_->DidConsume(result);
735 if (write_buf_->BytesRemaining() == 0) {
736 // Clear the write buffer.
737 write_buf_ = NULL;
738 write_command_buf_ = NULL;
740 next_state_ = STATE_CTRL_READ;
741 } else {
742 next_state_ = STATE_CTRL_WRITE;
744 return OK;
747 // FTP Commands and responses
749 // USER Command.
750 int FtpNetworkTransaction::DoCtrlWriteUSER() {
751 std::string command = "USER " + base::UTF16ToUTF8(credentials_.username());
753 if (!IsValidFTPCommandString(command))
754 return Stop(ERR_MALFORMED_IDENTITY);
756 next_state_ = STATE_CTRL_READ;
757 return SendFtpCommand(command, "USER ***", COMMAND_USER);
760 int FtpNetworkTransaction::ProcessResponseUSER(
761 const FtpCtrlResponse& response) {
762 switch (GetErrorClass(response.status_code)) {
763 case ERROR_CLASS_OK:
764 next_state_ = STATE_CTRL_WRITE_SYST;
765 break;
766 case ERROR_CLASS_INFO_NEEDED:
767 next_state_ = STATE_CTRL_WRITE_PASS;
768 break;
769 case ERROR_CLASS_TRANSIENT_ERROR:
770 case ERROR_CLASS_PERMANENT_ERROR:
771 response_.needs_auth = true;
772 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
773 default:
774 NOTREACHED();
775 return Stop(ERR_UNEXPECTED);
777 return OK;
780 // PASS command.
781 int FtpNetworkTransaction::DoCtrlWritePASS() {
782 std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password());
784 if (!IsValidFTPCommandString(command))
785 return Stop(ERR_MALFORMED_IDENTITY);
787 next_state_ = STATE_CTRL_READ;
788 return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
791 int FtpNetworkTransaction::ProcessResponsePASS(
792 const FtpCtrlResponse& response) {
793 switch (GetErrorClass(response.status_code)) {
794 case ERROR_CLASS_OK:
795 next_state_ = STATE_CTRL_WRITE_SYST;
796 break;
797 case ERROR_CLASS_INFO_NEEDED:
798 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
799 case ERROR_CLASS_TRANSIENT_ERROR:
800 case ERROR_CLASS_PERMANENT_ERROR:
801 response_.needs_auth = true;
802 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
803 default:
804 NOTREACHED();
805 return Stop(ERR_UNEXPECTED);
807 return OK;
810 // SYST command.
811 int FtpNetworkTransaction::DoCtrlWriteSYST() {
812 std::string command = "SYST";
813 next_state_ = STATE_CTRL_READ;
814 return SendFtpCommand(command, command, COMMAND_SYST);
817 int FtpNetworkTransaction::ProcessResponseSYST(
818 const FtpCtrlResponse& response) {
819 switch (GetErrorClass(response.status_code)) {
820 case ERROR_CLASS_INITIATED:
821 return Stop(ERR_INVALID_RESPONSE);
822 case ERROR_CLASS_OK: {
823 // All important info should be on the first line.
824 std::string line = response.lines[0];
825 // The response should be ASCII, which allows us to do case-insensitive
826 // comparisons easily. If it is not ASCII, we leave the system type
827 // as unknown.
828 if (base::IsStringASCII(line)) {
829 line = base::StringToLowerASCII(line);
831 // Remove all whitespace, to correctly handle cases like fancy "V M S"
832 // response instead of "VMS".
833 base::RemoveChars(line, base::kWhitespaceASCII, &line);
835 // The "magic" strings we test for below have been gathered by an
836 // empirical study. VMS needs to come first because some VMS systems
837 // also respond with "UNIX emulation", which is not perfect. It is much
838 // more reliable to talk to these servers in their native language.
839 if (line.find("vms") != std::string::npos) {
840 system_type_ = SYSTEM_TYPE_VMS;
841 } else if (line.find("l8") != std::string::npos ||
842 line.find("unix") != std::string::npos ||
843 line.find("bsd") != std::string::npos) {
844 system_type_ = SYSTEM_TYPE_UNIX;
845 } else if (line.find("win32") != std::string::npos ||
846 line.find("windows") != std::string::npos) {
847 system_type_ = SYSTEM_TYPE_WINDOWS;
848 } else if (line.find("os/2") != std::string::npos) {
849 system_type_ = SYSTEM_TYPE_OS2;
852 next_state_ = STATE_CTRL_WRITE_PWD;
853 break;
855 case ERROR_CLASS_INFO_NEEDED:
856 return Stop(ERR_INVALID_RESPONSE);
857 case ERROR_CLASS_TRANSIENT_ERROR:
858 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
859 case ERROR_CLASS_PERMANENT_ERROR:
860 // Server does not recognize the SYST command so proceed.
861 next_state_ = STATE_CTRL_WRITE_PWD;
862 break;
863 default:
864 NOTREACHED();
865 return Stop(ERR_UNEXPECTED);
867 return OK;
870 // PWD command.
871 int FtpNetworkTransaction::DoCtrlWritePWD() {
872 std::string command = "PWD";
873 next_state_ = STATE_CTRL_READ;
874 return SendFtpCommand(command, command, COMMAND_PWD);
877 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
878 switch (GetErrorClass(response.status_code)) {
879 case ERROR_CLASS_INITIATED:
880 return Stop(ERR_INVALID_RESPONSE);
881 case ERROR_CLASS_OK: {
882 // The info we look for should be on the first line.
883 std::string line = response.lines[0];
884 if (line.empty())
885 return Stop(ERR_INVALID_RESPONSE);
886 std::string::size_type quote_pos = line.find('"');
887 if (quote_pos != std::string::npos) {
888 line = line.substr(quote_pos + 1);
889 quote_pos = line.find('"');
890 if (quote_pos == std::string::npos)
891 return Stop(ERR_INVALID_RESPONSE);
892 line = line.substr(0, quote_pos);
894 if (system_type_ == SYSTEM_TYPE_VMS)
895 line = FtpUtil::VMSPathToUnix(line);
896 if (line.length() && line[line.length() - 1] == '/')
897 line.erase(line.length() - 1);
898 current_remote_directory_ = line;
899 next_state_ = STATE_CTRL_WRITE_TYPE;
900 break;
902 case ERROR_CLASS_INFO_NEEDED:
903 return Stop(ERR_INVALID_RESPONSE);
904 case ERROR_CLASS_TRANSIENT_ERROR:
905 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
906 case ERROR_CLASS_PERMANENT_ERROR:
907 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
908 default:
909 NOTREACHED();
910 return Stop(ERR_UNEXPECTED);
912 return OK;
915 // TYPE command.
916 int FtpNetworkTransaction::DoCtrlWriteTYPE() {
917 std::string command = "TYPE ";
918 if (data_type_ == DATA_TYPE_ASCII) {
919 command += "A";
920 } else if (data_type_ == DATA_TYPE_IMAGE) {
921 command += "I";
922 } else {
923 NOTREACHED();
924 return Stop(ERR_UNEXPECTED);
926 next_state_ = STATE_CTRL_READ;
927 return SendFtpCommand(command, command, COMMAND_TYPE);
930 int FtpNetworkTransaction::ProcessResponseTYPE(
931 const FtpCtrlResponse& response) {
932 switch (GetErrorClass(response.status_code)) {
933 case ERROR_CLASS_INITIATED:
934 return Stop(ERR_INVALID_RESPONSE);
935 case ERROR_CLASS_OK:
936 next_state_ = STATE_CTRL_WRITE_SIZE;
937 break;
938 case ERROR_CLASS_INFO_NEEDED:
939 return Stop(ERR_INVALID_RESPONSE);
940 case ERROR_CLASS_TRANSIENT_ERROR:
941 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
942 case ERROR_CLASS_PERMANENT_ERROR:
943 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
944 default:
945 NOTREACHED();
946 return Stop(ERR_UNEXPECTED);
948 return OK;
951 // EPSV command
952 int FtpNetworkTransaction::DoCtrlWriteEPSV() {
953 const std::string command = "EPSV";
954 next_state_ = STATE_CTRL_READ;
955 return SendFtpCommand(command, command, COMMAND_EPSV);
958 int FtpNetworkTransaction::ProcessResponseEPSV(
959 const FtpCtrlResponse& response) {
960 switch (GetErrorClass(response.status_code)) {
961 case ERROR_CLASS_INITIATED:
962 return Stop(ERR_INVALID_RESPONSE);
963 case ERROR_CLASS_OK: {
964 int port;
965 if (!ExtractPortFromEPSVResponse(response, &port))
966 return Stop(ERR_INVALID_RESPONSE);
967 if (port < 1024 || !IsPortAllowedByFtp(port))
968 return Stop(ERR_UNSAFE_PORT);
969 data_connection_port_ = static_cast<uint16>(port);
970 next_state_ = STATE_DATA_CONNECT;
971 break;
973 case ERROR_CLASS_INFO_NEEDED:
974 return Stop(ERR_INVALID_RESPONSE);
975 case ERROR_CLASS_TRANSIENT_ERROR:
976 case ERROR_CLASS_PERMANENT_ERROR:
977 use_epsv_ = false;
978 next_state_ = STATE_CTRL_WRITE_PASV;
979 return OK;
980 default:
981 NOTREACHED();
982 return Stop(ERR_UNEXPECTED);
984 return OK;
987 // PASV command
988 int FtpNetworkTransaction::DoCtrlWritePASV() {
989 std::string command = "PASV";
990 next_state_ = STATE_CTRL_READ;
991 return SendFtpCommand(command, command, COMMAND_PASV);
994 int FtpNetworkTransaction::ProcessResponsePASV(
995 const FtpCtrlResponse& response) {
996 switch (GetErrorClass(response.status_code)) {
997 case ERROR_CLASS_INITIATED:
998 return Stop(ERR_INVALID_RESPONSE);
999 case ERROR_CLASS_OK: {
1000 int port;
1001 if (!ExtractPortFromPASVResponse(response, &port))
1002 return Stop(ERR_INVALID_RESPONSE);
1003 if (port < 1024 || !IsPortAllowedByFtp(port))
1004 return Stop(ERR_UNSAFE_PORT);
1005 data_connection_port_ = static_cast<uint16>(port);
1006 next_state_ = STATE_DATA_CONNECT;
1007 break;
1009 case ERROR_CLASS_INFO_NEEDED:
1010 return Stop(ERR_INVALID_RESPONSE);
1011 case ERROR_CLASS_TRANSIENT_ERROR:
1012 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1013 case ERROR_CLASS_PERMANENT_ERROR:
1014 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1015 default:
1016 NOTREACHED();
1017 return Stop(ERR_UNEXPECTED);
1019 return OK;
1022 // RETR command
1023 int FtpNetworkTransaction::DoCtrlWriteRETR() {
1024 std::string command = "RETR " + GetRequestPathForFtpCommand(false);
1025 next_state_ = STATE_CTRL_READ;
1026 return SendFtpCommand(command, command, COMMAND_RETR);
1029 int FtpNetworkTransaction::ProcessResponseRETR(
1030 const FtpCtrlResponse& response) {
1031 // Resource type should be either filled in by DetectTypecode() or
1032 // detected with CWD. RETR is sent only when the resource is a file.
1033 DCHECK_EQ(RESOURCE_TYPE_FILE, resource_type_);
1035 switch (GetErrorClass(response.status_code)) {
1036 case ERROR_CLASS_INITIATED:
1037 // We want the client to start reading the response at this point.
1038 // It got here either through Start or RestartWithAuth. We want that
1039 // method to complete. Not setting next state here will make DoLoop exit
1040 // and in turn make Start/RestartWithAuth complete.
1041 break;
1042 case ERROR_CLASS_OK:
1043 next_state_ = STATE_CTRL_WRITE_QUIT;
1044 break;
1045 case ERROR_CLASS_INFO_NEEDED:
1046 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1047 case ERROR_CLASS_TRANSIENT_ERROR:
1048 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1049 case ERROR_CLASS_PERMANENT_ERROR:
1050 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1051 default:
1052 NOTREACHED();
1053 return Stop(ERR_UNEXPECTED);
1056 return OK;
1059 // SIZE command
1060 int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1061 std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
1062 next_state_ = STATE_CTRL_READ;
1063 return SendFtpCommand(command, command, COMMAND_SIZE);
1066 int FtpNetworkTransaction::ProcessResponseSIZE(
1067 const FtpCtrlResponse& response) {
1068 switch (GetErrorClass(response.status_code)) {
1069 case ERROR_CLASS_INITIATED:
1070 break;
1071 case ERROR_CLASS_OK:
1072 if (response.lines.size() != 1)
1073 return Stop(ERR_INVALID_RESPONSE);
1074 int64 size;
1075 if (!base::StringToInt64(response.lines[0], &size))
1076 return Stop(ERR_INVALID_RESPONSE);
1077 if (size < 0)
1078 return Stop(ERR_INVALID_RESPONSE);
1080 // A successful response to SIZE does not mean the resource is a file.
1081 // Some FTP servers (for example, the qnx one) send a SIZE even for
1082 // directories.
1083 response_.expected_content_size = size;
1084 break;
1085 case ERROR_CLASS_INFO_NEEDED:
1086 break;
1087 case ERROR_CLASS_TRANSIENT_ERROR:
1088 break;
1089 case ERROR_CLASS_PERMANENT_ERROR:
1090 // It's possible that SIZE failed because the path is a directory.
1091 // TODO(xunjieli): Add a test for this case.
1092 if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
1093 response.status_code != 550) {
1094 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1096 break;
1097 default:
1098 NOTREACHED();
1099 return Stop(ERR_UNEXPECTED);
1102 // If the resource is known beforehand to be a file, RETR should be issued,
1103 // otherwise do CWD which will detect the resource type.
1104 if (resource_type_ == RESOURCE_TYPE_FILE)
1105 EstablishDataConnection(STATE_CTRL_WRITE_RETR);
1106 else
1107 next_state_ = STATE_CTRL_WRITE_CWD;
1108 return OK;
1111 // CWD command
1112 int FtpNetworkTransaction::DoCtrlWriteCWD() {
1113 std::string command = "CWD " + GetRequestPathForFtpCommand(true);
1114 next_state_ = STATE_CTRL_READ;
1115 return SendFtpCommand(command, command, COMMAND_CWD);
1118 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
1119 // CWD should be invoked only when the resource is not a file.
1120 DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
1122 switch (GetErrorClass(response.status_code)) {
1123 case ERROR_CLASS_INITIATED:
1124 return Stop(ERR_INVALID_RESPONSE);
1125 case ERROR_CLASS_OK:
1126 resource_type_ = RESOURCE_TYPE_DIRECTORY;
1127 EstablishDataConnection(STATE_CTRL_WRITE_LIST);
1128 break;
1129 case ERROR_CLASS_INFO_NEEDED:
1130 return Stop(ERR_INVALID_RESPONSE);
1131 case ERROR_CLASS_TRANSIENT_ERROR:
1132 // Some FTP servers send response 451 (not a valid CWD response according
1133 // to RFC 959) instead of 550.
1134 if (response.status_code == 451)
1135 return ProcessResponseCWDNotADirectory();
1137 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1138 case ERROR_CLASS_PERMANENT_ERROR:
1139 if (response.status_code == 550)
1140 return ProcessResponseCWDNotADirectory();
1142 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1143 default:
1144 NOTREACHED();
1145 return Stop(ERR_UNEXPECTED);
1148 return OK;
1151 int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1152 if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
1153 // We're assuming that the resource is a directory, but the server
1154 // says it's not true. The most probable interpretation is that it
1155 // doesn't exist (with FTP we can't be sure).
1156 return Stop(ERR_FILE_NOT_FOUND);
1159 // If it is not a directory, it is probably a file.
1160 resource_type_ = RESOURCE_TYPE_FILE;
1162 EstablishDataConnection(STATE_CTRL_WRITE_RETR);
1163 return OK;
1166 // LIST command
1167 int FtpNetworkTransaction::DoCtrlWriteLIST() {
1168 // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1169 // forces LIST output instead of NLST (which would be ambiguous for us
1170 // to parse).
1171 std::string command("LIST -l");
1172 if (system_type_ == SYSTEM_TYPE_VMS)
1173 command = "LIST *.*;0";
1175 next_state_ = STATE_CTRL_READ;
1176 return SendFtpCommand(command, command, COMMAND_LIST);
1179 int FtpNetworkTransaction::ProcessResponseLIST(
1180 const FtpCtrlResponse& response) {
1181 // Resource type should be either filled in by DetectTypecode() or
1182 // detected with CWD. LIST is sent only when the resource is a directory.
1183 DCHECK_EQ(RESOURCE_TYPE_DIRECTORY, resource_type_);
1185 switch (GetErrorClass(response.status_code)) {
1186 case ERROR_CLASS_INITIATED:
1187 // We want the client to start reading the response at this point.
1188 // It got here either through Start or RestartWithAuth. We want that
1189 // method to complete. Not setting next state here will make DoLoop exit
1190 // and in turn make Start/RestartWithAuth complete.
1191 response_.is_directory_listing = true;
1192 break;
1193 case ERROR_CLASS_OK:
1194 response_.is_directory_listing = true;
1195 next_state_ = STATE_CTRL_WRITE_QUIT;
1196 break;
1197 case ERROR_CLASS_INFO_NEEDED:
1198 return Stop(ERR_INVALID_RESPONSE);
1199 case ERROR_CLASS_TRANSIENT_ERROR:
1200 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1201 case ERROR_CLASS_PERMANENT_ERROR:
1202 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1203 default:
1204 NOTREACHED();
1205 return Stop(ERR_UNEXPECTED);
1207 return OK;
1210 // QUIT command
1211 int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1212 std::string command = "QUIT";
1213 next_state_ = STATE_CTRL_READ;
1214 return SendFtpCommand(command, command, COMMAND_QUIT);
1217 int FtpNetworkTransaction::ProcessResponseQUIT(
1218 const FtpCtrlResponse& response) {
1219 ctrl_socket_->Disconnect();
1220 return last_error_;
1223 // Data Connection
1225 int FtpNetworkTransaction::DoDataConnect() {
1226 next_state_ = STATE_DATA_CONNECT_COMPLETE;
1227 IPEndPoint ip_endpoint;
1228 AddressList data_address;
1229 // Connect to the same host as the control socket to prevent PASV port
1230 // scanning attacks.
1231 int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
1232 if (rv != OK)
1233 return Stop(rv);
1234 data_address = AddressList::CreateFromIPAddress(
1235 ip_endpoint.address(), data_connection_port_);
1236 data_socket_ = socket_factory_->CreateTransportClientSocket(
1237 data_address, net_log_.net_log(), net_log_.source());
1238 net_log_.AddEvent(
1239 NetLog::TYPE_FTP_DATA_CONNECTION,
1240 data_socket_->NetLog().source().ToEventParametersCallback());
1241 return data_socket_->Connect(io_callback_);
1244 int FtpNetworkTransaction::DoDataConnectComplete(int result) {
1245 if (result != OK && use_epsv_) {
1246 // It's possible we hit a broken server, sadly. They can break in different
1247 // ways. Some time out, some reset a connection. Fall back to PASV.
1248 // TODO(phajdan.jr): remember it for future transactions with this server.
1249 // TODO(phajdan.jr): write a test for this code path.
1250 use_epsv_ = false;
1251 next_state_ = STATE_CTRL_WRITE_PASV;
1252 return OK;
1255 // Only record the connection error after we've applied all our fallbacks.
1256 // We want to capture the final error, one we're not going to recover from.
1257 RecordDataConnectionError(result);
1259 if (result != OK)
1260 return Stop(result);
1262 next_state_ = state_after_data_connect_complete_;
1263 return OK;
1266 int FtpNetworkTransaction::DoDataRead() {
1267 DCHECK(read_data_buf_.get());
1268 DCHECK_GT(read_data_buf_len_, 0);
1270 if (data_socket_ == NULL || !data_socket_->IsConnected()) {
1271 // If we don't destroy the data socket completely, some servers will wait
1272 // for us (http://crbug.com/21127). The half-closed TCP connection needs
1273 // to be closed on our side too.
1274 data_socket_.reset();
1276 if (ctrl_socket_->IsConnected()) {
1277 // Wait for the server's response, we should get it before sending QUIT.
1278 next_state_ = STATE_CTRL_READ;
1279 return OK;
1282 // We are no longer connected to the server, so just finish the transaction.
1283 return Stop(OK);
1286 next_state_ = STATE_DATA_READ_COMPLETE;
1287 read_data_buf_->data()[0] = 0;
1288 return data_socket_->Read(
1289 read_data_buf_.get(), read_data_buf_len_, io_callback_);
1292 int FtpNetworkTransaction::DoDataReadComplete(int result) {
1293 return result;
1296 // We're using a histogram as a group of counters, with one bucket for each
1297 // enumeration value. We're only interested in the values of the counters.
1298 // Ignore the shape, average, and standard deviation of the histograms because
1299 // they are meaningless.
1301 // We use two histograms. In the first histogram we tally whether the user has
1302 // seen an error of that type during the session. In the second histogram we
1303 // tally the total number of times the users sees each errer.
1304 void FtpNetworkTransaction::RecordDataConnectionError(int result) {
1305 // Gather data for http://crbug.com/3073. See how many users have trouble
1306 // establishing FTP data connection in passive FTP mode.
1307 enum {
1308 // Data connection successful.
1309 NET_ERROR_OK = 0,
1311 // Local firewall blocked the connection.
1312 NET_ERROR_ACCESS_DENIED = 1,
1314 // Connection timed out.
1315 NET_ERROR_TIMED_OUT = 2,
1317 // Connection has been estabilished, but then got broken (either reset
1318 // or aborted).
1319 NET_ERROR_CONNECTION_BROKEN = 3,
1321 // Connection has been refused.
1322 NET_ERROR_CONNECTION_REFUSED = 4,
1324 // No connection to the internet.
1325 NET_ERROR_INTERNET_DISCONNECTED = 5,
1327 // Could not reach the destination address.
1328 NET_ERROR_ADDRESS_UNREACHABLE = 6,
1330 // A programming error in our network stack.
1331 NET_ERROR_UNEXPECTED = 7,
1333 // Other kind of error.
1334 NET_ERROR_OTHER = 20,
1336 NUM_OF_NET_ERROR_TYPES
1337 } type;
1338 switch (result) {
1339 case OK:
1340 type = NET_ERROR_OK;
1341 break;
1342 case ERR_ACCESS_DENIED:
1343 case ERR_NETWORK_ACCESS_DENIED:
1344 type = NET_ERROR_ACCESS_DENIED;
1345 break;
1346 case ERR_TIMED_OUT:
1347 type = NET_ERROR_TIMED_OUT;
1348 break;
1349 case ERR_CONNECTION_ABORTED:
1350 case ERR_CONNECTION_RESET:
1351 case ERR_CONNECTION_CLOSED:
1352 type = NET_ERROR_CONNECTION_BROKEN;
1353 break;
1354 case ERR_CONNECTION_FAILED:
1355 case ERR_CONNECTION_REFUSED:
1356 type = NET_ERROR_CONNECTION_REFUSED;
1357 break;
1358 case ERR_INTERNET_DISCONNECTED:
1359 type = NET_ERROR_INTERNET_DISCONNECTED;
1360 break;
1361 case ERR_ADDRESS_INVALID:
1362 case ERR_ADDRESS_UNREACHABLE:
1363 type = NET_ERROR_ADDRESS_UNREACHABLE;
1364 break;
1365 case ERR_UNEXPECTED:
1366 type = NET_ERROR_UNEXPECTED;
1367 break;
1368 default:
1369 type = NET_ERROR_OTHER;
1370 break;
1372 static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
1374 DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
1375 if (!had_error_type[type]) {
1376 had_error_type[type] = true;
1377 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1378 type, NUM_OF_NET_ERROR_TYPES);
1380 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1381 type, NUM_OF_NET_ERROR_TYPES);
1384 } // namespace net