1 // Copyright (c) 2010 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.
4
5 #include "net/ftp/ftp_network_transaction.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/metrics/histogram.h"
9 #include "base/string_number_conversions.h"
10 #include "base/string_util.h"
11 #include "base/utf_string_conversions.h"
12 #include "net/base/address_list.h"
13 #include "net/base/connection_type_histograms.h"
14 #include "net/base/escape.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/net_log.h"
17 #include "net/base/net_util.h"
18 #include "net/ftp/ftp_network_session.h"
19 #include "net/ftp/ftp_request_info.h"
20 #include "net/ftp/ftp_util.h"
21 #include "net/socket/client_socket.h"
22 #include "net/socket/client_socket_factory.h"
23
24 // TODO(ibrar): Try to avoid sscanf.
25 #if !defined(COMPILER_MSVC)
26 #define sscanf_s sscanf
27 #endif
28
29 const char kCRLF[] = "\r\n";
30
31 const int kCtrlBufLen = 1024;
32
33 namespace {
34
35 // Returns true if |input| can be safely used as a part of FTP command.
IsValidFTPCommandString(const std::string & input)36 bool IsValidFTPCommandString(const std::string& input) {
37 // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
38 // characters in the command if the request path contains them. To be
39 // compatible, we do the same and allow non-ASCII characters in a command.
40
41 // Protect agains newline injection attack.
42 if (input.find_first_of("\r\n") != std::string::npos)
43 return false;
44
45 return true;
46 }
47
48 enum ErrorClass {
49 // The requested action was initiated. The client should expect another
50 // reply before issuing the next command.
51 ERROR_CLASS_INITIATED,
52
53 // The requested action has been successfully completed.
54 ERROR_CLASS_OK,
55
56 // The command has been accepted, but to complete the operation, more
57 // information must be sent by the client.
58 ERROR_CLASS_INFO_NEEDED,
59
60 // The command was not accepted and the requested action did not take place.
61 // This condition is temporary, and the client is encouraged to restart the
62 // command sequence.
63 ERROR_CLASS_TRANSIENT_ERROR,
64
65 // The command was not accepted and the requested action did not take place.
66 // This condition is rather permanent, and the client is discouraged from
67 // repeating the exact request.
68 ERROR_CLASS_PERMANENT_ERROR,
69 };
70
71 // Returns the error class for given response code. Caller should ensure
72 // that |response_code| is in range 100-599.
GetErrorClass(int response_code)73 ErrorClass GetErrorClass(int response_code) {
74 if (response_code >= 100 && response_code <= 199)
75 return ERROR_CLASS_INITIATED;
76
77 if (response_code >= 200 && response_code <= 299)
78 return ERROR_CLASS_OK;
79
80 if (response_code >= 300 && response_code <= 399)
81 return ERROR_CLASS_INFO_NEEDED;
82
83 if (response_code >= 400 && response_code <= 499)
84 return ERROR_CLASS_TRANSIENT_ERROR;
85
86 if (response_code >= 500 && response_code <= 599)
87 return ERROR_CLASS_PERMANENT_ERROR;
88
89 // We should not be called on invalid error codes.
90 NOTREACHED() << response_code;
91 return ERROR_CLASS_PERMANENT_ERROR;
92 }
93
94 // Returns network error code for received FTP |response_code|.
GetNetErrorCodeForFtpResponseCode(int response_code)95 int GetNetErrorCodeForFtpResponseCode(int response_code) {
96 switch (response_code) {
97 case 421:
98 return net::ERR_FTP_SERVICE_UNAVAILABLE;
99 case 426:
100 return net::ERR_FTP_TRANSFER_ABORTED;
101 case 450:
102 return net::ERR_FTP_FILE_BUSY;
103 case 500:
104 case 501:
105 return net::ERR_FTP_SYNTAX_ERROR;
106 case 502:
107 case 504:
108 return net::ERR_FTP_COMMAND_NOT_SUPPORTED;
109 case 503:
110 return net::ERR_FTP_BAD_COMMAND_SEQUENCE;
111 default:
112 return net::ERR_FTP_FAILED;
113 }
114 }
115
116 // From RFC 2428 Section 3:
117 // The text returned in response to the EPSV command MUST be:
118 // <some text> (<d><d><d><tcp-port><d>)
119 // <d> is a delimiter character, ideally to be |
ExtractPortFromEPSVResponse(const net::FtpCtrlResponse & response,int * port)120 bool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response,
121 int* port) {
122 if (response.lines.size() != 1)
123 return false;
124 const char* ptr = response.lines[0].c_str();
125 while (*ptr && *ptr != '(')
126 ++ptr;
127 if (!*ptr)
128 return false;
129 char sep = *(++ptr);
130 if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
131 return false;
132 if (!isdigit(*(++ptr)))
133 return false;
134 *port = *ptr - '0';
135 while (isdigit(*(++ptr))) {
136 *port *= 10;
137 *port += *ptr - '0';
138 }
139 if (*ptr != sep)
140 return false;
141
142 return true;
143 }
144
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 ().
148 //
149 // See RFC 959, Section 4.1.2
ExtractPortFromPASVResponse(const net::FtpCtrlResponse & response,int * port)150 bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
151 int* port) {
152 if (response.lines.size() != 1)
153 return false;
154 const char* ptr = response.lines[0].c_str();
155 while (*ptr && *ptr != '(') // Try with bracket.
156 ++ptr;
157 if (*ptr) {
158 ++ptr;
159 } else {
160 ptr = response.lines[0].c_str(); // Try without bracket.
161 while (*ptr && *ptr != ',')
162 ++ptr;
163 while (*ptr && *ptr != ' ')
164 --ptr;
165 }
166 int i0, i1, i2, i3, p0, p1;
167 if (sscanf_s(ptr, "%d,%d,%d,%d,%d,%d", &i0, &i1, &i2, &i3, &p0, &p1) != 6)
168 return false;
169
170 // Ignore the IP address supplied in the response. We are always going
171 // to connect back to the same server to prevent FTP PASV port scanning.
172 *port = (p0 << 8) + p1;
173 return true;
174 }
175
176 } // namespace
177
178 namespace net {
179
FtpNetworkTransaction(FtpNetworkSession * session,ClientSocketFactory * socket_factory)180 FtpNetworkTransaction::FtpNetworkTransaction(
181 FtpNetworkSession* session,
182 ClientSocketFactory* socket_factory)
183 : command_sent_(COMMAND_NONE),
184 ALLOW_THIS_IN_INITIALIZER_LIST(
185 io_callback_(this, &FtpNetworkTransaction::OnIOComplete)),
186 user_callback_(NULL),
187 session_(session),
188 request_(NULL),
189 resolver_(session->host_resolver()),
190 read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
191 ctrl_response_buffer_(new FtpCtrlResponseBuffer()),
192 read_data_buf_len_(0),
193 last_error_(OK),
194 system_type_(SYSTEM_TYPE_UNKNOWN),
195 // Use image (binary) transfer by default. It should always work,
196 // whereas the ascii transfer may damage binary data.
197 data_type_(DATA_TYPE_IMAGE),
198 resource_type_(RESOURCE_TYPE_UNKNOWN),
199 use_epsv_(true),
200 data_connection_port_(0),
201 socket_factory_(socket_factory),
202 next_state_(STATE_NONE) {
203 }
204
~FtpNetworkTransaction()205 FtpNetworkTransaction::~FtpNetworkTransaction() {
206 }
207
Stop(int error)208 int FtpNetworkTransaction::Stop(int error) {
209 if (command_sent_ == COMMAND_QUIT)
210 return error;
211
212 next_state_ = STATE_CTRL_WRITE_QUIT;
213 last_error_ = error;
214 return OK;
215 }
216
RestartIgnoringLastError(CompletionCallback * callback)217 int FtpNetworkTransaction::RestartIgnoringLastError(
218 CompletionCallback* callback) {
219 return ERR_NOT_IMPLEMENTED;
220 }
221
Start(const FtpRequestInfo * request_info,CompletionCallback * callback,const BoundNetLog & net_log)222 int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
223 CompletionCallback* callback,
224 const BoundNetLog& net_log) {
225 net_log_ = net_log;
226 request_ = request_info;
227
228 if (request_->url.has_username()) {
229 GetIdentityFromURL(request_->url, &username_, &password_);
230 } else {
231 username_ = ASCIIToUTF16("anonymous");
232 password_ = ASCIIToUTF16("chrome@example.com");
233 }
234
235 DetectTypecode();
236
237 next_state_ = STATE_CTRL_RESOLVE_HOST;
238 int rv = DoLoop(OK);
239 if (rv == ERR_IO_PENDING)
240 user_callback_ = callback;
241 return rv;
242 }
243
RestartWithAuth(const string16 & username,const string16 & password,CompletionCallback * callback)244 int FtpNetworkTransaction::RestartWithAuth(const string16& username,
245 const string16& password,
246 CompletionCallback* callback) {
247 ResetStateForRestart();
248
249 username_ = username;
250 password_ = password;
251
252 next_state_ = STATE_CTRL_RESOLVE_HOST;
253 int rv = DoLoop(OK);
254 if (rv == ERR_IO_PENDING)
255 user_callback_ = callback;
256 return rv;
257 }
258
Read(IOBuffer * buf,int buf_len,CompletionCallback * callback)259 int FtpNetworkTransaction::Read(IOBuffer* buf,
260 int buf_len,
261 CompletionCallback* callback) {
262 DCHECK(buf);
263 DCHECK_GT(buf_len, 0);
264
265 read_data_buf_ = buf;
266 read_data_buf_len_ = buf_len;
267
268 next_state_ = STATE_DATA_READ;
269 int rv = DoLoop(OK);
270 if (rv == ERR_IO_PENDING)
271 user_callback_ = callback;
272 return rv;
273 }
274
GetResponseInfo() const275 const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
276 return &response_;
277 }
278
GetLoadState() const279 LoadState FtpNetworkTransaction::GetLoadState() const {
280 if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
281 return LOAD_STATE_RESOLVING_HOST;
282
283 if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
284 next_state_ == STATE_DATA_CONNECT_COMPLETE)
285 return LOAD_STATE_CONNECTING;
286
287 if (next_state_ == STATE_DATA_READ_COMPLETE)
288 return LOAD_STATE_READING_RESPONSE;
289
290 if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
291 return LOAD_STATE_READING_RESPONSE;
292
293 if (command_sent_ == COMMAND_QUIT)
294 return LOAD_STATE_IDLE;
295
296 if (command_sent_ != COMMAND_NONE)
297 return LOAD_STATE_SENDING_REQUEST;
298
299 return LOAD_STATE_IDLE;
300 }
301
GetUploadProgress() const302 uint64 FtpNetworkTransaction::GetUploadProgress() const {
303 return 0;
304 }
305
ResetStateForRestart()306 void FtpNetworkTransaction::ResetStateForRestart() {
307 command_sent_ = COMMAND_NONE;
308 user_callback_ = NULL;
309 response_ = FtpResponseInfo();
310 read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
311 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer());
312 read_data_buf_ = NULL;
313 read_data_buf_len_ = 0;
314 if (write_buf_)
315 write_buf_->SetOffset(0);
316 last_error_ = OK;
317 data_connection_port_ = 0;
318 ctrl_socket_.reset();
319 data_socket_.reset();
320 next_state_ = STATE_NONE;
321 }
322
DoCallback(int rv)323 void FtpNetworkTransaction::DoCallback(int rv) {
324 DCHECK(rv != ERR_IO_PENDING);
325 DCHECK(user_callback_);
326
327 // Since Run may result in Read being called, clear callback_ up front.
328 CompletionCallback* c = user_callback_;
329 user_callback_ = NULL;
330 c->Run(rv);
331 }
332
OnIOComplete(int result)333 void FtpNetworkTransaction::OnIOComplete(int result) {
334 int rv = DoLoop(result);
335 if (rv != ERR_IO_PENDING)
336 DoCallback(rv);
337 }
338
ProcessCtrlResponse()339 int FtpNetworkTransaction::ProcessCtrlResponse() {
340 FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
341
342 int rv = OK;
343 switch (command_sent_) {
344 case COMMAND_NONE:
345 // TODO(phajdan.jr): Check for errors in the welcome message.
346 next_state_ = STATE_CTRL_WRITE_USER;
347 break;
348 case COMMAND_USER:
349 rv = ProcessResponseUSER(response);
350 break;
351 case COMMAND_PASS:
352 rv = ProcessResponsePASS(response);
353 break;
354 case COMMAND_SYST:
355 rv = ProcessResponseSYST(response);
356 break;
357 case COMMAND_PWD:
358 rv = ProcessResponsePWD(response);
359 break;
360 case COMMAND_TYPE:
361 rv = ProcessResponseTYPE(response);
362 break;
363 case COMMAND_EPSV:
364 rv = ProcessResponseEPSV(response);
365 break;
366 case COMMAND_PASV:
367 rv = ProcessResponsePASV(response);
368 break;
369 case COMMAND_SIZE:
370 rv = ProcessResponseSIZE(response);
371 break;
372 case COMMAND_RETR:
373 rv = ProcessResponseRETR(response);
374 break;
375 case COMMAND_CWD:
376 rv = ProcessResponseCWD(response);
377 break;
378 case COMMAND_LIST:
379 rv = ProcessResponseLIST(response);
380 break;
381 case COMMAND_QUIT:
382 rv = ProcessResponseQUIT(response);
383 break;
384 default:
385 LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
386 return ERR_UNEXPECTED;
387 }
388
389 // We may get multiple responses for some commands,
390 // see http://crbug.com/18036.
391 while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
392 response = ctrl_response_buffer_->PopResponse();
393
394 switch (command_sent_) {
395 case COMMAND_RETR:
396 rv = ProcessResponseRETR(response);
397 break;
398 case COMMAND_LIST:
399 rv = ProcessResponseLIST(response);
400 break;
401 default:
402 // Multiple responses for other commands are invalid.
403 return Stop(ERR_INVALID_RESPONSE);
404 }
405 }
406
407 return rv;
408 }
409
410 // Used to prepare and send FTP command.
SendFtpCommand(const std::string & command,Command cmd)411 int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
412 Command cmd) {
413 // If we send a new command when we still have unprocessed responses
414 // for previous commands, the response receiving code will have no way to know
415 // which responses are for which command.
416 DCHECK(!ctrl_response_buffer_->ResponseAvailable());
417
418 DCHECK(!write_command_buf_);
419 DCHECK(!write_buf_);
420
421 if (!IsValidFTPCommandString(command)) {
422 // Callers should validate the command themselves and return a more specific
423 // error code.
424 NOTREACHED();
425 return Stop(ERR_UNEXPECTED);
426 }
427
428 command_sent_ = cmd;
429
430 write_command_buf_ = new IOBufferWithSize(command.length() + 2);
431 write_buf_ = new DrainableIOBuffer(write_command_buf_,
432 write_command_buf_->size());
433 memcpy(write_command_buf_->data(), command.data(), command.length());
434 memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
435
436 next_state_ = STATE_CTRL_WRITE;
437 return OK;
438 }
439
GetRequestPathForFtpCommand(bool is_directory) const440 std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
441 bool is_directory) const {
442 std::string path(current_remote_directory_);
443 if (request_->url.has_path()) {
444 std::string gurl_path(request_->url.path());
445
446 // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
447 std::string::size_type pos = gurl_path.rfind(';');
448 if (pos != std::string::npos)
449 gurl_path.resize(pos);
450
451 path.append(gurl_path);
452 }
453 // Make sure that if the path is expected to be a file, it won't end
454 // with a trailing slash.
455 if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
456 path.erase(path.length() - 1);
457 UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
458 UnescapeRule::URL_SPECIAL_CHARS;
459 // This may unescape to non-ASCII characters, but we allow that. See the
460 // comment for IsValidFTPCommandString.
461 path = UnescapeURLComponent(path, unescape_rules);
462
463 if (system_type_ == SYSTEM_TYPE_VMS) {
464 if (is_directory)
465 path = FtpUtil::UnixDirectoryPathToVMS(path);
466 else
467 path = FtpUtil::UnixFilePathToVMS(path);
468 }
469
470 DCHECK(IsValidFTPCommandString(path));
471 return path;
472 }
473
DetectTypecode()474 void FtpNetworkTransaction::DetectTypecode() {
475 if (!request_->url.has_path())
476 return;
477 std::string gurl_path(request_->url.path());
478
479 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
480 std::string::size_type pos = gurl_path.rfind(';');
481 if (pos == std::string::npos)
482 return;
483 std::string typecode_string(gurl_path.substr(pos));
484 if (typecode_string == ";type=a") {
485 data_type_ = DATA_TYPE_ASCII;
486 resource_type_ = RESOURCE_TYPE_FILE;
487 } else if (typecode_string == ";type=i") {
488 data_type_ = DATA_TYPE_IMAGE;
489 resource_type_ = RESOURCE_TYPE_FILE;
490 } else if (typecode_string == ";type=d") {
491 resource_type_ = RESOURCE_TYPE_DIRECTORY;
492 }
493 }
494
DoLoop(int result)495 int FtpNetworkTransaction::DoLoop(int result) {
496 DCHECK(next_state_ != STATE_NONE);
497
498 int rv = result;
499 do {
500 State state = next_state_;
501 next_state_ = STATE_NONE;
502 switch (state) {
503 case STATE_CTRL_RESOLVE_HOST:
504 DCHECK(rv == OK);
505 rv = DoCtrlResolveHost();
506 break;
507 case STATE_CTRL_RESOLVE_HOST_COMPLETE:
508 rv = DoCtrlResolveHostComplete(rv);
509 break;
510 case STATE_CTRL_CONNECT:
511 DCHECK(rv == OK);
512 rv = DoCtrlConnect();
513 break;
514 case STATE_CTRL_CONNECT_COMPLETE:
515 rv = DoCtrlConnectComplete(rv);
516 break;
517 case STATE_CTRL_READ:
518 DCHECK(rv == OK);
519 rv = DoCtrlRead();
520 break;
521 case STATE_CTRL_READ_COMPLETE:
522 rv = DoCtrlReadComplete(rv);
523 break;
524 case STATE_CTRL_WRITE:
525 DCHECK(rv == OK);
526 rv = DoCtrlWrite();
527 break;
528 case STATE_CTRL_WRITE_COMPLETE:
529 rv = DoCtrlWriteComplete(rv);
530 break;
531 case STATE_CTRL_WRITE_USER:
532 DCHECK(rv == OK);
533 rv = DoCtrlWriteUSER();
534 break;
535 case STATE_CTRL_WRITE_PASS:
536 DCHECK(rv == OK);
537 rv = DoCtrlWritePASS();
538 break;
539 case STATE_CTRL_WRITE_SYST:
540 DCHECK(rv == OK);
541 rv = DoCtrlWriteSYST();
542 break;
543 case STATE_CTRL_WRITE_PWD:
544 DCHECK(rv == OK);
545 rv = DoCtrlWritePWD();
546 break;
547 case STATE_CTRL_WRITE_TYPE:
548 DCHECK(rv == OK);
549 rv = DoCtrlWriteTYPE();
550 break;
551 case STATE_CTRL_WRITE_EPSV:
552 DCHECK(rv == OK);
553 rv = DoCtrlWriteEPSV();
554 break;
555 case STATE_CTRL_WRITE_PASV:
556 DCHECK(rv == OK);
557 rv = DoCtrlWritePASV();
558 break;
559 case STATE_CTRL_WRITE_RETR:
560 DCHECK(rv == OK);
561 rv = DoCtrlWriteRETR();
562 break;
563 case STATE_CTRL_WRITE_SIZE:
564 DCHECK(rv == OK);
565 rv = DoCtrlWriteSIZE();
566 break;
567 case STATE_CTRL_WRITE_CWD:
568 DCHECK(rv == OK);
569 rv = DoCtrlWriteCWD();
570 break;
571 case STATE_CTRL_WRITE_LIST:
572 DCHECK(rv == OK);
573 rv = DoCtrlWriteLIST();
574 break;
575 case STATE_CTRL_WRITE_QUIT:
576 DCHECK(rv == OK);
577 rv = DoCtrlWriteQUIT();
578 break;
579 case STATE_DATA_CONNECT:
580 DCHECK(rv == OK);
581 rv = DoDataConnect();
582 break;
583 case STATE_DATA_CONNECT_COMPLETE:
584 rv = DoDataConnectComplete(rv);
585 break;
586 case STATE_DATA_READ:
587 DCHECK(rv == OK);
588 rv = DoDataRead();
589 break;
590 case STATE_DATA_READ_COMPLETE:
591 rv = DoDataReadComplete(rv);
592 break;
593 default:
594 NOTREACHED() << "bad state";
595 rv = ERR_UNEXPECTED;
596 break;
597 }
598 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
599 return rv;
600 }
601
DoCtrlResolveHost()602 int FtpNetworkTransaction::DoCtrlResolveHost() {
603 next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
604
605 HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
606 // No known referrer.
607 return resolver_.Resolve(info, &addresses_, &io_callback_, net_log_);
608 }
609
DoCtrlResolveHostComplete(int result)610 int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
611 if (result == OK)
612 next_state_ = STATE_CTRL_CONNECT;
613 return result;
614 }
615
DoCtrlConnect()616 int FtpNetworkTransaction::DoCtrlConnect() {
617 next_state_ = STATE_CTRL_CONNECT_COMPLETE;
618 ctrl_socket_.reset(socket_factory_->CreateTransportClientSocket(
619 addresses_, net_log_.net_log(), net_log_.source()));
620 return ctrl_socket_->Connect(&io_callback_);
621 }
622
DoCtrlConnectComplete(int result)623 int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
624 if (result == OK) {
625 // Put the peer's IP address and port into the response.
626 AddressList address;
627 result = ctrl_socket_->GetPeerAddress(&address);
628 if (result == OK) {
629 response_.socket_address = HostPortPair::FromAddrInfo(address.head());
630 next_state_ = STATE_CTRL_READ;
631 }
632 }
633 return result;
634 }
635
DoCtrlRead()636 int FtpNetworkTransaction::DoCtrlRead() {
637 next_state_ = STATE_CTRL_READ_COMPLETE;
638 return ctrl_socket_->Read(
639 read_ctrl_buf_,
640 kCtrlBufLen,
641 &io_callback_);
642 }
643
DoCtrlReadComplete(int result)644 int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
645 if (result == 0) {
646 // Some servers (for example Pure-FTPd) apparently close the control
647 // connection when anonymous login is not permitted. For more details
648 // see http://crbug.com/25023.
649 if (command_sent_ == COMMAND_USER && username_ == ASCIIToUTF16("anonymous"))
650 response_.needs_auth = true;
651 return Stop(ERR_EMPTY_RESPONSE);
652 }
653 if (result < 0)
654 return Stop(result);
655
656 ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
657
658 if (!ctrl_response_buffer_->ResponseAvailable()) {
659 // Read more data from the control socket.
660 next_state_ = STATE_CTRL_READ;
661 return OK;
662 }
663
664 return ProcessCtrlResponse();
665 }
666
DoCtrlWrite()667 int FtpNetworkTransaction::DoCtrlWrite() {
668 next_state_ = STATE_CTRL_WRITE_COMPLETE;
669
670 return ctrl_socket_->Write(write_buf_,
671 write_buf_->BytesRemaining(),
672 &io_callback_);
673 }
674
DoCtrlWriteComplete(int result)675 int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
676 if (result < 0)
677 return result;
678
679 write_buf_->DidConsume(result);
680 if (write_buf_->BytesRemaining() == 0) {
681 // Clear the write buffer.
682 write_buf_ = NULL;
683 write_command_buf_ = NULL;
684
685 next_state_ = STATE_CTRL_READ;
686 } else {
687 next_state_ = STATE_CTRL_WRITE;
688 }
689 return OK;
690 }
691
692 // FTP Commands and responses
693
694 // USER Command.
DoCtrlWriteUSER()695 int FtpNetworkTransaction::DoCtrlWriteUSER() {
696 std::string command = "USER " + UTF16ToUTF8(username_);
697
698 if (!IsValidFTPCommandString(command))
699 return Stop(ERR_MALFORMED_IDENTITY);
700
701 next_state_ = STATE_CTRL_READ;
702 return SendFtpCommand(command, COMMAND_USER);
703 }
704
ProcessResponseUSER(const FtpCtrlResponse & response)705 int FtpNetworkTransaction::ProcessResponseUSER(
706 const FtpCtrlResponse& response) {
707 switch (GetErrorClass(response.status_code)) {
708 case ERROR_CLASS_OK:
709 next_state_ = STATE_CTRL_WRITE_SYST;
710 break;
711 case ERROR_CLASS_INFO_NEEDED:
712 next_state_ = STATE_CTRL_WRITE_PASS;
713 break;
714 case ERROR_CLASS_TRANSIENT_ERROR:
715 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
716 case ERROR_CLASS_PERMANENT_ERROR:
717 response_.needs_auth = true;
718 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
719 default:
720 NOTREACHED();
721 return Stop(ERR_UNEXPECTED);
722 }
723 return OK;
724 }
725
726 // PASS command.
DoCtrlWritePASS()727 int FtpNetworkTransaction::DoCtrlWritePASS() {
728 std::string command = "PASS " + UTF16ToUTF8(password_);
729
730 if (!IsValidFTPCommandString(command))
731 return Stop(ERR_MALFORMED_IDENTITY);
732
733 next_state_ = STATE_CTRL_READ;
734 return SendFtpCommand(command, COMMAND_PASS);
735 }
736
ProcessResponsePASS(const FtpCtrlResponse & response)737 int FtpNetworkTransaction::ProcessResponsePASS(
738 const FtpCtrlResponse& response) {
739 switch (GetErrorClass(response.status_code)) {
740 case ERROR_CLASS_OK:
741 next_state_ = STATE_CTRL_WRITE_SYST;
742 break;
743 case ERROR_CLASS_INFO_NEEDED:
744 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
745 case ERROR_CLASS_TRANSIENT_ERROR:
746 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
747 case ERROR_CLASS_PERMANENT_ERROR:
748 response_.needs_auth = true;
749 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
750 default:
751 NOTREACHED();
752 return Stop(ERR_UNEXPECTED);
753 }
754 return OK;
755 }
756
757 // SYST command.
DoCtrlWriteSYST()758 int FtpNetworkTransaction::DoCtrlWriteSYST() {
759 std::string command = "SYST";
760 next_state_ = STATE_CTRL_READ;
761 return SendFtpCommand(command, COMMAND_SYST);
762 }
763
ProcessResponseSYST(const FtpCtrlResponse & response)764 int FtpNetworkTransaction::ProcessResponseSYST(
765 const FtpCtrlResponse& response) {
766 switch (GetErrorClass(response.status_code)) {
767 case ERROR_CLASS_INITIATED:
768 return Stop(ERR_INVALID_RESPONSE);
769 case ERROR_CLASS_OK: {
770 // All important info should be on the first line.
771 std::string line = response.lines[0];
772 // The response should be ASCII, which allows us to do case-insensitive
773 // comparisons easily. If it is not ASCII, we leave the system type
774 // as unknown.
775 if (IsStringASCII(line)) {
776 line = StringToLowerASCII(line);
777 // The "magic" strings we test for below have been gathered by an
778 // empirical study.
779 if (line.find("l8") != std::string::npos ||
780 line.find("unix") != std::string::npos ||
781 line.find("bsd") != std::string::npos) {
782 system_type_ = SYSTEM_TYPE_UNIX;
783 } else if (line.find("win32") != std::string::npos ||
784 line.find("windows") != std::string::npos) {
785 system_type_ = SYSTEM_TYPE_WINDOWS;
786 } else if (line.find("os/2") != std::string::npos) {
787 system_type_ = SYSTEM_TYPE_OS2;
788 } else if (line.find("vms") != std::string::npos) {
789 system_type_ = SYSTEM_TYPE_VMS;
790 }
791 }
792 next_state_ = STATE_CTRL_WRITE_PWD;
793 break;
794 }
795 case ERROR_CLASS_INFO_NEEDED:
796 return Stop(ERR_INVALID_RESPONSE);
797 case ERROR_CLASS_TRANSIENT_ERROR:
798 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
799 case ERROR_CLASS_PERMANENT_ERROR:
800 // Server does not recognize the SYST command so proceed.
801 next_state_ = STATE_CTRL_WRITE_PWD;
802 break;
803 default:
804 NOTREACHED();
805 return Stop(ERR_UNEXPECTED);
806 }
807 return OK;
808 }
809
810 // PWD command.
DoCtrlWritePWD()811 int FtpNetworkTransaction::DoCtrlWritePWD() {
812 std::string command = "PWD";
813 next_state_ = STATE_CTRL_READ;
814 return SendFtpCommand(command, COMMAND_PWD);
815 }
816
ProcessResponsePWD(const FtpCtrlResponse & response)817 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
818 switch (GetErrorClass(response.status_code)) {
819 case ERROR_CLASS_INITIATED:
820 return Stop(ERR_INVALID_RESPONSE);
821 case ERROR_CLASS_OK: {
822 // The info we look for should be on the first line.
823 std::string line = response.lines[0];
824 if (line.empty())
825 return Stop(ERR_INVALID_RESPONSE);
826 std::string::size_type quote_pos = line.find('"');
827 if (quote_pos != std::string::npos) {
828 line = line.substr(quote_pos + 1);
829 quote_pos = line.find('"');
830 if (quote_pos == std::string::npos)
831 return Stop(ERR_INVALID_RESPONSE);
832 line = line.substr(0, quote_pos);
833 }
834 if (system_type_ == SYSTEM_TYPE_VMS)
835 line = FtpUtil::VMSPathToUnix(line);
836 if (line.length() && line[line.length() - 1] == '/')
837 line.erase(line.length() - 1);
838 current_remote_directory_ = line;
839 next_state_ = STATE_CTRL_WRITE_TYPE;
840 break;
841 }
842 case ERROR_CLASS_INFO_NEEDED:
843 return Stop(ERR_INVALID_RESPONSE);
844 case ERROR_CLASS_TRANSIENT_ERROR:
845 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
846 case ERROR_CLASS_PERMANENT_ERROR:
847 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
848 default:
849 NOTREACHED();
850 return Stop(ERR_UNEXPECTED);
851 }
852 return OK;
853 }
854
855 // TYPE command.
DoCtrlWriteTYPE()856 int FtpNetworkTransaction::DoCtrlWriteTYPE() {
857 std::string command = "TYPE ";
858 if (data_type_ == DATA_TYPE_ASCII) {
859 command += "A";
860 } else if (data_type_ == DATA_TYPE_IMAGE) {
861 command += "I";
862 } else {
863 NOTREACHED();
864 return Stop(ERR_UNEXPECTED);
865 }
866 next_state_ = STATE_CTRL_READ;
867 return SendFtpCommand(command, COMMAND_TYPE);
868 }
869
ProcessResponseTYPE(const FtpCtrlResponse & response)870 int FtpNetworkTransaction::ProcessResponseTYPE(
871 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 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
877 break;
878 case ERROR_CLASS_INFO_NEEDED:
879 return Stop(ERR_INVALID_RESPONSE);
880 case ERROR_CLASS_TRANSIENT_ERROR:
881 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
882 case ERROR_CLASS_PERMANENT_ERROR:
883 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
884 default:
885 NOTREACHED();
886 return Stop(ERR_UNEXPECTED);
887 }
888 return OK;
889 }
890
891 // EPSV command
DoCtrlWriteEPSV()892 int FtpNetworkTransaction::DoCtrlWriteEPSV() {
893 const std::string command = "EPSV";
894 next_state_ = STATE_CTRL_READ;
895 return SendFtpCommand(command, COMMAND_EPSV);
896 }
897
ProcessResponseEPSV(const FtpCtrlResponse & response)898 int FtpNetworkTransaction::ProcessResponseEPSV(
899 const FtpCtrlResponse& response) {
900 switch (GetErrorClass(response.status_code)) {
901 case ERROR_CLASS_INITIATED:
902 return Stop(ERR_INVALID_RESPONSE);
903 case ERROR_CLASS_OK:
904 if (!ExtractPortFromEPSVResponse( response, &data_connection_port_))
905 return Stop(ERR_INVALID_RESPONSE);
906 if (data_connection_port_ < 1024 ||
907 !IsPortAllowedByFtp(data_connection_port_))
908 return Stop(ERR_UNSAFE_PORT);
909 next_state_ = STATE_DATA_CONNECT;
910 break;
911 case ERROR_CLASS_INFO_NEEDED:
912 return Stop(ERR_INVALID_RESPONSE);
913 case ERROR_CLASS_TRANSIENT_ERROR:
914 case ERROR_CLASS_PERMANENT_ERROR:
915 use_epsv_ = false;
916 next_state_ = STATE_CTRL_WRITE_PASV;
917 return OK;
918 default:
919 NOTREACHED();
920 return Stop(ERR_UNEXPECTED);
921 }
922 return OK;
923 }
924
925 // PASV command
DoCtrlWritePASV()926 int FtpNetworkTransaction::DoCtrlWritePASV() {
927 std::string command = "PASV";
928 next_state_ = STATE_CTRL_READ;
929 return SendFtpCommand(command, COMMAND_PASV);
930 }
931
ProcessResponsePASV(const FtpCtrlResponse & response)932 int FtpNetworkTransaction::ProcessResponsePASV(
933 const FtpCtrlResponse& response) {
934 switch (GetErrorClass(response.status_code)) {
935 case ERROR_CLASS_INITIATED:
936 return Stop(ERR_INVALID_RESPONSE);
937 case ERROR_CLASS_OK:
938 if (!ExtractPortFromPASVResponse(response, &data_connection_port_))
939 return Stop(ERR_INVALID_RESPONSE);
940 if (data_connection_port_ < 1024 ||
941 !IsPortAllowedByFtp(data_connection_port_))
942 return Stop(ERR_UNSAFE_PORT);
943 next_state_ = STATE_DATA_CONNECT;
944 break;
945 case ERROR_CLASS_INFO_NEEDED:
946 return Stop(ERR_INVALID_RESPONSE);
947 case ERROR_CLASS_TRANSIENT_ERROR:
948 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
949 case ERROR_CLASS_PERMANENT_ERROR:
950 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
951 default:
952 NOTREACHED();
953 return Stop(ERR_UNEXPECTED);
954 }
955 return OK;
956 }
957
958 // RETR command
DoCtrlWriteRETR()959 int FtpNetworkTransaction::DoCtrlWriteRETR() {
960 std::string command = "RETR " + GetRequestPathForFtpCommand(false);
961 next_state_ = STATE_CTRL_READ;
962 return SendFtpCommand(command, COMMAND_RETR);
963 }
964
ProcessResponseRETR(const FtpCtrlResponse & response)965 int FtpNetworkTransaction::ProcessResponseRETR(
966 const FtpCtrlResponse& response) {
967 switch (GetErrorClass(response.status_code)) {
968 case ERROR_CLASS_INITIATED:
969 // We want the client to start reading the response at this point.
970 // It got here either through Start or RestartWithAuth. We want that
971 // method to complete. Not setting next state here will make DoLoop exit
972 // and in turn make Start/RestartWithAuth complete.
973 resource_type_ = RESOURCE_TYPE_FILE;
974 break;
975 case ERROR_CLASS_OK:
976 resource_type_ = RESOURCE_TYPE_FILE;
977 next_state_ = STATE_CTRL_WRITE_QUIT;
978 break;
979 case ERROR_CLASS_INFO_NEEDED:
980 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
981 case ERROR_CLASS_TRANSIENT_ERROR:
982 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
983 case ERROR_CLASS_PERMANENT_ERROR:
984 // Code 550 means "Failed to open file". Other codes are unrelated,
985 // like "Not logged in" etc.
986 if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE)
987 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
988
989 // It's possible that RETR failed because the path is a directory.
990 resource_type_ = RESOURCE_TYPE_DIRECTORY;
991
992 // We're going to try CWD next, but first send a PASV one more time,
993 // because some FTP servers, including FileZilla, require that.
994 // See http://crbug.com/25316.
995 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
996 break;
997 default:
998 NOTREACHED();
999 return Stop(ERR_UNEXPECTED);
1000 }
1001
1002 // We should be sure about our resource type now. Otherwise we risk
1003 // an infinite loop (RETR can later send CWD, and CWD can later send RETR).
1004 DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_);
1005
1006 return OK;
1007 }
1008
1009 // SIZE command
DoCtrlWriteSIZE()1010 int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1011 std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
1012 next_state_ = STATE_CTRL_READ;
1013 return SendFtpCommand(command, COMMAND_SIZE);
1014 }
1015
ProcessResponseSIZE(const FtpCtrlResponse & response)1016 int FtpNetworkTransaction::ProcessResponseSIZE(
1017 const FtpCtrlResponse& response) {
1018 switch (GetErrorClass(response.status_code)) {
1019 case ERROR_CLASS_INITIATED:
1020 break;
1021 case ERROR_CLASS_OK:
1022 if (response.lines.size() != 1)
1023 return Stop(ERR_INVALID_RESPONSE);
1024 int64 size;
1025 if (!base::StringToInt64(response.lines[0], &size))
1026 return Stop(ERR_INVALID_RESPONSE);
1027 if (size < 0)
1028 return Stop(ERR_INVALID_RESPONSE);
1029
1030 // A successful response to SIZE does not mean the resource is a file.
1031 // Some FTP servers (for example, the qnx one) send a SIZE even for
1032 // directories.
1033 response_.expected_content_size = size;
1034 break;
1035 case ERROR_CLASS_INFO_NEEDED:
1036 break;
1037 case ERROR_CLASS_TRANSIENT_ERROR:
1038 break;
1039 case ERROR_CLASS_PERMANENT_ERROR:
1040 // It's possible that SIZE failed because the path is a directory.
1041 if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
1042 response.status_code != 550) {
1043 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1044 }
1045 break;
1046 default:
1047 NOTREACHED();
1048 return Stop(ERR_UNEXPECTED);
1049 }
1050
1051 if (resource_type_ == RESOURCE_TYPE_FILE)
1052 next_state_ = STATE_CTRL_WRITE_RETR;
1053 else
1054 next_state_ = STATE_CTRL_WRITE_CWD;
1055
1056 return OK;
1057 }
1058
1059 // CWD command
DoCtrlWriteCWD()1060 int FtpNetworkTransaction::DoCtrlWriteCWD() {
1061 std::string command = "CWD " + GetRequestPathForFtpCommand(true);
1062 next_state_ = STATE_CTRL_READ;
1063 return SendFtpCommand(command, COMMAND_CWD);
1064 }
1065
ProcessResponseCWD(const FtpCtrlResponse & response)1066 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
1067 // We should never issue CWD if we know the target resource is a file.
1068 DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
1069
1070 switch (GetErrorClass(response.status_code)) {
1071 case ERROR_CLASS_INITIATED:
1072 return Stop(ERR_INVALID_RESPONSE);
1073 case ERROR_CLASS_OK:
1074 next_state_ = STATE_CTRL_WRITE_LIST;
1075 break;
1076 case ERROR_CLASS_INFO_NEEDED:
1077 return Stop(ERR_INVALID_RESPONSE);
1078 case ERROR_CLASS_TRANSIENT_ERROR:
1079 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1080 case ERROR_CLASS_PERMANENT_ERROR:
1081 if (response.status_code == 550) {
1082 if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
1083 // We're assuming that the resource is a directory, but the server
1084 // says it's not true. The most probable interpretation is that it
1085 // doesn't exist (with FTP we can't be sure).
1086 return Stop(ERR_FILE_NOT_FOUND);
1087 }
1088
1089 // We are here because SIZE failed and we are not sure what the resource
1090 // type is. It could still be file, and SIZE could fail because of
1091 // an access error (http://crbug.com/56734). Try RETR just to be sure.
1092 resource_type_ = RESOURCE_TYPE_FILE;
1093 next_state_ = STATE_CTRL_WRITE_RETR;
1094 return OK;
1095 }
1096
1097 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1098 default:
1099 NOTREACHED();
1100 return Stop(ERR_UNEXPECTED);
1101 }
1102 return OK;
1103 }
1104
1105 // LIST command
DoCtrlWriteLIST()1106 int FtpNetworkTransaction::DoCtrlWriteLIST() {
1107 std::string command(system_type_ == SYSTEM_TYPE_VMS ? "LIST *.*;0" : "LIST");
1108 next_state_ = STATE_CTRL_READ;
1109 return SendFtpCommand(command, COMMAND_LIST);
1110 }
1111
ProcessResponseLIST(const FtpCtrlResponse & response)1112 int FtpNetworkTransaction::ProcessResponseLIST(
1113 const FtpCtrlResponse& response) {
1114 switch (GetErrorClass(response.status_code)) {
1115 case ERROR_CLASS_INITIATED:
1116 // We want the client to start reading the response at this point.
1117 // It got here either through Start or RestartWithAuth. We want that
1118 // method to complete. Not setting next state here will make DoLoop exit
1119 // and in turn make Start/RestartWithAuth complete.
1120 response_.is_directory_listing = true;
1121 break;
1122 case ERROR_CLASS_OK:
1123 response_.is_directory_listing = true;
1124 next_state_ = STATE_CTRL_WRITE_QUIT;
1125 break;
1126 case ERROR_CLASS_INFO_NEEDED:
1127 return Stop(ERR_INVALID_RESPONSE);
1128 case ERROR_CLASS_TRANSIENT_ERROR:
1129 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1130 case ERROR_CLASS_PERMANENT_ERROR:
1131 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1132 default:
1133 NOTREACHED();
1134 return Stop(ERR_UNEXPECTED);
1135 }
1136 return OK;
1137 }
1138
1139 // QUIT command
DoCtrlWriteQUIT()1140 int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1141 std::string command = "QUIT";
1142 next_state_ = STATE_CTRL_READ;
1143 return SendFtpCommand(command, COMMAND_QUIT);
1144 }
1145
ProcessResponseQUIT(const FtpCtrlResponse & response)1146 int FtpNetworkTransaction::ProcessResponseQUIT(
1147 const FtpCtrlResponse& response) {
1148 ctrl_socket_->Disconnect();
1149 return last_error_;
1150 }
1151
1152 // Data Connection
1153
DoDataConnect()1154 int FtpNetworkTransaction::DoDataConnect() {
1155 next_state_ = STATE_DATA_CONNECT_COMPLETE;
1156 AddressList data_address;
1157 // Connect to the same host as the control socket to prevent PASV port
1158 // scanning attacks.
1159 int rv = ctrl_socket_->GetPeerAddress(&data_address);
1160 if (rv != OK)
1161 return Stop(rv);
1162 data_address.SetPort(data_connection_port_);
1163 data_socket_.reset(socket_factory_->CreateTransportClientSocket(
1164 data_address, net_log_.net_log(), net_log_.source()));
1165 return data_socket_->Connect(&io_callback_);
1166 }
1167
DoDataConnectComplete(int result)1168 int FtpNetworkTransaction::DoDataConnectComplete(int result) {
1169 if (result != OK && use_epsv_) {
1170 // It's possible we hit a broken server, sadly. They can break in different
1171 // ways. Some time out, some reset a connection. Fall back to PASV.
1172 // TODO(phajdan.jr): remember it for future transactions with this server.
1173 // TODO(phajdan.jr): write a test for this code path.
1174 use_epsv_ = false;
1175 next_state_ = STATE_CTRL_WRITE_PASV;
1176 return OK;
1177 }
1178
1179 // Only record the connection error after we've applied all our fallbacks.
1180 // We want to capture the final error, one we're not going to recover from.
1181 RecordDataConnectionError(result);
1182
1183 if (result != OK)
1184 return Stop(result);
1185
1186 next_state_ = STATE_CTRL_WRITE_SIZE;
1187 return OK;
1188 }
1189
DoDataRead()1190 int FtpNetworkTransaction::DoDataRead() {
1191 DCHECK(read_data_buf_);
1192 DCHECK_GT(read_data_buf_len_, 0);
1193
1194 if (data_socket_ == NULL || !data_socket_->IsConnected()) {
1195 // If we don't destroy the data socket completely, some servers will wait
1196 // for us (http://crbug.com/21127). The half-closed TCP connection needs
1197 // to be closed on our side too.
1198 data_socket_.reset();
1199
1200 if (ctrl_socket_->IsConnected()) {
1201 // Wait for the server's response, we should get it before sending QUIT.
1202 next_state_ = STATE_CTRL_READ;
1203 return OK;
1204 }
1205
1206 // We are no longer connected to the server, so just finish the transaction.
1207 return Stop(OK);
1208 }
1209
1210 next_state_ = STATE_DATA_READ_COMPLETE;
1211 read_data_buf_->data()[0] = 0;
1212 return data_socket_->Read(read_data_buf_, read_data_buf_len_,
1213 &io_callback_);
1214 }
1215
DoDataReadComplete(int result)1216 int FtpNetworkTransaction::DoDataReadComplete(int result) {
1217 return result;
1218 }
1219
1220 // We're using a histogram as a group of counters, with one bucket for each
1221 // enumeration value. We're only interested in the values of the counters.
1222 // Ignore the shape, average, and standard deviation of the histograms because
1223 // they are meaningless.
1224 //
1225 // We use two histograms. In the first histogram we tally whether the user has
1226 // seen an error of that type during the session. In the second histogram we
1227 // tally the total number of times the users sees each errer.
RecordDataConnectionError(int result)1228 void FtpNetworkTransaction::RecordDataConnectionError(int result) {
1229 // Gather data for http://crbug.com/3073. See how many users have trouble
1230 // establishing FTP data connection in passive FTP mode.
1231 enum {
1232 // Data connection successful.
1233 NET_ERROR_OK = 0,
1234
1235 // Local firewall blocked the connection.
1236 NET_ERROR_ACCESS_DENIED = 1,
1237
1238 // Connection timed out.
1239 NET_ERROR_TIMED_OUT = 2,
1240
1241 // Connection has been estabilished, but then got broken (either reset
1242 // or aborted).
1243 NET_ERROR_CONNECTION_BROKEN = 3,
1244
1245 // Connection has been refused.
1246 NET_ERROR_CONNECTION_REFUSED = 4,
1247
1248 // No connection to the internet.
1249 NET_ERROR_INTERNET_DISCONNECTED = 5,
1250
1251 // Could not reach the destination address.
1252 NET_ERROR_ADDRESS_UNREACHABLE = 6,
1253
1254 // A programming error in our network stack.
1255 NET_ERROR_UNEXPECTED = 7,
1256
1257 // Other kind of error.
1258 NET_ERROR_OTHER = 20,
1259
1260 NUM_OF_NET_ERROR_TYPES
1261 } type;
1262 switch (result) {
1263 case OK:
1264 type = NET_ERROR_OK;
1265 break;
1266 case ERR_ACCESS_DENIED:
1267 case ERR_NETWORK_ACCESS_DENIED:
1268 type = NET_ERROR_ACCESS_DENIED;
1269 break;
1270 case ERR_TIMED_OUT:
1271 type = NET_ERROR_TIMED_OUT;
1272 break;
1273 case ERR_CONNECTION_ABORTED:
1274 case ERR_CONNECTION_RESET:
1275 case ERR_CONNECTION_CLOSED:
1276 type = NET_ERROR_CONNECTION_BROKEN;
1277 break;
1278 case ERR_CONNECTION_FAILED:
1279 case ERR_CONNECTION_REFUSED:
1280 type = NET_ERROR_CONNECTION_REFUSED;
1281 break;
1282 case ERR_INTERNET_DISCONNECTED:
1283 type = NET_ERROR_INTERNET_DISCONNECTED;
1284 break;
1285 case ERR_ADDRESS_INVALID:
1286 case ERR_ADDRESS_UNREACHABLE:
1287 type = NET_ERROR_ADDRESS_UNREACHABLE;
1288 break;
1289 case ERR_UNEXPECTED:
1290 type = NET_ERROR_UNEXPECTED;
1291 break;
1292 default:
1293 type = NET_ERROR_OTHER;
1294 break;
1295 };
1296 static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
1297
1298 DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
1299 if (!had_error_type[type]) {
1300 had_error_type[type] = true;
1301 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1302 type, NUM_OF_NET_ERROR_TYPES);
1303 }
1304 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1305 type, NUM_OF_NET_ERROR_TYPES);
1306 }
1307
1308 } // namespace net
1309