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