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