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 |