ftp_network_transaction.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
11320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Copyright (c) 2012 The Chromium Authors. All rights reserved. 21320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Use of this source code is governed by a BSD-style license that can be 31320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// found in the LICENSE file. 41320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 51320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/ftp/ftp_network_transaction.h" 61320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/bind.h" 81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/bind_helpers.h" 91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/compiler_specific.h" 101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/metrics/histogram.h" 111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/string_number_conversions.h" 121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/string_util.h" 131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/strings/string_split.h" 141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/utf_string_conversions.h" 151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/values.h" 161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/address_list.h" 171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/connection_type_histograms.h" 181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/escape.h" 191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/net_errors.h" 201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/net_log.h" 211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/net_util.h" 221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/ftp/ftp_network_session.h" 231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/ftp/ftp_request_info.h" 241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/ftp/ftp_util.h" 251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/socket/client_socket_factory.h" 261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/socket/stream_socket.h" 271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst char kCRLF[] = "\r\n"; 291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst int kCtrlBufLen = 1024; 311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccinamespace { 331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Returns true if |input| can be safely used as a part of FTP command. 351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool IsValidFTPCommandString(const std::string& input) { 361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII 371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // characters in the command if the request path contains them. To be 381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // compatible, we do the same and allow non-ASCII characters in a command. 391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Protect agains newline injection attack. 411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (input.find_first_of("\r\n") != std::string::npos) 421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return true; 451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccienum ErrorClass { 481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // The requested action was initiated. The client should expect another 491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // reply before issuing the next command. 501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ERROR_CLASS_INITIATED, 511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // The requested action has been successfully completed. 531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ERROR_CLASS_OK, 541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // The command has been accepted, but to complete the operation, more 561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // information must be sent by the client. 571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ERROR_CLASS_INFO_NEEDED, 581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // The command was not accepted and the requested action did not take place. 601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // This condition is temporary, and the client is encouraged to restart the 611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // command sequence. 621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ERROR_CLASS_TRANSIENT_ERROR, 631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // The command was not accepted and the requested action did not take place. 651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // This condition is rather permanent, and the client is discouraged from 661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // repeating the exact request. 671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ERROR_CLASS_PERMANENT_ERROR, 681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}; 691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Returns the error class for given response code. Caller should ensure 711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// that |response_code| is in range 100-599. 721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciErrorClass GetErrorClass(int response_code) { 731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (response_code >= 100 && response_code <= 199) 741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ERROR_CLASS_INITIATED; 751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (response_code >= 200 && response_code <= 299) 771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ERROR_CLASS_OK; 781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (response_code >= 300 && response_code <= 399) 801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ERROR_CLASS_INFO_NEEDED; 811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (response_code >= 400 && response_code <= 499) 831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ERROR_CLASS_TRANSIENT_ERROR; 841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (response_code >= 500 && response_code <= 599) 861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ERROR_CLASS_PERMANENT_ERROR; 871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // We should not be called on invalid error codes. 891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci NOTREACHED() << response_code; 901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ERROR_CLASS_PERMANENT_ERROR; 911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Returns network error code for received FTP |response_code|. 941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint GetNetErrorCodeForFtpResponseCode(int response_code) { 951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci switch (response_code) { 961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case 421: 971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return net::ERR_FTP_SERVICE_UNAVAILABLE; 981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case 426: 991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return net::ERR_FTP_TRANSFER_ABORTED; 1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case 450: 1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return net::ERR_FTP_FILE_BUSY; 1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case 500: 1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case 501: 1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return net::ERR_FTP_SYNTAX_ERROR; 1051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case 502: 1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case 504: 1071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return net::ERR_FTP_COMMAND_NOT_SUPPORTED; 1081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case 503: 1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return net::ERR_FTP_BAD_COMMAND_SEQUENCE; 1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci default: 1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return net::ERR_FTP_FAILED; 1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// From RFC 2428 Section 3: 1161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// The text returned in response to the EPSV command MUST be: 1171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// <some text> (<d><d><d><tcp-port><d>) 1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// <d> is a delimiter character, ideally to be | 1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response, 1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int* port) { 1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (response.lines.size() != 1) 1221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const char* ptr = response.lines[0].c_str(); 1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci while (*ptr && *ptr != '(') 1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ++ptr; 1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!*ptr) 1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci char sep = *(++ptr); 1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep) 1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!isdigit(*(++ptr))) 1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *port = *ptr - '0'; 1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci while (isdigit(*(++ptr))) { 1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *port *= 10; 1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *port += *ptr - '0'; 1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (*ptr != sep) 1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return true; 1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// There are two way we can receive IP address and port. 1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// (127,0,0,1,23,21) IP address and port encapsulated in (). 1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// 127,0,0,1,23,21 IP address and port without (). 1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// 1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// See RFC 959, Section 4.1.2 1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response, 1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int* port) { 1511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (response.lines.size() != 1) 1521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::string line(response.lines[0]); 1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!IsStringASCII(line)) 1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (line.length() < 2) 1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci size_t paren_pos = line.find('('); 1611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (paren_pos == std::string::npos) { 1621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Find the first comma and use it to locate the beginning 1631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // of the response data. 1641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci size_t comma_pos = line.find(','); 1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (comma_pos == std::string::npos) 1661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci size_t space_pos = line.rfind(' ', comma_pos); 1691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (space_pos != std::string::npos) 1701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci line = line.substr(space_pos + 1); 1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else { 1721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Remove the parentheses and use the text inside them. 1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci size_t closing_paren_pos = line.rfind(')'); 1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (closing_paren_pos == std::string::npos) 1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (closing_paren_pos <= paren_pos) 1771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1); 1801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Split the line into comma-separated pieces and extract 1831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // the last two. 1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::vector<std::string> pieces; 1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci base::SplitString(line, ',', &pieces); 1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (pieces.size() != 6) 1871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Ignore the IP address supplied in the response. We are always going 1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // to connect back to the same server to prevent FTP PASV port scanning. 1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int p0, p1; 1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!base::StringToInt(pieces[4], &p0)) 1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!base::StringToInt(pieces[5], &p1)) 1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *port = (p0 << 8) + p1; 1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return true; 1991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} // namespace 2021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccinamespace net { 2041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciFtpNetworkTransaction::FtpNetworkTransaction( 2061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci FtpNetworkSession* session, 2071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ClientSocketFactory* socket_factory) 2081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci : command_sent_(COMMAND_NONE), 2091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ALLOW_THIS_IN_INITIALIZER_LIST( 2101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete, 2111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci base::Unretained(this)))), 2121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci session_(session), 2131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci request_(NULL), 2141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci resolver_(session->host_resolver()), 2151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci read_ctrl_buf_(new IOBuffer(kCtrlBufLen)), 2161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ctrl_response_buffer_(NULL), 2171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci read_data_buf_len_(0), 2181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci last_error_(OK), 2191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci system_type_(SYSTEM_TYPE_UNKNOWN), 2201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Use image (binary) transfer by default. It should always work, 2211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // whereas the ascii transfer may damage binary data. 2221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci data_type_(DATA_TYPE_IMAGE), 2231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci resource_type_(RESOURCE_TYPE_UNKNOWN), 2241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci use_epsv_(true), 2251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci data_connection_port_(0), 2261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci socket_factory_(socket_factory), 2271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci next_state_(STATE_NONE), 2281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci state_after_data_connect_complete_(STATE_CTRL_WRITE_SIZE) { 2291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciFtpNetworkTransaction::~FtpNetworkTransaction() { 2321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::Stop(int error) { 2351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (command_sent_ == COMMAND_QUIT) 2361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return error; 2371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci next_state_ = STATE_CTRL_WRITE_QUIT; 2391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci last_error_ = error; 2401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return OK; 2411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::RestartIgnoringLastError( 2441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const CompletionCallback& callback) { 2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ERR_NOT_IMPLEMENTED; 2461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::Start(const FtpRequestInfo* request_info, 2491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const CompletionCallback& callback, 2501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const BoundNetLog& net_log) { 2511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci net_log_ = net_log; 2521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci request_ = request_info; 2531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_)); 2551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (request_->url.has_username()) { 2571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci string16 username; 2581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci string16 password; 2591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci GetIdentityFromURL(request_->url, &username, &password); 2601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci credentials_.Set(username, password); 2611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else { 2621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci credentials_.Set(ASCIIToUTF16("anonymous"), 2631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ASCIIToUTF16("chrome@example.com")); 2641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DetectTypecode(); 2671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci next_state_ = STATE_CTRL_RESOLVE_HOST; 2691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int rv = DoLoop(OK); 2701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (rv == ERR_IO_PENDING) 2711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci user_callback_ = callback; 2721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return rv; 2731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials, 2761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const CompletionCallback& callback) { 2771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ResetStateForRestart(); 2781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci credentials_ = credentials; 2801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci next_state_ = STATE_CTRL_RESOLVE_HOST; 2821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int rv = DoLoop(OK); 2831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (rv == ERR_IO_PENDING) 2841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci user_callback_ = callback; 2851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return rv; 2861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::Read(IOBuffer* buf, 2891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int buf_len, 2901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const CompletionCallback& callback) { 2911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK(buf); 2921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK_GT(buf_len, 0); 2931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci read_data_buf_ = buf; 2951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci read_data_buf_len_ = buf_len; 2961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci next_state_ = STATE_DATA_READ; 2981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int rv = DoLoop(OK); 2991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (rv == ERR_IO_PENDING) 3001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci user_callback_ = callback; 3011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return rv; 3021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 3031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const { 3051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return &response_; 3061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 3071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciLoadState FtpNetworkTransaction::GetLoadState() const { 3091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE) 3101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return LOAD_STATE_RESOLVING_HOST; 3111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (next_state_ == STATE_CTRL_CONNECT_COMPLETE || 3131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci next_state_ == STATE_DATA_CONNECT_COMPLETE) 3141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return LOAD_STATE_CONNECTING; 3151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (next_state_ == STATE_DATA_READ_COMPLETE) 3171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return LOAD_STATE_READING_RESPONSE; 3181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (command_sent_ == COMMAND_RETR && read_data_buf_.get()) 3201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return LOAD_STATE_READING_RESPONSE; 3211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (command_sent_ == COMMAND_QUIT) 3231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return LOAD_STATE_IDLE; 3241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (command_sent_ != COMMAND_NONE) 3261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return LOAD_STATE_SENDING_REQUEST; 3271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return LOAD_STATE_IDLE; 3291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 3301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciuint64 FtpNetworkTransaction::GetUploadProgress() const { 3321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return 0; 3331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 3341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid FtpNetworkTransaction::ResetStateForRestart() { 3361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci command_sent_ = COMMAND_NONE; 3371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci user_callback_.Reset(); 3381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci response_ = FtpResponseInfo(); 3391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci read_ctrl_buf_ = new IOBuffer(kCtrlBufLen); 3401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_)); 3411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci read_data_buf_ = NULL; 3421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci read_data_buf_len_ = 0; 3431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (write_buf_) 3441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci write_buf_->SetOffset(0); 3451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci last_error_ = OK; 3461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci data_connection_port_ = 0; 3471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ctrl_socket_.reset(); 3481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci data_socket_.reset(); 3491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci next_state_ = STATE_NONE; 3501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci state_after_data_connect_complete_ = STATE_CTRL_WRITE_SIZE; 3511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 3521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid FtpNetworkTransaction::ResetDataConnectionAfterError(State next_state) { 3541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // The server _might_ have reset the data connection 3551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // (see RFC 959 3.2. ESTABLISHING DATA CONNECTIONS: 3561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // "The server MUST close the data connection under the following 3571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // conditions: 3581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // ... 3591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // 5. An irrecoverable error condition occurs.") 3601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // 3611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // It is ambiguous what an irrecoverable error condition is, 3621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // so we take no chances. 3631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci state_after_data_connect_complete_ = next_state; 3641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV; 3651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 3661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid FtpNetworkTransaction::DoCallback(int rv) { 3681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK(rv != ERR_IO_PENDING); 3691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK(!user_callback_.is_null()); 3701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Since Run may result in Read being called, clear callback_ up front. 3721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci CompletionCallback c = user_callback_; 3731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci user_callback_.Reset(); 3741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci c.Run(rv); 3751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 3761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid FtpNetworkTransaction::OnIOComplete(int result) { 3781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int rv = DoLoop(result); 3791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (rv != ERR_IO_PENDING) 3801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DoCallback(rv); 3811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 3821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::ProcessCtrlResponse() { 3841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci FtpCtrlResponse response = ctrl_response_buffer_->PopResponse(); 3851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int rv = OK; 3871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci switch (command_sent_) { 3881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_NONE: 3891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // TODO(phajdan.jr): Check for errors in the welcome message. 3901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci next_state_ = STATE_CTRL_WRITE_USER; 3911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 3921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_USER: 3931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseUSER(response); 3941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 3951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_PASS: 3961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponsePASS(response); 3971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 3981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_SYST: 3991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseSYST(response); 4001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_PWD: 4021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponsePWD(response); 4031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_TYPE: 4051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseTYPE(response); 4061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_EPSV: 4081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseEPSV(response); 4091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_PASV: 4111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponsePASV(response); 4121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_SIZE: 4141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseSIZE(response); 4151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_RETR: 4171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseRETR(response); 4181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_CWD: 4201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseCWD(response); 4211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_LIST: 4231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseLIST(response); 4241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_QUIT: 4261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseQUIT(response); 4271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci default: 4291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_; 4301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ERR_UNEXPECTED; 4311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 4321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // We may get multiple responses for some commands, 4341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // see http://crbug.com/18036. 4351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) { 4361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci response = ctrl_response_buffer_->PopResponse(); 4371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci switch (command_sent_) { 4391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_RETR: 4401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseRETR(response); 4411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case COMMAND_LIST: 4431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rv = ProcessResponseLIST(response); 4441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci default: 4461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Multiple responses for other commands are invalid. 4471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return Stop(ERR_INVALID_RESPONSE); 4481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 4491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 4501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return rv; 4521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 4531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Used to prepare and send FTP command. 4551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::SendFtpCommand(const std::string& command, 4561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const std::string& command_for_log, 4571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Command cmd) { 4581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // If we send a new command when we still have unprocessed responses 4591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // for previous commands, the response receiving code will have no way to know 4601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // which responses are for which command. 4611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK(!ctrl_response_buffer_->ResponseAvailable()); 4621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK(!write_command_buf_); 4641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK(!write_buf_); 4651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!IsValidFTPCommandString(command)) { 4671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Callers should validate the command themselves and return a more specific 4681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // error code. 4691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci NOTREACHED(); 4701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return Stop(ERR_UNEXPECTED); 4711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 4721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci command_sent_ = cmd; 4741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci write_command_buf_ = new IOBufferWithSize(command.length() + 2); 4761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci write_buf_ = new DrainableIOBuffer(write_command_buf_, 4771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci write_command_buf_->size()); 4781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci memcpy(write_command_buf_->data(), command.data(), command.length()); 4791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci memcpy(write_command_buf_->data() + command.length(), kCRLF, 2); 4801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT, 4821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci NetLog::StringCallback("command", &command_for_log)); 4831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci next_state_ = STATE_CTRL_WRITE; 4851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return OK; 4861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 4871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistd::string FtpNetworkTransaction::GetRequestPathForFtpCommand( 4891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bool is_directory) const { 4901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::string path(current_remote_directory_); 4911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (request_->url.has_path()) { 4921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::string gurl_path(request_->url.path()); 4931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path. 4951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::string::size_type pos = gurl_path.rfind(';'); 4961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (pos != std::string::npos) 4971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gurl_path.resize(pos); 4981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci path.append(gurl_path); 5001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 5011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Make sure that if the path is expected to be a file, it won't end 5021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // with a trailing slash. 5031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/') 5041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci path.erase(path.length() - 1); 5051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci UnescapeRule::Type unescape_rules = UnescapeRule::SPACES | 5061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci UnescapeRule::URL_SPECIAL_CHARS; 5071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // This may unescape to non-ASCII characters, but we allow that. See the 5081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // comment for IsValidFTPCommandString. 5091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci path = net::UnescapeURLComponent(path, unescape_rules); 5101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 5111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (system_type_ == SYSTEM_TYPE_VMS) { 5121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (is_directory) 5131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci path = FtpUtil::UnixDirectoryPathToVMS(path); 514 else 515 path = FtpUtil::UnixFilePathToVMS(path); 516 } 517 518 DCHECK(IsValidFTPCommandString(path)); 519 return path; 520} 521 522void FtpNetworkTransaction::DetectTypecode() { 523 if (!request_->url.has_path()) 524 return; 525 std::string gurl_path(request_->url.path()); 526 527 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path. 528 std::string::size_type pos = gurl_path.rfind(';'); 529 if (pos == std::string::npos) 530 return; 531 std::string typecode_string(gurl_path.substr(pos)); 532 if (typecode_string == ";type=a") { 533 data_type_ = DATA_TYPE_ASCII; 534 resource_type_ = RESOURCE_TYPE_FILE; 535 } else if (typecode_string == ";type=i") { 536 data_type_ = DATA_TYPE_IMAGE; 537 resource_type_ = RESOURCE_TYPE_FILE; 538 } else if (typecode_string == ";type=d") { 539 resource_type_ = RESOURCE_TYPE_DIRECTORY; 540 } 541} 542 543int FtpNetworkTransaction::DoLoop(int result) { 544 DCHECK(next_state_ != STATE_NONE); 545 546 int rv = result; 547 do { 548 State state = next_state_; 549 next_state_ = STATE_NONE; 550 switch (state) { 551 case STATE_CTRL_RESOLVE_HOST: 552 DCHECK(rv == OK); 553 rv = DoCtrlResolveHost(); 554 break; 555 case STATE_CTRL_RESOLVE_HOST_COMPLETE: 556 rv = DoCtrlResolveHostComplete(rv); 557 break; 558 case STATE_CTRL_CONNECT: 559 DCHECK(rv == OK); 560 rv = DoCtrlConnect(); 561 break; 562 case STATE_CTRL_CONNECT_COMPLETE: 563 rv = DoCtrlConnectComplete(rv); 564 break; 565 case STATE_CTRL_READ: 566 DCHECK(rv == OK); 567 rv = DoCtrlRead(); 568 break; 569 case STATE_CTRL_READ_COMPLETE: 570 rv = DoCtrlReadComplete(rv); 571 break; 572 case STATE_CTRL_WRITE: 573 DCHECK(rv == OK); 574 rv = DoCtrlWrite(); 575 break; 576 case STATE_CTRL_WRITE_COMPLETE: 577 rv = DoCtrlWriteComplete(rv); 578 break; 579 case STATE_CTRL_WRITE_USER: 580 DCHECK(rv == OK); 581 rv = DoCtrlWriteUSER(); 582 break; 583 case STATE_CTRL_WRITE_PASS: 584 DCHECK(rv == OK); 585 rv = DoCtrlWritePASS(); 586 break; 587 case STATE_CTRL_WRITE_SYST: 588 DCHECK(rv == OK); 589 rv = DoCtrlWriteSYST(); 590 break; 591 case STATE_CTRL_WRITE_PWD: 592 DCHECK(rv == OK); 593 rv = DoCtrlWritePWD(); 594 break; 595 case STATE_CTRL_WRITE_TYPE: 596 DCHECK(rv == OK); 597 rv = DoCtrlWriteTYPE(); 598 break; 599 case STATE_CTRL_WRITE_EPSV: 600 DCHECK(rv == OK); 601 rv = DoCtrlWriteEPSV(); 602 break; 603 case STATE_CTRL_WRITE_PASV: 604 DCHECK(rv == OK); 605 rv = DoCtrlWritePASV(); 606 break; 607 case STATE_CTRL_WRITE_RETR: 608 DCHECK(rv == OK); 609 rv = DoCtrlWriteRETR(); 610 break; 611 case STATE_CTRL_WRITE_SIZE: 612 DCHECK(rv == OK); 613 rv = DoCtrlWriteSIZE(); 614 break; 615 case STATE_CTRL_WRITE_CWD: 616 DCHECK(rv == OK); 617 rv = DoCtrlWriteCWD(); 618 break; 619 case STATE_CTRL_WRITE_LIST: 620 DCHECK(rv == OK); 621 rv = DoCtrlWriteLIST(); 622 break; 623 case STATE_CTRL_WRITE_QUIT: 624 DCHECK(rv == OK); 625 rv = DoCtrlWriteQUIT(); 626 break; 627 case STATE_DATA_CONNECT: 628 DCHECK(rv == OK); 629 rv = DoDataConnect(); 630 break; 631 case STATE_DATA_CONNECT_COMPLETE: 632 rv = DoDataConnectComplete(rv); 633 break; 634 case STATE_DATA_READ: 635 DCHECK(rv == OK); 636 rv = DoDataRead(); 637 break; 638 case STATE_DATA_READ_COMPLETE: 639 rv = DoDataReadComplete(rv); 640 break; 641 default: 642 NOTREACHED() << "bad state"; 643 rv = ERR_UNEXPECTED; 644 break; 645 } 646 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); 647 return rv; 648} 649 650int FtpNetworkTransaction::DoCtrlResolveHost() { 651 next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE; 652 653 HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url)); 654 // No known referrer. 655 return resolver_.Resolve( 656 info, &addresses_, 657 base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)), 658 net_log_); 659} 660 661int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) { 662 if (result == OK) 663 next_state_ = STATE_CTRL_CONNECT; 664 return result; 665} 666 667int FtpNetworkTransaction::DoCtrlConnect() { 668 next_state_ = STATE_CTRL_CONNECT_COMPLETE; 669 ctrl_socket_.reset(socket_factory_->CreateTransportClientSocket( 670 addresses_, net_log_.net_log(), net_log_.source())); 671 net_log_.AddEvent( 672 NetLog::TYPE_FTP_CONTROL_CONNECTION, 673 ctrl_socket_->NetLog().source().ToEventParametersCallback()); 674 return ctrl_socket_->Connect(io_callback_); 675} 676 677int FtpNetworkTransaction::DoCtrlConnectComplete(int result) { 678 if (result == OK) { 679 // Put the peer's IP address and port into the response. 680 IPEndPoint ip_endpoint; 681 result = ctrl_socket_->GetPeerAddress(&ip_endpoint); 682 if (result == OK) { 683 response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint); 684 next_state_ = STATE_CTRL_READ; 685 686 if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) { 687 // Do not use EPSV for IPv4 connections. Some servers become confused 688 // and we time out while waiting to connect. PASV is perfectly fine for 689 // IPv4. Note that this blacklists IPv4 not to use EPSV instead of 690 // whitelisting IPv6 to use it, to make the code more future-proof: 691 // all future protocols should just use EPSV. 692 use_epsv_ = false; 693 } 694 } 695 } 696 return result; 697} 698 699int FtpNetworkTransaction::DoCtrlRead() { 700 next_state_ = STATE_CTRL_READ_COMPLETE; 701 return ctrl_socket_->Read(read_ctrl_buf_, kCtrlBufLen, io_callback_); 702} 703 704int FtpNetworkTransaction::DoCtrlReadComplete(int result) { 705 if (result == 0) { 706 // Some servers (for example Pure-FTPd) apparently close the control 707 // connection when anonymous login is not permitted. For more details 708 // see http://crbug.com/25023. 709 if (command_sent_ == COMMAND_USER && 710 credentials_.username() == ASCIIToUTF16("anonymous")) { 711 response_.needs_auth = true; 712 } 713 return Stop(ERR_EMPTY_RESPONSE); 714 } 715 if (result < 0) 716 return Stop(result); 717 718 ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result); 719 720 if (!ctrl_response_buffer_->ResponseAvailable()) { 721 // Read more data from the control socket. 722 next_state_ = STATE_CTRL_READ; 723 return OK; 724 } 725 726 return ProcessCtrlResponse(); 727} 728 729int FtpNetworkTransaction::DoCtrlWrite() { 730 next_state_ = STATE_CTRL_WRITE_COMPLETE; 731 732 return ctrl_socket_->Write(write_buf_, 733 write_buf_->BytesRemaining(), 734 io_callback_); 735} 736 737int FtpNetworkTransaction::DoCtrlWriteComplete(int result) { 738 if (result < 0) 739 return result; 740 741 write_buf_->DidConsume(result); 742 if (write_buf_->BytesRemaining() == 0) { 743 // Clear the write buffer. 744 write_buf_ = NULL; 745 write_command_buf_ = NULL; 746 747 next_state_ = STATE_CTRL_READ; 748 } else { 749 next_state_ = STATE_CTRL_WRITE; 750 } 751 return OK; 752} 753 754// FTP Commands and responses 755 756// USER Command. 757int FtpNetworkTransaction::DoCtrlWriteUSER() { 758 std::string command = "USER " + UTF16ToUTF8(credentials_.username()); 759 760 if (!IsValidFTPCommandString(command)) 761 return Stop(ERR_MALFORMED_IDENTITY); 762 763 next_state_ = STATE_CTRL_READ; 764 return SendFtpCommand(command, "USER ***", COMMAND_USER); 765} 766 767int FtpNetworkTransaction::ProcessResponseUSER( 768 const FtpCtrlResponse& response) { 769 switch (GetErrorClass(response.status_code)) { 770 case ERROR_CLASS_OK: 771 next_state_ = STATE_CTRL_WRITE_SYST; 772 break; 773 case ERROR_CLASS_INFO_NEEDED: 774 next_state_ = STATE_CTRL_WRITE_PASS; 775 break; 776 case ERROR_CLASS_TRANSIENT_ERROR: 777 case ERROR_CLASS_PERMANENT_ERROR: 778 response_.needs_auth = true; 779 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 780 default: 781 NOTREACHED(); 782 return Stop(ERR_UNEXPECTED); 783 } 784 return OK; 785} 786 787// PASS command. 788int FtpNetworkTransaction::DoCtrlWritePASS() { 789 std::string command = "PASS " + UTF16ToUTF8(credentials_.password()); 790 791 if (!IsValidFTPCommandString(command)) 792 return Stop(ERR_MALFORMED_IDENTITY); 793 794 next_state_ = STATE_CTRL_READ; 795 return SendFtpCommand(command, "PASS ***", COMMAND_PASS); 796} 797 798int FtpNetworkTransaction::ProcessResponsePASS( 799 const FtpCtrlResponse& response) { 800 switch (GetErrorClass(response.status_code)) { 801 case ERROR_CLASS_OK: 802 next_state_ = STATE_CTRL_WRITE_SYST; 803 break; 804 case ERROR_CLASS_INFO_NEEDED: 805 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 806 case ERROR_CLASS_TRANSIENT_ERROR: 807 case ERROR_CLASS_PERMANENT_ERROR: 808 response_.needs_auth = true; 809 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 810 default: 811 NOTREACHED(); 812 return Stop(ERR_UNEXPECTED); 813 } 814 return OK; 815} 816 817// SYST command. 818int FtpNetworkTransaction::DoCtrlWriteSYST() { 819 std::string command = "SYST"; 820 next_state_ = STATE_CTRL_READ; 821 return SendFtpCommand(command, command, COMMAND_SYST); 822} 823 824int FtpNetworkTransaction::ProcessResponseSYST( 825 const FtpCtrlResponse& response) { 826 switch (GetErrorClass(response.status_code)) { 827 case ERROR_CLASS_INITIATED: 828 return Stop(ERR_INVALID_RESPONSE); 829 case ERROR_CLASS_OK: { 830 // All important info should be on the first line. 831 std::string line = response.lines[0]; 832 // The response should be ASCII, which allows us to do case-insensitive 833 // comparisons easily. If it is not ASCII, we leave the system type 834 // as unknown. 835 if (IsStringASCII(line)) { 836 line = StringToLowerASCII(line); 837 838 // Remove all whitespace, to correctly handle cases like fancy "V M S" 839 // response instead of "VMS". 840 RemoveChars(line, kWhitespaceASCII, &line); 841 842 // The "magic" strings we test for below have been gathered by an 843 // empirical study. VMS needs to come first because some VMS systems 844 // also respond with "UNIX emulation", which is not perfect. It is much 845 // more reliable to talk to these servers in their native language. 846 if (line.find("vms") != std::string::npos) { 847 system_type_ = SYSTEM_TYPE_VMS; 848 } else if (line.find("l8") != std::string::npos || 849 line.find("unix") != std::string::npos || 850 line.find("bsd") != std::string::npos) { 851 system_type_ = SYSTEM_TYPE_UNIX; 852 } else if (line.find("win32") != std::string::npos || 853 line.find("windows") != std::string::npos) { 854 system_type_ = SYSTEM_TYPE_WINDOWS; 855 } else if (line.find("os/2") != std::string::npos) { 856 system_type_ = SYSTEM_TYPE_OS2; 857 } 858 } 859 next_state_ = STATE_CTRL_WRITE_PWD; 860 break; 861 } 862 case ERROR_CLASS_INFO_NEEDED: 863 return Stop(ERR_INVALID_RESPONSE); 864 case ERROR_CLASS_TRANSIENT_ERROR: 865 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 866 case ERROR_CLASS_PERMANENT_ERROR: 867 // Server does not recognize the SYST command so proceed. 868 next_state_ = STATE_CTRL_WRITE_PWD; 869 break; 870 default: 871 NOTREACHED(); 872 return Stop(ERR_UNEXPECTED); 873 } 874 return OK; 875} 876 877// PWD command. 878int FtpNetworkTransaction::DoCtrlWritePWD() { 879 std::string command = "PWD"; 880 next_state_ = STATE_CTRL_READ; 881 return SendFtpCommand(command, command, COMMAND_PWD); 882} 883 884int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) { 885 switch (GetErrorClass(response.status_code)) { 886 case ERROR_CLASS_INITIATED: 887 return Stop(ERR_INVALID_RESPONSE); 888 case ERROR_CLASS_OK: { 889 // The info we look for should be on the first line. 890 std::string line = response.lines[0]; 891 if (line.empty()) 892 return Stop(ERR_INVALID_RESPONSE); 893 std::string::size_type quote_pos = line.find('"'); 894 if (quote_pos != std::string::npos) { 895 line = line.substr(quote_pos + 1); 896 quote_pos = line.find('"'); 897 if (quote_pos == std::string::npos) 898 return Stop(ERR_INVALID_RESPONSE); 899 line = line.substr(0, quote_pos); 900 } 901 if (system_type_ == SYSTEM_TYPE_VMS) 902 line = FtpUtil::VMSPathToUnix(line); 903 if (line.length() && line[line.length() - 1] == '/') 904 line.erase(line.length() - 1); 905 current_remote_directory_ = line; 906 next_state_ = STATE_CTRL_WRITE_TYPE; 907 break; 908 } 909 case ERROR_CLASS_INFO_NEEDED: 910 return Stop(ERR_INVALID_RESPONSE); 911 case ERROR_CLASS_TRANSIENT_ERROR: 912 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 913 case ERROR_CLASS_PERMANENT_ERROR: 914 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 915 default: 916 NOTREACHED(); 917 return Stop(ERR_UNEXPECTED); 918 } 919 return OK; 920} 921 922// TYPE command. 923int FtpNetworkTransaction::DoCtrlWriteTYPE() { 924 std::string command = "TYPE "; 925 if (data_type_ == DATA_TYPE_ASCII) { 926 command += "A"; 927 } else if (data_type_ == DATA_TYPE_IMAGE) { 928 command += "I"; 929 } else { 930 NOTREACHED(); 931 return Stop(ERR_UNEXPECTED); 932 } 933 next_state_ = STATE_CTRL_READ; 934 return SendFtpCommand(command, command, COMMAND_TYPE); 935} 936 937int FtpNetworkTransaction::ProcessResponseTYPE( 938 const FtpCtrlResponse& response) { 939 switch (GetErrorClass(response.status_code)) { 940 case ERROR_CLASS_INITIATED: 941 return Stop(ERR_INVALID_RESPONSE); 942 case ERROR_CLASS_OK: 943 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV; 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// EPSV command 959int FtpNetworkTransaction::DoCtrlWriteEPSV() { 960 const std::string command = "EPSV"; 961 next_state_ = STATE_CTRL_READ; 962 return SendFtpCommand(command, command, COMMAND_EPSV); 963} 964 965int FtpNetworkTransaction::ProcessResponseEPSV( 966 const FtpCtrlResponse& response) { 967 switch (GetErrorClass(response.status_code)) { 968 case ERROR_CLASS_INITIATED: 969 return Stop(ERR_INVALID_RESPONSE); 970 case ERROR_CLASS_OK: 971 if (!ExtractPortFromEPSVResponse( response, &data_connection_port_)) 972 return Stop(ERR_INVALID_RESPONSE); 973 if (data_connection_port_ < 1024 || 974 !IsPortAllowedByFtp(data_connection_port_)) 975 return Stop(ERR_UNSAFE_PORT); 976 next_state_ = STATE_DATA_CONNECT; 977 break; 978 case ERROR_CLASS_INFO_NEEDED: 979 return Stop(ERR_INVALID_RESPONSE); 980 case ERROR_CLASS_TRANSIENT_ERROR: 981 case ERROR_CLASS_PERMANENT_ERROR: 982 use_epsv_ = false; 983 next_state_ = STATE_CTRL_WRITE_PASV; 984 return OK; 985 default: 986 NOTREACHED(); 987 return Stop(ERR_UNEXPECTED); 988 } 989 return OK; 990} 991 992// PASV command 993int FtpNetworkTransaction::DoCtrlWritePASV() { 994 std::string command = "PASV"; 995 next_state_ = STATE_CTRL_READ; 996 return SendFtpCommand(command, command, COMMAND_PASV); 997} 998 999int FtpNetworkTransaction::ProcessResponsePASV( 1000 const FtpCtrlResponse& response) { 1001 switch (GetErrorClass(response.status_code)) { 1002 case ERROR_CLASS_INITIATED: 1003 return Stop(ERR_INVALID_RESPONSE); 1004 case ERROR_CLASS_OK: 1005 if (!ExtractPortFromPASVResponse(response, &data_connection_port_)) 1006 return Stop(ERR_INVALID_RESPONSE); 1007 if (data_connection_port_ < 1024 || 1008 !IsPortAllowedByFtp(data_connection_port_)) 1009 return Stop(ERR_UNSAFE_PORT); 1010 next_state_ = STATE_DATA_CONNECT; 1011 break; 1012 case ERROR_CLASS_INFO_NEEDED: 1013 return Stop(ERR_INVALID_RESPONSE); 1014 case ERROR_CLASS_TRANSIENT_ERROR: 1015 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1016 case ERROR_CLASS_PERMANENT_ERROR: 1017 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1018 default: 1019 NOTREACHED(); 1020 return Stop(ERR_UNEXPECTED); 1021 } 1022 return OK; 1023} 1024 1025// RETR command 1026int FtpNetworkTransaction::DoCtrlWriteRETR() { 1027 std::string command = "RETR " + GetRequestPathForFtpCommand(false); 1028 next_state_ = STATE_CTRL_READ; 1029 return SendFtpCommand(command, command, COMMAND_RETR); 1030} 1031 1032int FtpNetworkTransaction::ProcessResponseRETR( 1033 const FtpCtrlResponse& response) { 1034 switch (GetErrorClass(response.status_code)) { 1035 case ERROR_CLASS_INITIATED: 1036 // We want the client to start reading the response at this point. 1037 // It got here either through Start or RestartWithAuth. We want that 1038 // method to complete. Not setting next state here will make DoLoop exit 1039 // and in turn make Start/RestartWithAuth complete. 1040 resource_type_ = RESOURCE_TYPE_FILE; 1041 break; 1042 case ERROR_CLASS_OK: 1043 resource_type_ = RESOURCE_TYPE_FILE; 1044 next_state_ = STATE_CTRL_WRITE_QUIT; 1045 break; 1046 case ERROR_CLASS_INFO_NEEDED: 1047 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1048 case ERROR_CLASS_TRANSIENT_ERROR: 1049 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1050 case ERROR_CLASS_PERMANENT_ERROR: 1051 // Code 550 means "Failed to open file". Other codes are unrelated, 1052 // like "Not logged in" etc. 1053 if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE) 1054 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1055 1056 // It's possible that RETR failed because the path is a directory. 1057 resource_type_ = RESOURCE_TYPE_DIRECTORY; 1058 1059 // We're going to try CWD next, but first send a PASV one more time, 1060 // because some FTP servers, including FileZilla, require that. 1061 // See http://crbug.com/25316. 1062 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV; 1063 break; 1064 default: 1065 NOTREACHED(); 1066 return Stop(ERR_UNEXPECTED); 1067 } 1068 1069 // We should be sure about our resource type now. Otherwise we risk 1070 // an infinite loop (RETR can later send CWD, and CWD can later send RETR). 1071 DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_); 1072 1073 return OK; 1074} 1075 1076// SIZE command 1077int FtpNetworkTransaction::DoCtrlWriteSIZE() { 1078 std::string command = "SIZE " + GetRequestPathForFtpCommand(false); 1079 next_state_ = STATE_CTRL_READ; 1080 return SendFtpCommand(command, command, COMMAND_SIZE); 1081} 1082 1083int FtpNetworkTransaction::ProcessResponseSIZE( 1084 const FtpCtrlResponse& response) { 1085 State state_after_size; 1086 if (resource_type_ == RESOURCE_TYPE_FILE) 1087 state_after_size = STATE_CTRL_WRITE_RETR; 1088 else 1089 state_after_size = STATE_CTRL_WRITE_CWD; 1090 1091 switch (GetErrorClass(response.status_code)) { 1092 case ERROR_CLASS_INITIATED: 1093 next_state_ = state_after_size; 1094 break; 1095 case ERROR_CLASS_OK: 1096 if (response.lines.size() != 1) 1097 return Stop(ERR_INVALID_RESPONSE); 1098 int64 size; 1099 if (!base::StringToInt64(response.lines[0], &size)) 1100 return Stop(ERR_INVALID_RESPONSE); 1101 if (size < 0) 1102 return Stop(ERR_INVALID_RESPONSE); 1103 1104 // A successful response to SIZE does not mean the resource is a file. 1105 // Some FTP servers (for example, the qnx one) send a SIZE even for 1106 // directories. 1107 response_.expected_content_size = size; 1108 1109 next_state_ = state_after_size; 1110 break; 1111 case ERROR_CLASS_INFO_NEEDED: 1112 next_state_ = state_after_size; 1113 break; 1114 case ERROR_CLASS_TRANSIENT_ERROR: 1115 ResetDataConnectionAfterError(state_after_size); 1116 break; 1117 case ERROR_CLASS_PERMANENT_ERROR: 1118 // It's possible that SIZE failed because the path is a directory. 1119 if (resource_type_ == RESOURCE_TYPE_UNKNOWN && 1120 response.status_code != 550) { 1121 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1122 } 1123 1124 ResetDataConnectionAfterError(state_after_size); 1125 break; 1126 default: 1127 NOTREACHED(); 1128 return Stop(ERR_UNEXPECTED); 1129 } 1130 1131 return OK; 1132} 1133 1134// CWD command 1135int FtpNetworkTransaction::DoCtrlWriteCWD() { 1136 std::string command = "CWD " + GetRequestPathForFtpCommand(true); 1137 next_state_ = STATE_CTRL_READ; 1138 return SendFtpCommand(command, command, COMMAND_CWD); 1139} 1140 1141int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) { 1142 // We should never issue CWD if we know the target resource is a file. 1143 DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_); 1144 1145 switch (GetErrorClass(response.status_code)) { 1146 case ERROR_CLASS_INITIATED: 1147 return Stop(ERR_INVALID_RESPONSE); 1148 case ERROR_CLASS_OK: 1149 next_state_ = STATE_CTRL_WRITE_LIST; 1150 break; 1151 case ERROR_CLASS_INFO_NEEDED: 1152 return Stop(ERR_INVALID_RESPONSE); 1153 case ERROR_CLASS_TRANSIENT_ERROR: 1154 // Some FTP servers send response 451 (not a valid CWD response according 1155 // to RFC 959) instead of 550. 1156 if (response.status_code == 451) 1157 return ProcessResponseCWDNotADirectory(); 1158 1159 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1160 case ERROR_CLASS_PERMANENT_ERROR: 1161 if (response.status_code == 550) 1162 return ProcessResponseCWDNotADirectory(); 1163 1164 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1165 default: 1166 NOTREACHED(); 1167 return Stop(ERR_UNEXPECTED); 1168 } 1169 1170 return OK; 1171} 1172 1173int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() { 1174 if (resource_type_ == RESOURCE_TYPE_DIRECTORY) { 1175 // We're assuming that the resource is a directory, but the server 1176 // says it's not true. The most probable interpretation is that it 1177 // doesn't exist (with FTP we can't be sure). 1178 return Stop(ERR_FILE_NOT_FOUND); 1179 } 1180 1181 // We are here because SIZE failed and we are not sure what the resource 1182 // type is. It could still be file, and SIZE could fail because of 1183 // an access error (http://crbug.com/56734). Try RETR just to be sure. 1184 resource_type_ = RESOURCE_TYPE_FILE; 1185 1186 ResetDataConnectionAfterError(STATE_CTRL_WRITE_RETR); 1187 return OK; 1188} 1189 1190// LIST command 1191int FtpNetworkTransaction::DoCtrlWriteLIST() { 1192 // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option 1193 // forces LIST output instead of NLST (which would be ambiguous for us 1194 // to parse). 1195 std::string command("LIST -l"); 1196 if (system_type_ == SYSTEM_TYPE_VMS) 1197 command = "LIST *.*;0"; 1198 1199 next_state_ = STATE_CTRL_READ; 1200 return SendFtpCommand(command, command, COMMAND_LIST); 1201} 1202 1203int FtpNetworkTransaction::ProcessResponseLIST( 1204 const FtpCtrlResponse& response) { 1205 switch (GetErrorClass(response.status_code)) { 1206 case ERROR_CLASS_INITIATED: 1207 // We want the client to start reading the response at this point. 1208 // It got here either through Start or RestartWithAuth. We want that 1209 // method to complete. Not setting next state here will make DoLoop exit 1210 // and in turn make Start/RestartWithAuth complete. 1211 response_.is_directory_listing = true; 1212 break; 1213 case ERROR_CLASS_OK: 1214 response_.is_directory_listing = true; 1215 next_state_ = STATE_CTRL_WRITE_QUIT; 1216 break; 1217 case ERROR_CLASS_INFO_NEEDED: 1218 return Stop(ERR_INVALID_RESPONSE); 1219 case ERROR_CLASS_TRANSIENT_ERROR: 1220 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1221 case ERROR_CLASS_PERMANENT_ERROR: 1222 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); 1223 default: 1224 NOTREACHED(); 1225 return Stop(ERR_UNEXPECTED); 1226 } 1227 return OK; 1228} 1229 1230// QUIT command 1231int FtpNetworkTransaction::DoCtrlWriteQUIT() { 1232 std::string command = "QUIT"; 1233 next_state_ = STATE_CTRL_READ; 1234 return SendFtpCommand(command, command, COMMAND_QUIT); 1235} 1236 1237int FtpNetworkTransaction::ProcessResponseQUIT( 1238 const FtpCtrlResponse& response) { 1239 ctrl_socket_->Disconnect(); 1240 return last_error_; 1241} 1242 1243// Data Connection 1244 1245int FtpNetworkTransaction::DoDataConnect() { 1246 next_state_ = STATE_DATA_CONNECT_COMPLETE; 1247 IPEndPoint ip_endpoint; 1248 AddressList data_address; 1249 // Connect to the same host as the control socket to prevent PASV port 1250 // scanning attacks. 1251 int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint); 1252 if (rv != OK) 1253 return Stop(rv); 1254 data_address = AddressList::CreateFromIPAddress( 1255 ip_endpoint.address(), data_connection_port_); 1256 data_socket_.reset(socket_factory_->CreateTransportClientSocket( 1257 data_address, net_log_.net_log(), net_log_.source())); 1258 net_log_.AddEvent( 1259 NetLog::TYPE_FTP_DATA_CONNECTION, 1260 data_socket_->NetLog().source().ToEventParametersCallback()); 1261 return data_socket_->Connect(io_callback_); 1262} 1263 1264int FtpNetworkTransaction::DoDataConnectComplete(int result) { 1265 if (result != OK && use_epsv_) { 1266 // It's possible we hit a broken server, sadly. They can break in different 1267 // ways. Some time out, some reset a connection. Fall back to PASV. 1268 // TODO(phajdan.jr): remember it for future transactions with this server. 1269 // TODO(phajdan.jr): write a test for this code path. 1270 use_epsv_ = false; 1271 next_state_ = STATE_CTRL_WRITE_PASV; 1272 return OK; 1273 } 1274 1275 // Only record the connection error after we've applied all our fallbacks. 1276 // We want to capture the final error, one we're not going to recover from. 1277 RecordDataConnectionError(result); 1278 1279 if (result != OK) 1280 return Stop(result); 1281 1282 next_state_ = state_after_data_connect_complete_; 1283 return OK; 1284} 1285 1286int FtpNetworkTransaction::DoDataRead() { 1287 DCHECK(read_data_buf_); 1288 DCHECK_GT(read_data_buf_len_, 0); 1289 1290 if (data_socket_ == NULL || !data_socket_->IsConnected()) { 1291 // If we don't destroy the data socket completely, some servers will wait 1292 // for us (http://crbug.com/21127). The half-closed TCP connection needs 1293 // to be closed on our side too. 1294 data_socket_.reset(); 1295 1296 if (ctrl_socket_->IsConnected()) { 1297 // Wait for the server's response, we should get it before sending QUIT. 1298 next_state_ = STATE_CTRL_READ; 1299 return OK; 1300 } 1301 1302 // We are no longer connected to the server, so just finish the transaction. 1303 return Stop(OK); 1304 } 1305 1306 next_state_ = STATE_DATA_READ_COMPLETE; 1307 read_data_buf_->data()[0] = 0; 1308 return data_socket_->Read(read_data_buf_, read_data_buf_len_, io_callback_); 1309} 1310 1311int FtpNetworkTransaction::DoDataReadComplete(int result) { 1312 return result; 1313} 1314 1315// We're using a histogram as a group of counters, with one bucket for each 1316// enumeration value. We're only interested in the values of the counters. 1317// Ignore the shape, average, and standard deviation of the histograms because 1318// they are meaningless. 1319// 1320// We use two histograms. In the first histogram we tally whether the user has 1321// seen an error of that type during the session. In the second histogram we 1322// tally the total number of times the users sees each errer. 1323void FtpNetworkTransaction::RecordDataConnectionError(int result) { 1324 // Gather data for http://crbug.com/3073. See how many users have trouble 1325 // establishing FTP data connection in passive FTP mode. 1326 enum { 1327 // Data connection successful. 1328 NET_ERROR_OK = 0, 1329 1330 // Local firewall blocked the connection. 1331 NET_ERROR_ACCESS_DENIED = 1, 1332 1333 // Connection timed out. 1334 NET_ERROR_TIMED_OUT = 2, 1335 1336 // Connection has been estabilished, but then got broken (either reset 1337 // or aborted). 1338 NET_ERROR_CONNECTION_BROKEN = 3, 1339 1340 // Connection has been refused. 1341 NET_ERROR_CONNECTION_REFUSED = 4, 1342 1343 // No connection to the internet. 1344 NET_ERROR_INTERNET_DISCONNECTED = 5, 1345 1346 // Could not reach the destination address. 1347 NET_ERROR_ADDRESS_UNREACHABLE = 6, 1348 1349 // A programming error in our network stack. 1350 NET_ERROR_UNEXPECTED = 7, 1351 1352 // Other kind of error. 1353 NET_ERROR_OTHER = 20, 1354 1355 NUM_OF_NET_ERROR_TYPES 1356 } type; 1357 switch (result) { 1358 case OK: 1359 type = NET_ERROR_OK; 1360 break; 1361 case ERR_ACCESS_DENIED: 1362 case ERR_NETWORK_ACCESS_DENIED: 1363 type = NET_ERROR_ACCESS_DENIED; 1364 break; 1365 case ERR_TIMED_OUT: 1366 type = NET_ERROR_TIMED_OUT; 1367 break; 1368 case ERR_CONNECTION_ABORTED: 1369 case ERR_CONNECTION_RESET: 1370 case ERR_CONNECTION_CLOSED: 1371 type = NET_ERROR_CONNECTION_BROKEN; 1372 break; 1373 case ERR_CONNECTION_FAILED: 1374 case ERR_CONNECTION_REFUSED: 1375 type = NET_ERROR_CONNECTION_REFUSED; 1376 break; 1377 case ERR_INTERNET_DISCONNECTED: 1378 type = NET_ERROR_INTERNET_DISCONNECTED; 1379 break; 1380 case ERR_ADDRESS_INVALID: 1381 case ERR_ADDRESS_UNREACHABLE: 1382 type = NET_ERROR_ADDRESS_UNREACHABLE; 1383 break; 1384 case ERR_UNEXPECTED: 1385 type = NET_ERROR_UNEXPECTED; 1386 break; 1387 default: 1388 type = NET_ERROR_OTHER; 1389 break; 1390 }; 1391 static bool had_error_type[NUM_OF_NET_ERROR_TYPES]; 1392 1393 DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES); 1394 if (!had_error_type[type]) { 1395 had_error_type[type] = true; 1396 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened", 1397 type, NUM_OF_NET_ERROR_TYPES); 1398 } 1399 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount", 1400 type, NUM_OF_NET_ERROR_TYPES); 1401} 1402 1403} // namespace net 1404