Roll libvpx 861f35:1fff3e
[chromium-blink-merge.git] / net / ftp / ftp_network_transaction.cc
blob5732c646c0604969a8a20ba2c02696aa974b92f3
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 const char kCRLF[] = "\r\n";
30 const int kCtrlBufLen = 1024;
32 namespace {
34 // Returns true if |input| can be safely used as a part of FTP command.
35 bool IsValidFTPCommandString(const std::string& input) {
36 // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
37 // characters in the command if the request path contains them. To be
38 // compatible, we do the same and allow non-ASCII characters in a command.
40 // Protect agains newline injection attack.
41 if (input.find_first_of("\r\n") != std::string::npos)
42 return false;
44 return true;
47 enum ErrorClass {
48 // The requested action was initiated. The client should expect another
49 // reply before issuing the next command.
50 ERROR_CLASS_INITIATED,
52 // The requested action has been successfully completed.
53 ERROR_CLASS_OK,
55 // The command has been accepted, but to complete the operation, more
56 // information must be sent by the client.
57 ERROR_CLASS_INFO_NEEDED,
59 // The command was not accepted and the requested action did not take place.
60 // This condition is temporary, and the client is encouraged to restart the
61 // command sequence.
62 ERROR_CLASS_TRANSIENT_ERROR,
64 // The command was not accepted and the requested action did not take place.
65 // This condition is rather permanent, and the client is discouraged from
66 // repeating the exact request.
67 ERROR_CLASS_PERMANENT_ERROR,
70 // Returns the error class for given response code. Caller should ensure
71 // that |response_code| is in range 100-599.
72 ErrorClass GetErrorClass(int response_code) {
73 if (response_code >= 100 && response_code <= 199)
74 return ERROR_CLASS_INITIATED;
76 if (response_code >= 200 && response_code <= 299)
77 return ERROR_CLASS_OK;
79 if (response_code >= 300 && response_code <= 399)
80 return ERROR_CLASS_INFO_NEEDED;
82 if (response_code >= 400 && response_code <= 499)
83 return ERROR_CLASS_TRANSIENT_ERROR;
85 if (response_code >= 500 && response_code <= 599)
86 return ERROR_CLASS_PERMANENT_ERROR;
88 // We should not be called on invalid error codes.
89 NOTREACHED() << response_code;
90 return ERROR_CLASS_PERMANENT_ERROR;
93 // Returns network error code for received FTP |response_code|.
94 int GetNetErrorCodeForFtpResponseCode(int response_code) {
95 switch (response_code) {
96 case 421:
97 return net::ERR_FTP_SERVICE_UNAVAILABLE;
98 case 426:
99 return net::ERR_FTP_TRANSFER_ABORTED;
100 case 450:
101 return net::ERR_FTP_FILE_BUSY;
102 case 500:
103 case 501:
104 return net::ERR_FTP_SYNTAX_ERROR;
105 case 502:
106 case 504:
107 return net::ERR_FTP_COMMAND_NOT_SUPPORTED;
108 case 503:
109 return net::ERR_FTP_BAD_COMMAND_SEQUENCE;
110 default:
111 return net::ERR_FTP_FAILED;
115 // From RFC 2428 Section 3:
116 // The text returned in response to the EPSV command MUST be:
117 // <some text> (<d><d><d><tcp-port><d>)
118 // <d> is a delimiter character, ideally to be |
119 bool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response,
120 int* port) {
121 if (response.lines.size() != 1)
122 return false;
123 const char* ptr = response.lines[0].c_str();
124 while (*ptr && *ptr != '(')
125 ++ptr;
126 if (!*ptr)
127 return false;
128 char sep = *(++ptr);
129 if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
130 return false;
131 if (!isdigit(*(++ptr)))
132 return false;
133 *port = *ptr - '0';
134 while (isdigit(*(++ptr))) {
135 *port *= 10;
136 *port += *ptr - '0';
138 if (*ptr != sep)
139 return false;
141 return true;
144 // There are two way we can receive IP address and port.
145 // (127,0,0,1,23,21) IP address and port encapsulated in ().
146 // 127,0,0,1,23,21 IP address and port without ().
148 // See RFC 959, Section 4.1.2
149 bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
150 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 namespace net {
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 = net::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 (port < 1024 || !IsPortAllowedByFtp(port))
962 return Stop(ERR_UNSAFE_PORT);
963 data_connection_port_ = static_cast<uint16>(port);
964 next_state_ = STATE_DATA_CONNECT;
965 break;
967 case ERROR_CLASS_INFO_NEEDED:
968 return Stop(ERR_INVALID_RESPONSE);
969 case ERROR_CLASS_TRANSIENT_ERROR:
970 case ERROR_CLASS_PERMANENT_ERROR:
971 use_epsv_ = false;
972 next_state_ = STATE_CTRL_WRITE_PASV;
973 return OK;
974 default:
975 NOTREACHED();
976 return Stop(ERR_UNEXPECTED);
978 return OK;
981 // PASV command
982 int FtpNetworkTransaction::DoCtrlWritePASV() {
983 std::string command = "PASV";
984 next_state_ = STATE_CTRL_READ;
985 return SendFtpCommand(command, command, COMMAND_PASV);
988 int FtpNetworkTransaction::ProcessResponsePASV(
989 const FtpCtrlResponse& response) {
990 switch (GetErrorClass(response.status_code)) {
991 case ERROR_CLASS_INITIATED:
992 return Stop(ERR_INVALID_RESPONSE);
993 case ERROR_CLASS_OK: {
994 int port;
995 if (!ExtractPortFromPASVResponse(response, &port))
996 return Stop(ERR_INVALID_RESPONSE);
997 if (port < 1024 || !IsPortAllowedByFtp(port))
998 return Stop(ERR_UNSAFE_PORT);
999 data_connection_port_ = static_cast<uint16>(port);
1000 next_state_ = STATE_DATA_CONNECT;
1001 break;
1003 case ERROR_CLASS_INFO_NEEDED:
1004 return Stop(ERR_INVALID_RESPONSE);
1005 case ERROR_CLASS_TRANSIENT_ERROR:
1006 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1007 case ERROR_CLASS_PERMANENT_ERROR:
1008 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1009 default:
1010 NOTREACHED();
1011 return Stop(ERR_UNEXPECTED);
1013 return OK;
1016 // RETR command
1017 int FtpNetworkTransaction::DoCtrlWriteRETR() {
1018 std::string command = "RETR " + GetRequestPathForFtpCommand(false);
1019 next_state_ = STATE_CTRL_READ;
1020 return SendFtpCommand(command, command, COMMAND_RETR);
1023 int FtpNetworkTransaction::ProcessResponseRETR(
1024 const FtpCtrlResponse& response) {
1025 // Resource type should be either filled in by DetectTypecode() or
1026 // detected with CWD. RETR is sent only when the resource is a file.
1027 DCHECK_EQ(RESOURCE_TYPE_FILE, resource_type_);
1029 switch (GetErrorClass(response.status_code)) {
1030 case ERROR_CLASS_INITIATED:
1031 // We want the client to start reading the response at this point.
1032 // It got here either through Start or RestartWithAuth. We want that
1033 // method to complete. Not setting next state here will make DoLoop exit
1034 // and in turn make Start/RestartWithAuth complete.
1035 break;
1036 case ERROR_CLASS_OK:
1037 next_state_ = STATE_CTRL_WRITE_QUIT;
1038 break;
1039 case ERROR_CLASS_INFO_NEEDED:
1040 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1041 case ERROR_CLASS_TRANSIENT_ERROR:
1042 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1043 case ERROR_CLASS_PERMANENT_ERROR:
1044 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1045 default:
1046 NOTREACHED();
1047 return Stop(ERR_UNEXPECTED);
1050 return OK;
1053 // SIZE command
1054 int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1055 std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
1056 next_state_ = STATE_CTRL_READ;
1057 return SendFtpCommand(command, command, COMMAND_SIZE);
1060 int FtpNetworkTransaction::ProcessResponseSIZE(
1061 const FtpCtrlResponse& response) {
1062 switch (GetErrorClass(response.status_code)) {
1063 case ERROR_CLASS_INITIATED:
1064 break;
1065 case ERROR_CLASS_OK:
1066 if (response.lines.size() != 1)
1067 return Stop(ERR_INVALID_RESPONSE);
1068 int64 size;
1069 if (!base::StringToInt64(response.lines[0], &size))
1070 return Stop(ERR_INVALID_RESPONSE);
1071 if (size < 0)
1072 return Stop(ERR_INVALID_RESPONSE);
1074 // A successful response to SIZE does not mean the resource is a file.
1075 // Some FTP servers (for example, the qnx one) send a SIZE even for
1076 // directories.
1077 response_.expected_content_size = size;
1078 break;
1079 case ERROR_CLASS_INFO_NEEDED:
1080 break;
1081 case ERROR_CLASS_TRANSIENT_ERROR:
1082 break;
1083 case ERROR_CLASS_PERMANENT_ERROR:
1084 // It's possible that SIZE failed because the path is a directory.
1085 // TODO(xunjieli): Add a test for this case.
1086 if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
1087 response.status_code != 550) {
1088 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1090 break;
1091 default:
1092 NOTREACHED();
1093 return Stop(ERR_UNEXPECTED);
1096 // If the resource is known beforehand to be a file, RETR should be issued,
1097 // otherwise do CWD which will detect the resource type.
1098 if (resource_type_ == RESOURCE_TYPE_FILE)
1099 EstablishDataConnection(STATE_CTRL_WRITE_RETR);
1100 else
1101 next_state_ = STATE_CTRL_WRITE_CWD;
1102 return OK;
1105 // CWD command
1106 int FtpNetworkTransaction::DoCtrlWriteCWD() {
1107 std::string command = "CWD " + GetRequestPathForFtpCommand(true);
1108 next_state_ = STATE_CTRL_READ;
1109 return SendFtpCommand(command, command, COMMAND_CWD);
1112 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
1113 // CWD should be invoked only when the resource is not a file.
1114 DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
1116 switch (GetErrorClass(response.status_code)) {
1117 case ERROR_CLASS_INITIATED:
1118 return Stop(ERR_INVALID_RESPONSE);
1119 case ERROR_CLASS_OK:
1120 resource_type_ = RESOURCE_TYPE_DIRECTORY;
1121 EstablishDataConnection(STATE_CTRL_WRITE_LIST);
1122 break;
1123 case ERROR_CLASS_INFO_NEEDED:
1124 return Stop(ERR_INVALID_RESPONSE);
1125 case ERROR_CLASS_TRANSIENT_ERROR:
1126 // Some FTP servers send response 451 (not a valid CWD response according
1127 // to RFC 959) instead of 550.
1128 if (response.status_code == 451)
1129 return ProcessResponseCWDNotADirectory();
1131 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1132 case ERROR_CLASS_PERMANENT_ERROR:
1133 if (response.status_code == 550)
1134 return ProcessResponseCWDNotADirectory();
1136 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1137 default:
1138 NOTREACHED();
1139 return Stop(ERR_UNEXPECTED);
1142 return OK;
1145 int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1146 if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
1147 // We're assuming that the resource is a directory, but the server
1148 // says it's not true. The most probable interpretation is that it
1149 // doesn't exist (with FTP we can't be sure).
1150 return Stop(ERR_FILE_NOT_FOUND);
1153 // If it is not a directory, it is probably a file.
1154 resource_type_ = RESOURCE_TYPE_FILE;
1156 EstablishDataConnection(STATE_CTRL_WRITE_RETR);
1157 return OK;
1160 // LIST command
1161 int FtpNetworkTransaction::DoCtrlWriteLIST() {
1162 // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1163 // forces LIST output instead of NLST (which would be ambiguous for us
1164 // to parse).
1165 std::string command("LIST -l");
1166 if (system_type_ == SYSTEM_TYPE_VMS)
1167 command = "LIST *.*;0";
1169 next_state_ = STATE_CTRL_READ;
1170 return SendFtpCommand(command, command, COMMAND_LIST);
1173 int FtpNetworkTransaction::ProcessResponseLIST(
1174 const FtpCtrlResponse& response) {
1175 // Resource type should be either filled in by DetectTypecode() or
1176 // detected with CWD. LIST is sent only when the resource is a directory.
1177 DCHECK_EQ(RESOURCE_TYPE_DIRECTORY, resource_type_);
1179 switch (GetErrorClass(response.status_code)) {
1180 case ERROR_CLASS_INITIATED:
1181 // We want the client to start reading the response at this point.
1182 // It got here either through Start or RestartWithAuth. We want that
1183 // method to complete. Not setting next state here will make DoLoop exit
1184 // and in turn make Start/RestartWithAuth complete.
1185 response_.is_directory_listing = true;
1186 break;
1187 case ERROR_CLASS_OK:
1188 response_.is_directory_listing = true;
1189 next_state_ = STATE_CTRL_WRITE_QUIT;
1190 break;
1191 case ERROR_CLASS_INFO_NEEDED:
1192 return Stop(ERR_INVALID_RESPONSE);
1193 case ERROR_CLASS_TRANSIENT_ERROR:
1194 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1195 case ERROR_CLASS_PERMANENT_ERROR:
1196 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1197 default:
1198 NOTREACHED();
1199 return Stop(ERR_UNEXPECTED);
1201 return OK;
1204 // QUIT command
1205 int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1206 std::string command = "QUIT";
1207 next_state_ = STATE_CTRL_READ;
1208 return SendFtpCommand(command, command, COMMAND_QUIT);
1211 int FtpNetworkTransaction::ProcessResponseQUIT(
1212 const FtpCtrlResponse& response) {
1213 ctrl_socket_->Disconnect();
1214 return last_error_;
1217 // Data Connection
1219 int FtpNetworkTransaction::DoDataConnect() {
1220 next_state_ = STATE_DATA_CONNECT_COMPLETE;
1221 IPEndPoint ip_endpoint;
1222 AddressList data_address;
1223 // Connect to the same host as the control socket to prevent PASV port
1224 // scanning attacks.
1225 int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
1226 if (rv != OK)
1227 return Stop(rv);
1228 data_address = AddressList::CreateFromIPAddress(
1229 ip_endpoint.address(), data_connection_port_);
1230 data_socket_ = socket_factory_->CreateTransportClientSocket(
1231 data_address, net_log_.net_log(), net_log_.source());
1232 net_log_.AddEvent(
1233 NetLog::TYPE_FTP_DATA_CONNECTION,
1234 data_socket_->NetLog().source().ToEventParametersCallback());
1235 return data_socket_->Connect(io_callback_);
1238 int FtpNetworkTransaction::DoDataConnectComplete(int result) {
1239 if (result != OK && use_epsv_) {
1240 // It's possible we hit a broken server, sadly. They can break in different
1241 // ways. Some time out, some reset a connection. Fall back to PASV.
1242 // TODO(phajdan.jr): remember it for future transactions with this server.
1243 // TODO(phajdan.jr): write a test for this code path.
1244 use_epsv_ = false;
1245 next_state_ = STATE_CTRL_WRITE_PASV;
1246 return OK;
1249 // Only record the connection error after we've applied all our fallbacks.
1250 // We want to capture the final error, one we're not going to recover from.
1251 RecordDataConnectionError(result);
1253 if (result != OK)
1254 return Stop(result);
1256 next_state_ = state_after_data_connect_complete_;
1257 return OK;
1260 int FtpNetworkTransaction::DoDataRead() {
1261 DCHECK(read_data_buf_.get());
1262 DCHECK_GT(read_data_buf_len_, 0);
1264 if (data_socket_ == NULL || !data_socket_->IsConnected()) {
1265 // If we don't destroy the data socket completely, some servers will wait
1266 // for us (http://crbug.com/21127). The half-closed TCP connection needs
1267 // to be closed on our side too.
1268 data_socket_.reset();
1270 if (ctrl_socket_->IsConnected()) {
1271 // Wait for the server's response, we should get it before sending QUIT.
1272 next_state_ = STATE_CTRL_READ;
1273 return OK;
1276 // We are no longer connected to the server, so just finish the transaction.
1277 return Stop(OK);
1280 next_state_ = STATE_DATA_READ_COMPLETE;
1281 read_data_buf_->data()[0] = 0;
1282 return data_socket_->Read(
1283 read_data_buf_.get(), read_data_buf_len_, io_callback_);
1286 int FtpNetworkTransaction::DoDataReadComplete(int result) {
1287 return result;
1290 // We're using a histogram as a group of counters, with one bucket for each
1291 // enumeration value. We're only interested in the values of the counters.
1292 // Ignore the shape, average, and standard deviation of the histograms because
1293 // they are meaningless.
1295 // We use two histograms. In the first histogram we tally whether the user has
1296 // seen an error of that type during the session. In the second histogram we
1297 // tally the total number of times the users sees each errer.
1298 void FtpNetworkTransaction::RecordDataConnectionError(int result) {
1299 // Gather data for http://crbug.com/3073. See how many users have trouble
1300 // establishing FTP data connection in passive FTP mode.
1301 enum {
1302 // Data connection successful.
1303 NET_ERROR_OK = 0,
1305 // Local firewall blocked the connection.
1306 NET_ERROR_ACCESS_DENIED = 1,
1308 // Connection timed out.
1309 NET_ERROR_TIMED_OUT = 2,
1311 // Connection has been estabilished, but then got broken (either reset
1312 // or aborted).
1313 NET_ERROR_CONNECTION_BROKEN = 3,
1315 // Connection has been refused.
1316 NET_ERROR_CONNECTION_REFUSED = 4,
1318 // No connection to the internet.
1319 NET_ERROR_INTERNET_DISCONNECTED = 5,
1321 // Could not reach the destination address.
1322 NET_ERROR_ADDRESS_UNREACHABLE = 6,
1324 // A programming error in our network stack.
1325 NET_ERROR_UNEXPECTED = 7,
1327 // Other kind of error.
1328 NET_ERROR_OTHER = 20,
1330 NUM_OF_NET_ERROR_TYPES
1331 } type;
1332 switch (result) {
1333 case OK:
1334 type = NET_ERROR_OK;
1335 break;
1336 case ERR_ACCESS_DENIED:
1337 case ERR_NETWORK_ACCESS_DENIED:
1338 type = NET_ERROR_ACCESS_DENIED;
1339 break;
1340 case ERR_TIMED_OUT:
1341 type = NET_ERROR_TIMED_OUT;
1342 break;
1343 case ERR_CONNECTION_ABORTED:
1344 case ERR_CONNECTION_RESET:
1345 case ERR_CONNECTION_CLOSED:
1346 type = NET_ERROR_CONNECTION_BROKEN;
1347 break;
1348 case ERR_CONNECTION_FAILED:
1349 case ERR_CONNECTION_REFUSED:
1350 type = NET_ERROR_CONNECTION_REFUSED;
1351 break;
1352 case ERR_INTERNET_DISCONNECTED:
1353 type = NET_ERROR_INTERNET_DISCONNECTED;
1354 break;
1355 case ERR_ADDRESS_INVALID:
1356 case ERR_ADDRESS_UNREACHABLE:
1357 type = NET_ERROR_ADDRESS_UNREACHABLE;
1358 break;
1359 case ERR_UNEXPECTED:
1360 type = NET_ERROR_UNEXPECTED;
1361 break;
1362 default:
1363 type = NET_ERROR_OTHER;
1364 break;
1366 static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
1368 DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
1369 if (!had_error_type[type]) {
1370 had_error_type[type] = true;
1371 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1372 type, NUM_OF_NET_ERROR_TYPES);
1374 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1375 type, NUM_OF_NET_ERROR_TYPES);
1378 } // namespace net