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