Revert of Revert of Use BoringSSL in the implementation of ClearKey for Chromecast...
[chromium-blink-merge.git] / net / ftp / ftp_network_transaction.cc
blob3c67114dceda6769d12b156dfb915cca7d4476ad
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/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/ftp/ftp_network_session.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"
28 namespace net {
30 namespace {
32 const char kCRLF[] = "\r\n";
34 const int kCtrlBufLen = 1024;
36 // Returns true if |input| can be safely used as a part of FTP command.
37 bool IsValidFTPCommandString(const std::string& input) {
38 // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
39 // characters in the command if the request path contains them. To be
40 // compatible, we do the same and allow non-ASCII characters in a command.
42 // Protect agains newline injection attack.
43 if (input.find_first_of("\r\n") != std::string::npos)
44 return false;
46 return true;
49 enum ErrorClass {
50 // The requested action was initiated. The client should expect another
51 // reply before issuing the next command.
52 ERROR_CLASS_INITIATED,
54 // The requested action has been successfully completed.
55 ERROR_CLASS_OK,
57 // The command has been accepted, but to complete the operation, more
58 // information must be sent by the client.
59 ERROR_CLASS_INFO_NEEDED,
61 // The command was not accepted and the requested action did not take place.
62 // This condition is temporary, and the client is encouraged to restart the
63 // command sequence.
64 ERROR_CLASS_TRANSIENT_ERROR,
66 // The command was not accepted and the requested action did not take place.
67 // This condition is rather permanent, and the client is discouraged from
68 // repeating the exact request.
69 ERROR_CLASS_PERMANENT_ERROR,
72 // Returns the error class for given response code. Caller should ensure
73 // that |response_code| is in range 100-599.
74 ErrorClass GetErrorClass(int response_code) {
75 if (response_code >= 100 && response_code <= 199)
76 return ERROR_CLASS_INITIATED;
78 if (response_code >= 200 && response_code <= 299)
79 return ERROR_CLASS_OK;
81 if (response_code >= 300 && response_code <= 399)
82 return ERROR_CLASS_INFO_NEEDED;
84 if (response_code >= 400 && response_code <= 499)
85 return ERROR_CLASS_TRANSIENT_ERROR;
87 if (response_code >= 500 && response_code <= 599)
88 return ERROR_CLASS_PERMANENT_ERROR;
90 // We should not be called on invalid error codes.
91 NOTREACHED() << response_code;
92 return ERROR_CLASS_PERMANENT_ERROR;
95 // Returns network error code for received FTP |response_code|.
96 int GetNetErrorCodeForFtpResponseCode(int response_code) {
97 switch (response_code) {
98 case 421:
99 return ERR_FTP_SERVICE_UNAVAILABLE;
100 case 426:
101 return ERR_FTP_TRANSFER_ABORTED;
102 case 450:
103 return ERR_FTP_FILE_BUSY;
104 case 500:
105 case 501:
106 return ERR_FTP_SYNTAX_ERROR;
107 case 502:
108 case 504:
109 return ERR_FTP_COMMAND_NOT_SUPPORTED;
110 case 503:
111 return ERR_FTP_BAD_COMMAND_SEQUENCE;
112 default:
113 return ERR_FTP_FAILED;
117 // From RFC 2428 Section 3:
118 // The text returned in response to the EPSV command MUST be:
119 // <some text> (<d><d><d><tcp-port><d>)
120 // <d> is a delimiter character, ideally to be |
121 bool ExtractPortFromEPSVResponse(const FtpCtrlResponse& response, 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 FtpCtrlResponse& response, int* port) {
151 if (response.lines.size() != 1)
152 return false;
154 std::string line(response.lines[0]);
155 if (!base::IsStringASCII(line))
156 return false;
157 if (line.length() < 2)
158 return false;
160 size_t paren_pos = line.find('(');
161 if (paren_pos == std::string::npos) {
162 // Find the first comma and use it to locate the beginning
163 // of the response data.
164 size_t comma_pos = line.find(',');
165 if (comma_pos == std::string::npos)
166 return false;
168 size_t space_pos = line.rfind(' ', comma_pos);
169 if (space_pos != std::string::npos)
170 line = line.substr(space_pos + 1);
171 } else {
172 // Remove the parentheses and use the text inside them.
173 size_t closing_paren_pos = line.rfind(')');
174 if (closing_paren_pos == std::string::npos)
175 return false;
176 if (closing_paren_pos <= paren_pos)
177 return false;
179 line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1);
182 // Split the line into comma-separated pieces and extract
183 // the last two.
184 std::vector<std::string> pieces;
185 base::SplitString(line, ',', &pieces);
186 if (pieces.size() != 6)
187 return false;
189 // Ignore the IP address supplied in the response. We are always going
190 // to connect back to the same server to prevent FTP PASV port scanning.
191 int p0, p1;
192 if (!base::StringToInt(pieces[4], &p0))
193 return false;
194 if (!base::StringToInt(pieces[5], &p1))
195 return false;
196 *port = (p0 << 8) + p1;
198 return true;
201 } // namespace
203 FtpNetworkTransaction::FtpNetworkTransaction(
204 FtpNetworkSession* session,
205 ClientSocketFactory* socket_factory)
206 : command_sent_(COMMAND_NONE),
207 io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
208 base::Unretained(this))),
209 session_(session),
210 request_(NULL),
211 resolver_(session->host_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): Check for errors in the welcome message.
374 next_state_ = STATE_CTRL_WRITE_USER;
375 break;
376 case COMMAND_USER:
377 rv = ProcessResponseUSER(response);
378 break;
379 case COMMAND_PASS:
380 rv = ProcessResponsePASS(response);
381 break;
382 case COMMAND_SYST:
383 rv = ProcessResponseSYST(response);
384 break;
385 case COMMAND_PWD:
386 rv = ProcessResponsePWD(response);
387 break;
388 case COMMAND_TYPE:
389 rv = ProcessResponseTYPE(response);
390 break;
391 case COMMAND_EPSV:
392 rv = ProcessResponseEPSV(response);
393 break;
394 case COMMAND_PASV:
395 rv = ProcessResponsePASV(response);
396 break;
397 case COMMAND_SIZE:
398 rv = ProcessResponseSIZE(response);
399 break;
400 case COMMAND_RETR:
401 rv = ProcessResponseRETR(response);
402 break;
403 case COMMAND_CWD:
404 rv = ProcessResponseCWD(response);
405 break;
406 case COMMAND_LIST:
407 rv = ProcessResponseLIST(response);
408 break;
409 case COMMAND_QUIT:
410 rv = ProcessResponseQUIT(response);
411 break;
412 default:
413 LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
414 return ERR_UNEXPECTED;
417 // We may get multiple responses for some commands,
418 // see http://crbug.com/18036.
419 while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
420 response = ctrl_response_buffer_->PopResponse();
422 switch (command_sent_) {
423 case COMMAND_RETR:
424 rv = ProcessResponseRETR(response);
425 break;
426 case COMMAND_LIST:
427 rv = ProcessResponseLIST(response);
428 break;
429 default:
430 // Multiple responses for other commands are invalid.
431 return Stop(ERR_INVALID_RESPONSE);
435 return rv;
438 // Used to prepare and send FTP command.
439 int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
440 const std::string& command_for_log,
441 Command cmd) {
442 // If we send a new command when we still have unprocessed responses
443 // for previous commands, the response receiving code will have no way to know
444 // which responses are for which command.
445 DCHECK(!ctrl_response_buffer_->ResponseAvailable());
447 DCHECK(!write_command_buf_.get());
448 DCHECK(!write_buf_.get());
450 if (!IsValidFTPCommandString(command)) {
451 // Callers should validate the command themselves and return a more specific
452 // error code.
453 NOTREACHED();
454 return Stop(ERR_UNEXPECTED);
457 command_sent_ = cmd;
459 write_command_buf_ = new IOBufferWithSize(command.length() + 2);
460 write_buf_ = new DrainableIOBuffer(write_command_buf_.get(),
461 write_command_buf_->size());
462 memcpy(write_command_buf_->data(), command.data(), command.length());
463 memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
465 net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
466 NetLog::StringCallback("command", &command_for_log));
468 next_state_ = STATE_CTRL_WRITE;
469 return OK;
472 std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
473 bool is_directory) const {
474 std::string path(current_remote_directory_);
475 if (request_->url.has_path()) {
476 std::string gurl_path(request_->url.path());
478 // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
479 std::string::size_type pos = gurl_path.rfind(';');
480 if (pos != std::string::npos)
481 gurl_path.resize(pos);
483 path.append(gurl_path);
485 // Make sure that if the path is expected to be a file, it won't end
486 // with a trailing slash.
487 if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
488 path.erase(path.length() - 1);
489 UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
490 UnescapeRule::URL_SPECIAL_CHARS;
491 // This may unescape to non-ASCII characters, but we allow that. See the
492 // comment for IsValidFTPCommandString.
493 path = UnescapeURLComponent(path, unescape_rules);
495 if (system_type_ == SYSTEM_TYPE_VMS) {
496 if (is_directory)
497 path = FtpUtil::UnixDirectoryPathToVMS(path);
498 else
499 path = FtpUtil::UnixFilePathToVMS(path);
502 DCHECK(IsValidFTPCommandString(path));
503 return path;
506 void FtpNetworkTransaction::DetectTypecode() {
507 if (!request_->url.has_path())
508 return;
509 std::string gurl_path(request_->url.path());
511 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
512 std::string::size_type pos = gurl_path.rfind(';');
513 if (pos == std::string::npos)
514 return;
515 std::string typecode_string(gurl_path.substr(pos));
516 if (typecode_string == ";type=a") {
517 data_type_ = DATA_TYPE_ASCII;
518 resource_type_ = RESOURCE_TYPE_FILE;
519 } else if (typecode_string == ";type=i") {
520 data_type_ = DATA_TYPE_IMAGE;
521 resource_type_ = RESOURCE_TYPE_FILE;
522 } else if (typecode_string == ";type=d") {
523 resource_type_ = RESOURCE_TYPE_DIRECTORY;
527 int FtpNetworkTransaction::DoLoop(int result) {
528 DCHECK(next_state_ != STATE_NONE);
530 int rv = result;
531 do {
532 State state = next_state_;
533 next_state_ = STATE_NONE;
534 switch (state) {
535 case STATE_CTRL_RESOLVE_HOST:
536 DCHECK(rv == OK);
537 rv = DoCtrlResolveHost();
538 break;
539 case STATE_CTRL_RESOLVE_HOST_COMPLETE:
540 rv = DoCtrlResolveHostComplete(rv);
541 break;
542 case STATE_CTRL_CONNECT:
543 DCHECK(rv == OK);
544 rv = DoCtrlConnect();
545 break;
546 case STATE_CTRL_CONNECT_COMPLETE:
547 rv = DoCtrlConnectComplete(rv);
548 break;
549 case STATE_CTRL_READ:
550 DCHECK(rv == OK);
551 rv = DoCtrlRead();
552 break;
553 case STATE_CTRL_READ_COMPLETE:
554 rv = DoCtrlReadComplete(rv);
555 break;
556 case STATE_CTRL_WRITE:
557 DCHECK(rv == OK);
558 rv = DoCtrlWrite();
559 break;
560 case STATE_CTRL_WRITE_COMPLETE:
561 rv = DoCtrlWriteComplete(rv);
562 break;
563 case STATE_CTRL_WRITE_USER:
564 DCHECK(rv == OK);
565 rv = DoCtrlWriteUSER();
566 break;
567 case STATE_CTRL_WRITE_PASS:
568 DCHECK(rv == OK);
569 rv = DoCtrlWritePASS();
570 break;
571 case STATE_CTRL_WRITE_SYST:
572 DCHECK(rv == OK);
573 rv = DoCtrlWriteSYST();
574 break;
575 case STATE_CTRL_WRITE_PWD:
576 DCHECK(rv == OK);
577 rv = DoCtrlWritePWD();
578 break;
579 case STATE_CTRL_WRITE_TYPE:
580 DCHECK(rv == OK);
581 rv = DoCtrlWriteTYPE();
582 break;
583 case STATE_CTRL_WRITE_EPSV:
584 DCHECK(rv == OK);
585 rv = DoCtrlWriteEPSV();
586 break;
587 case STATE_CTRL_WRITE_PASV:
588 DCHECK(rv == OK);
589 rv = DoCtrlWritePASV();
590 break;
591 case STATE_CTRL_WRITE_RETR:
592 DCHECK(rv == OK);
593 rv = DoCtrlWriteRETR();
594 break;
595 case STATE_CTRL_WRITE_SIZE:
596 DCHECK(rv == OK);
597 rv = DoCtrlWriteSIZE();
598 break;
599 case STATE_CTRL_WRITE_CWD:
600 DCHECK(rv == OK);
601 rv = DoCtrlWriteCWD();
602 break;
603 case STATE_CTRL_WRITE_LIST:
604 DCHECK(rv == OK);
605 rv = DoCtrlWriteLIST();
606 break;
607 case STATE_CTRL_WRITE_QUIT:
608 DCHECK(rv == OK);
609 rv = DoCtrlWriteQUIT();
610 break;
611 case STATE_DATA_CONNECT:
612 DCHECK(rv == OK);
613 rv = DoDataConnect();
614 break;
615 case STATE_DATA_CONNECT_COMPLETE:
616 rv = DoDataConnectComplete(rv);
617 break;
618 case STATE_DATA_READ:
619 DCHECK(rv == OK);
620 rv = DoDataRead();
621 break;
622 case STATE_DATA_READ_COMPLETE:
623 rv = DoDataReadComplete(rv);
624 break;
625 default:
626 NOTREACHED() << "bad state";
627 rv = ERR_UNEXPECTED;
628 break;
630 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
631 return rv;
634 int FtpNetworkTransaction::DoCtrlResolveHost() {
635 next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
637 HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
638 // No known referrer.
639 return resolver_.Resolve(
640 info,
641 DEFAULT_PRIORITY,
642 &addresses_,
643 base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
644 net_log_);
647 int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
648 if (result == OK)
649 next_state_ = STATE_CTRL_CONNECT;
650 return result;
653 int FtpNetworkTransaction::DoCtrlConnect() {
654 next_state_ = STATE_CTRL_CONNECT_COMPLETE;
655 ctrl_socket_ = socket_factory_->CreateTransportClientSocket(
656 addresses_, net_log_.net_log(), net_log_.source());
657 net_log_.AddEvent(
658 NetLog::TYPE_FTP_CONTROL_CONNECTION,
659 ctrl_socket_->NetLog().source().ToEventParametersCallback());
660 return ctrl_socket_->Connect(io_callback_);
663 int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
664 if (result == OK) {
665 // Put the peer's IP address and port into the response.
666 IPEndPoint ip_endpoint;
667 result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
668 if (result == OK) {
669 response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
670 next_state_ = STATE_CTRL_READ;
672 if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
673 // Do not use EPSV for IPv4 connections. Some servers become confused
674 // and we time out while waiting to connect. PASV is perfectly fine for
675 // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
676 // whitelisting IPv6 to use it, to make the code more future-proof:
677 // all future protocols should just use EPSV.
678 use_epsv_ = false;
682 return result;
685 int FtpNetworkTransaction::DoCtrlRead() {
686 next_state_ = STATE_CTRL_READ_COMPLETE;
687 return ctrl_socket_->Read(read_ctrl_buf_.get(), kCtrlBufLen, io_callback_);
690 int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
691 if (result == 0) {
692 // Some servers (for example Pure-FTPd) apparently close the control
693 // connection when anonymous login is not permitted. For more details
694 // see http://crbug.com/25023.
695 if (command_sent_ == COMMAND_USER &&
696 credentials_.username() == base::ASCIIToUTF16("anonymous")) {
697 response_.needs_auth = true;
699 return Stop(ERR_EMPTY_RESPONSE);
701 if (result < 0)
702 return Stop(result);
704 ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
706 if (!ctrl_response_buffer_->ResponseAvailable()) {
707 // Read more data from the control socket.
708 next_state_ = STATE_CTRL_READ;
709 return OK;
712 return ProcessCtrlResponse();
715 int FtpNetworkTransaction::DoCtrlWrite() {
716 next_state_ = STATE_CTRL_WRITE_COMPLETE;
718 return ctrl_socket_->Write(
719 write_buf_.get(), write_buf_->BytesRemaining(), io_callback_);
722 int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
723 if (result < 0)
724 return result;
726 write_buf_->DidConsume(result);
727 if (write_buf_->BytesRemaining() == 0) {
728 // Clear the write buffer.
729 write_buf_ = NULL;
730 write_command_buf_ = NULL;
732 next_state_ = STATE_CTRL_READ;
733 } else {
734 next_state_ = STATE_CTRL_WRITE;
736 return OK;
739 // FTP Commands and responses
741 // USER Command.
742 int FtpNetworkTransaction::DoCtrlWriteUSER() {
743 std::string command = "USER " + base::UTF16ToUTF8(credentials_.username());
745 if (!IsValidFTPCommandString(command))
746 return Stop(ERR_MALFORMED_IDENTITY);
748 next_state_ = STATE_CTRL_READ;
749 return SendFtpCommand(command, "USER ***", COMMAND_USER);
752 int FtpNetworkTransaction::ProcessResponseUSER(
753 const FtpCtrlResponse& response) {
754 switch (GetErrorClass(response.status_code)) {
755 case ERROR_CLASS_OK:
756 next_state_ = STATE_CTRL_WRITE_SYST;
757 break;
758 case ERROR_CLASS_INFO_NEEDED:
759 next_state_ = STATE_CTRL_WRITE_PASS;
760 break;
761 case ERROR_CLASS_TRANSIENT_ERROR:
762 case ERROR_CLASS_PERMANENT_ERROR:
763 response_.needs_auth = true;
764 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
765 default:
766 NOTREACHED();
767 return Stop(ERR_UNEXPECTED);
769 return OK;
772 // PASS command.
773 int FtpNetworkTransaction::DoCtrlWritePASS() {
774 std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password());
776 if (!IsValidFTPCommandString(command))
777 return Stop(ERR_MALFORMED_IDENTITY);
779 next_state_ = STATE_CTRL_READ;
780 return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
783 int FtpNetworkTransaction::ProcessResponsePASS(
784 const FtpCtrlResponse& response) {
785 switch (GetErrorClass(response.status_code)) {
786 case ERROR_CLASS_OK:
787 next_state_ = STATE_CTRL_WRITE_SYST;
788 break;
789 case ERROR_CLASS_INFO_NEEDED:
790 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
791 case ERROR_CLASS_TRANSIENT_ERROR:
792 case ERROR_CLASS_PERMANENT_ERROR:
793 response_.needs_auth = true;
794 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
795 default:
796 NOTREACHED();
797 return Stop(ERR_UNEXPECTED);
799 return OK;
802 // SYST command.
803 int FtpNetworkTransaction::DoCtrlWriteSYST() {
804 std::string command = "SYST";
805 next_state_ = STATE_CTRL_READ;
806 return SendFtpCommand(command, command, COMMAND_SYST);
809 int FtpNetworkTransaction::ProcessResponseSYST(
810 const FtpCtrlResponse& response) {
811 switch (GetErrorClass(response.status_code)) {
812 case ERROR_CLASS_INITIATED:
813 return Stop(ERR_INVALID_RESPONSE);
814 case ERROR_CLASS_OK: {
815 // All important info should be on the first line.
816 std::string line = response.lines[0];
817 // The response should be ASCII, which allows us to do case-insensitive
818 // comparisons easily. If it is not ASCII, we leave the system type
819 // as unknown.
820 if (base::IsStringASCII(line)) {
821 line = base::StringToLowerASCII(line);
823 // Remove all whitespace, to correctly handle cases like fancy "V M S"
824 // response instead of "VMS".
825 base::RemoveChars(line, base::kWhitespaceASCII, &line);
827 // The "magic" strings we test for below have been gathered by an
828 // empirical study. VMS needs to come first because some VMS systems
829 // also respond with "UNIX emulation", which is not perfect. It is much
830 // more reliable to talk to these servers in their native language.
831 if (line.find("vms") != std::string::npos) {
832 system_type_ = SYSTEM_TYPE_VMS;
833 } else if (line.find("l8") != std::string::npos ||
834 line.find("unix") != std::string::npos ||
835 line.find("bsd") != std::string::npos) {
836 system_type_ = SYSTEM_TYPE_UNIX;
837 } else if (line.find("win32") != std::string::npos ||
838 line.find("windows") != std::string::npos) {
839 system_type_ = SYSTEM_TYPE_WINDOWS;
840 } else if (line.find("os/2") != std::string::npos) {
841 system_type_ = SYSTEM_TYPE_OS2;
844 next_state_ = STATE_CTRL_WRITE_PWD;
845 break;
847 case ERROR_CLASS_INFO_NEEDED:
848 return Stop(ERR_INVALID_RESPONSE);
849 case ERROR_CLASS_TRANSIENT_ERROR:
850 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
851 case ERROR_CLASS_PERMANENT_ERROR:
852 // Server does not recognize the SYST command so proceed.
853 next_state_ = STATE_CTRL_WRITE_PWD;
854 break;
855 default:
856 NOTREACHED();
857 return Stop(ERR_UNEXPECTED);
859 return OK;
862 // PWD command.
863 int FtpNetworkTransaction::DoCtrlWritePWD() {
864 std::string command = "PWD";
865 next_state_ = STATE_CTRL_READ;
866 return SendFtpCommand(command, command, COMMAND_PWD);
869 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
870 switch (GetErrorClass(response.status_code)) {
871 case ERROR_CLASS_INITIATED:
872 return Stop(ERR_INVALID_RESPONSE);
873 case ERROR_CLASS_OK: {
874 // The info we look for should be on the first line.
875 std::string line = response.lines[0];
876 if (line.empty())
877 return Stop(ERR_INVALID_RESPONSE);
878 std::string::size_type quote_pos = line.find('"');
879 if (quote_pos != std::string::npos) {
880 line = line.substr(quote_pos + 1);
881 quote_pos = line.find('"');
882 if (quote_pos == std::string::npos)
883 return Stop(ERR_INVALID_RESPONSE);
884 line = line.substr(0, quote_pos);
886 if (system_type_ == SYSTEM_TYPE_VMS)
887 line = FtpUtil::VMSPathToUnix(line);
888 if (line.length() && line[line.length() - 1] == '/')
889 line.erase(line.length() - 1);
890 current_remote_directory_ = line;
891 next_state_ = STATE_CTRL_WRITE_TYPE;
892 break;
894 case ERROR_CLASS_INFO_NEEDED:
895 return Stop(ERR_INVALID_RESPONSE);
896 case ERROR_CLASS_TRANSIENT_ERROR:
897 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
898 case ERROR_CLASS_PERMANENT_ERROR:
899 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
900 default:
901 NOTREACHED();
902 return Stop(ERR_UNEXPECTED);
904 return OK;
907 // TYPE command.
908 int FtpNetworkTransaction::DoCtrlWriteTYPE() {
909 std::string command = "TYPE ";
910 if (data_type_ == DATA_TYPE_ASCII) {
911 command += "A";
912 } else if (data_type_ == DATA_TYPE_IMAGE) {
913 command += "I";
914 } else {
915 NOTREACHED();
916 return Stop(ERR_UNEXPECTED);
918 next_state_ = STATE_CTRL_READ;
919 return SendFtpCommand(command, command, COMMAND_TYPE);
922 int FtpNetworkTransaction::ProcessResponseTYPE(
923 const FtpCtrlResponse& response) {
924 switch (GetErrorClass(response.status_code)) {
925 case ERROR_CLASS_INITIATED:
926 return Stop(ERR_INVALID_RESPONSE);
927 case ERROR_CLASS_OK:
928 next_state_ = STATE_CTRL_WRITE_SIZE;
929 break;
930 case ERROR_CLASS_INFO_NEEDED:
931 return Stop(ERR_INVALID_RESPONSE);
932 case ERROR_CLASS_TRANSIENT_ERROR:
933 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
934 case ERROR_CLASS_PERMANENT_ERROR:
935 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
936 default:
937 NOTREACHED();
938 return Stop(ERR_UNEXPECTED);
940 return OK;
943 // EPSV command
944 int FtpNetworkTransaction::DoCtrlWriteEPSV() {
945 const std::string command = "EPSV";
946 next_state_ = STATE_CTRL_READ;
947 return SendFtpCommand(command, command, COMMAND_EPSV);
950 int FtpNetworkTransaction::ProcessResponseEPSV(
951 const FtpCtrlResponse& response) {
952 switch (GetErrorClass(response.status_code)) {
953 case ERROR_CLASS_INITIATED:
954 return Stop(ERR_INVALID_RESPONSE);
955 case ERROR_CLASS_OK: {
956 int port;
957 if (!ExtractPortFromEPSVResponse(response, &port))
958 return Stop(ERR_INVALID_RESPONSE);
959 if (port < 1024 || !IsPortAllowedByFtp(port))
960 return Stop(ERR_UNSAFE_PORT);
961 data_connection_port_ = static_cast<uint16>(port);
962 next_state_ = STATE_DATA_CONNECT;
963 break;
965 case ERROR_CLASS_INFO_NEEDED:
966 return Stop(ERR_INVALID_RESPONSE);
967 case ERROR_CLASS_TRANSIENT_ERROR:
968 case ERROR_CLASS_PERMANENT_ERROR:
969 use_epsv_ = false;
970 next_state_ = STATE_CTRL_WRITE_PASV;
971 return OK;
972 default:
973 NOTREACHED();
974 return Stop(ERR_UNEXPECTED);
976 return OK;
979 // PASV command
980 int FtpNetworkTransaction::DoCtrlWritePASV() {
981 std::string command = "PASV";
982 next_state_ = STATE_CTRL_READ;
983 return SendFtpCommand(command, command, COMMAND_PASV);
986 int FtpNetworkTransaction::ProcessResponsePASV(
987 const FtpCtrlResponse& response) {
988 switch (GetErrorClass(response.status_code)) {
989 case ERROR_CLASS_INITIATED:
990 return Stop(ERR_INVALID_RESPONSE);
991 case ERROR_CLASS_OK: {
992 int port;
993 if (!ExtractPortFromPASVResponse(response, &port))
994 return Stop(ERR_INVALID_RESPONSE);
995 if (port < 1024 || !IsPortAllowedByFtp(port))
996 return Stop(ERR_UNSAFE_PORT);
997 data_connection_port_ = static_cast<uint16>(port);
998 next_state_ = STATE_DATA_CONNECT;
999 break;
1001 case ERROR_CLASS_INFO_NEEDED:
1002 return Stop(ERR_INVALID_RESPONSE);
1003 case ERROR_CLASS_TRANSIENT_ERROR:
1004 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1005 case ERROR_CLASS_PERMANENT_ERROR:
1006 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1007 default:
1008 NOTREACHED();
1009 return Stop(ERR_UNEXPECTED);
1011 return OK;
1014 // RETR command
1015 int FtpNetworkTransaction::DoCtrlWriteRETR() {
1016 std::string command = "RETR " + GetRequestPathForFtpCommand(false);
1017 next_state_ = STATE_CTRL_READ;
1018 return SendFtpCommand(command, command, COMMAND_RETR);
1021 int FtpNetworkTransaction::ProcessResponseRETR(
1022 const FtpCtrlResponse& response) {
1023 // Resource type should be either filled in by DetectTypecode() or
1024 // detected with CWD. RETR is sent only when the resource is a file.
1025 DCHECK_EQ(RESOURCE_TYPE_FILE, resource_type_);
1027 switch (GetErrorClass(response.status_code)) {
1028 case ERROR_CLASS_INITIATED:
1029 // We want the client to start reading the response at this point.
1030 // It got here either through Start or RestartWithAuth. We want that
1031 // method to complete. Not setting next state here will make DoLoop exit
1032 // and in turn make Start/RestartWithAuth complete.
1033 break;
1034 case ERROR_CLASS_OK:
1035 next_state_ = STATE_CTRL_WRITE_QUIT;
1036 break;
1037 case ERROR_CLASS_INFO_NEEDED:
1038 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1039 case ERROR_CLASS_TRANSIENT_ERROR:
1040 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1041 case ERROR_CLASS_PERMANENT_ERROR:
1042 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1043 default:
1044 NOTREACHED();
1045 return Stop(ERR_UNEXPECTED);
1048 return OK;
1051 // SIZE command
1052 int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1053 std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
1054 next_state_ = STATE_CTRL_READ;
1055 return SendFtpCommand(command, command, COMMAND_SIZE);
1058 int FtpNetworkTransaction::ProcessResponseSIZE(
1059 const FtpCtrlResponse& response) {
1060 switch (GetErrorClass(response.status_code)) {
1061 case ERROR_CLASS_INITIATED:
1062 break;
1063 case ERROR_CLASS_OK:
1064 if (response.lines.size() != 1)
1065 return Stop(ERR_INVALID_RESPONSE);
1066 int64 size;
1067 if (!base::StringToInt64(response.lines[0], &size))
1068 return Stop(ERR_INVALID_RESPONSE);
1069 if (size < 0)
1070 return Stop(ERR_INVALID_RESPONSE);
1072 // A successful response to SIZE does not mean the resource is a file.
1073 // Some FTP servers (for example, the qnx one) send a SIZE even for
1074 // directories.
1075 response_.expected_content_size = size;
1076 break;
1077 case ERROR_CLASS_INFO_NEEDED:
1078 break;
1079 case ERROR_CLASS_TRANSIENT_ERROR:
1080 break;
1081 case ERROR_CLASS_PERMANENT_ERROR:
1082 // It's possible that SIZE failed because the path is a directory.
1083 // TODO(xunjieli): Add a test for this case.
1084 if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
1085 response.status_code != 550) {
1086 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1088 break;
1089 default:
1090 NOTREACHED();
1091 return Stop(ERR_UNEXPECTED);
1094 // If the resource is known beforehand to be a file, RETR should be issued,
1095 // otherwise do CWD which will detect the resource type.
1096 if (resource_type_ == RESOURCE_TYPE_FILE)
1097 EstablishDataConnection(STATE_CTRL_WRITE_RETR);
1098 else
1099 next_state_ = STATE_CTRL_WRITE_CWD;
1100 return OK;
1103 // CWD command
1104 int FtpNetworkTransaction::DoCtrlWriteCWD() {
1105 std::string command = "CWD " + GetRequestPathForFtpCommand(true);
1106 next_state_ = STATE_CTRL_READ;
1107 return SendFtpCommand(command, command, COMMAND_CWD);
1110 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
1111 // CWD should be invoked only when the resource is not a file.
1112 DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
1114 switch (GetErrorClass(response.status_code)) {
1115 case ERROR_CLASS_INITIATED:
1116 return Stop(ERR_INVALID_RESPONSE);
1117 case ERROR_CLASS_OK:
1118 resource_type_ = RESOURCE_TYPE_DIRECTORY;
1119 EstablishDataConnection(STATE_CTRL_WRITE_LIST);
1120 break;
1121 case ERROR_CLASS_INFO_NEEDED:
1122 return Stop(ERR_INVALID_RESPONSE);
1123 case ERROR_CLASS_TRANSIENT_ERROR:
1124 // Some FTP servers send response 451 (not a valid CWD response according
1125 // to RFC 959) instead of 550.
1126 if (response.status_code == 451)
1127 return ProcessResponseCWDNotADirectory();
1129 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1130 case ERROR_CLASS_PERMANENT_ERROR:
1131 if (response.status_code == 550)
1132 return ProcessResponseCWDNotADirectory();
1134 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1135 default:
1136 NOTREACHED();
1137 return Stop(ERR_UNEXPECTED);
1140 return OK;
1143 int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1144 if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
1145 // We're assuming that the resource is a directory, but the server
1146 // says it's not true. The most probable interpretation is that it
1147 // doesn't exist (with FTP we can't be sure).
1148 return Stop(ERR_FILE_NOT_FOUND);
1151 // If it is not a directory, it is probably a file.
1152 resource_type_ = RESOURCE_TYPE_FILE;
1154 EstablishDataConnection(STATE_CTRL_WRITE_RETR);
1155 return OK;
1158 // LIST command
1159 int FtpNetworkTransaction::DoCtrlWriteLIST() {
1160 // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1161 // forces LIST output instead of NLST (which would be ambiguous for us
1162 // to parse).
1163 std::string command("LIST -l");
1164 if (system_type_ == SYSTEM_TYPE_VMS)
1165 command = "LIST *.*;0";
1167 next_state_ = STATE_CTRL_READ;
1168 return SendFtpCommand(command, command, COMMAND_LIST);
1171 int FtpNetworkTransaction::ProcessResponseLIST(
1172 const FtpCtrlResponse& response) {
1173 // Resource type should be either filled in by DetectTypecode() or
1174 // detected with CWD. LIST is sent only when the resource is a directory.
1175 DCHECK_EQ(RESOURCE_TYPE_DIRECTORY, resource_type_);
1177 switch (GetErrorClass(response.status_code)) {
1178 case ERROR_CLASS_INITIATED:
1179 // We want the client to start reading the response at this point.
1180 // It got here either through Start or RestartWithAuth. We want that
1181 // method to complete. Not setting next state here will make DoLoop exit
1182 // and in turn make Start/RestartWithAuth complete.
1183 response_.is_directory_listing = true;
1184 break;
1185 case ERROR_CLASS_OK:
1186 response_.is_directory_listing = true;
1187 next_state_ = STATE_CTRL_WRITE_QUIT;
1188 break;
1189 case ERROR_CLASS_INFO_NEEDED:
1190 return Stop(ERR_INVALID_RESPONSE);
1191 case ERROR_CLASS_TRANSIENT_ERROR:
1192 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1193 case ERROR_CLASS_PERMANENT_ERROR:
1194 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1195 default:
1196 NOTREACHED();
1197 return Stop(ERR_UNEXPECTED);
1199 return OK;
1202 // QUIT command
1203 int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1204 std::string command = "QUIT";
1205 next_state_ = STATE_CTRL_READ;
1206 return SendFtpCommand(command, command, COMMAND_QUIT);
1209 int FtpNetworkTransaction::ProcessResponseQUIT(
1210 const FtpCtrlResponse& response) {
1211 ctrl_socket_->Disconnect();
1212 return last_error_;
1215 // Data Connection
1217 int FtpNetworkTransaction::DoDataConnect() {
1218 next_state_ = STATE_DATA_CONNECT_COMPLETE;
1219 IPEndPoint ip_endpoint;
1220 AddressList data_address;
1221 // Connect to the same host as the control socket to prevent PASV port
1222 // scanning attacks.
1223 int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
1224 if (rv != OK)
1225 return Stop(rv);
1226 data_address = AddressList::CreateFromIPAddress(
1227 ip_endpoint.address(), data_connection_port_);
1228 data_socket_ = socket_factory_->CreateTransportClientSocket(
1229 data_address, net_log_.net_log(), net_log_.source());
1230 net_log_.AddEvent(
1231 NetLog::TYPE_FTP_DATA_CONNECTION,
1232 data_socket_->NetLog().source().ToEventParametersCallback());
1233 return data_socket_->Connect(io_callback_);
1236 int FtpNetworkTransaction::DoDataConnectComplete(int result) {
1237 if (result != OK && use_epsv_) {
1238 // It's possible we hit a broken server, sadly. They can break in different
1239 // ways. Some time out, some reset a connection. Fall back to PASV.
1240 // TODO(phajdan.jr): remember it for future transactions with this server.
1241 // TODO(phajdan.jr): write a test for this code path.
1242 use_epsv_ = false;
1243 next_state_ = STATE_CTRL_WRITE_PASV;
1244 return OK;
1247 // Only record the connection error after we've applied all our fallbacks.
1248 // We want to capture the final error, one we're not going to recover from.
1249 RecordDataConnectionError(result);
1251 if (result != OK)
1252 return Stop(result);
1254 next_state_ = state_after_data_connect_complete_;
1255 return OK;
1258 int FtpNetworkTransaction::DoDataRead() {
1259 DCHECK(read_data_buf_.get());
1260 DCHECK_GT(read_data_buf_len_, 0);
1262 if (data_socket_ == NULL || !data_socket_->IsConnected()) {
1263 // If we don't destroy the data socket completely, some servers will wait
1264 // for us (http://crbug.com/21127). The half-closed TCP connection needs
1265 // to be closed on our side too.
1266 data_socket_.reset();
1268 if (ctrl_socket_->IsConnected()) {
1269 // Wait for the server's response, we should get it before sending QUIT.
1270 next_state_ = STATE_CTRL_READ;
1271 return OK;
1274 // We are no longer connected to the server, so just finish the transaction.
1275 return Stop(OK);
1278 next_state_ = STATE_DATA_READ_COMPLETE;
1279 read_data_buf_->data()[0] = 0;
1280 return data_socket_->Read(
1281 read_data_buf_.get(), read_data_buf_len_, io_callback_);
1284 int FtpNetworkTransaction::DoDataReadComplete(int result) {
1285 return result;
1288 // We're using a histogram as a group of counters, with one bucket for each
1289 // enumeration value. We're only interested in the values of the counters.
1290 // Ignore the shape, average, and standard deviation of the histograms because
1291 // they are meaningless.
1293 // We use two histograms. In the first histogram we tally whether the user has
1294 // seen an error of that type during the session. In the second histogram we
1295 // tally the total number of times the users sees each errer.
1296 void FtpNetworkTransaction::RecordDataConnectionError(int result) {
1297 // Gather data for http://crbug.com/3073. See how many users have trouble
1298 // establishing FTP data connection in passive FTP mode.
1299 enum {
1300 // Data connection successful.
1301 NET_ERROR_OK = 0,
1303 // Local firewall blocked the connection.
1304 NET_ERROR_ACCESS_DENIED = 1,
1306 // Connection timed out.
1307 NET_ERROR_TIMED_OUT = 2,
1309 // Connection has been estabilished, but then got broken (either reset
1310 // or aborted).
1311 NET_ERROR_CONNECTION_BROKEN = 3,
1313 // Connection has been refused.
1314 NET_ERROR_CONNECTION_REFUSED = 4,
1316 // No connection to the internet.
1317 NET_ERROR_INTERNET_DISCONNECTED = 5,
1319 // Could not reach the destination address.
1320 NET_ERROR_ADDRESS_UNREACHABLE = 6,
1322 // A programming error in our network stack.
1323 NET_ERROR_UNEXPECTED = 7,
1325 // Other kind of error.
1326 NET_ERROR_OTHER = 20,
1328 NUM_OF_NET_ERROR_TYPES
1329 } type;
1330 switch (result) {
1331 case OK:
1332 type = NET_ERROR_OK;
1333 break;
1334 case ERR_ACCESS_DENIED:
1335 case ERR_NETWORK_ACCESS_DENIED:
1336 type = NET_ERROR_ACCESS_DENIED;
1337 break;
1338 case ERR_TIMED_OUT:
1339 type = NET_ERROR_TIMED_OUT;
1340 break;
1341 case ERR_CONNECTION_ABORTED:
1342 case ERR_CONNECTION_RESET:
1343 case ERR_CONNECTION_CLOSED:
1344 type = NET_ERROR_CONNECTION_BROKEN;
1345 break;
1346 case ERR_CONNECTION_FAILED:
1347 case ERR_CONNECTION_REFUSED:
1348 type = NET_ERROR_CONNECTION_REFUSED;
1349 break;
1350 case ERR_INTERNET_DISCONNECTED:
1351 type = NET_ERROR_INTERNET_DISCONNECTED;
1352 break;
1353 case ERR_ADDRESS_INVALID:
1354 case ERR_ADDRESS_UNREACHABLE:
1355 type = NET_ERROR_ADDRESS_UNREACHABLE;
1356 break;
1357 case ERR_UNEXPECTED:
1358 type = NET_ERROR_UNEXPECTED;
1359 break;
1360 default:
1361 type = NET_ERROR_OTHER;
1362 break;
1364 static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
1366 DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
1367 if (!had_error_type[type]) {
1368 had_error_type[type] = true;
1369 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1370 type, NUM_OF_NET_ERROR_TYPES);
1372 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1373 type, NUM_OF_NET_ERROR_TYPES);
1376 } // namespace net