• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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