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