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