1 /* <!-- copyright */
2 /*
3 * aria2 - The high speed download utility
4 *
5 * Copyright (C) 2006 Tatsuhiro Tsujikawa
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 * In addition, as a special exception, the copyright holders give
22 * permission to link the code of portions of this program with the
23 * OpenSSL library under certain conditions as described in each
24 * individual source file, and distribute linked combinations
25 * including the two.
26 * You must obey the GNU General Public License in all respects
27 * for all of the code used other than OpenSSL. If you modify
28 * file(s) with this exception, you may extend this exception to your
29 * version of the file(s), but you are not obligated to do so. If you
30 * do not wish to do so, delete this exception statement from your
31 * version. If you delete this exception statement from all source
32 * files in the program, then also delete it here.
33 */
34 /* copyright --> */
35 #include "FtpNegotiationCommand.h"
36
37 #include <stdint.h>
38 #include <cassert>
39 #include <utility>
40 #include <map>
41
42 #include "Request.h"
43 #include "DownloadEngine.h"
44 #include "FtpConnection.h"
45 #include "RequestGroup.h"
46 #include "PieceStorage.h"
47 #include "FtpDownloadCommand.h"
48 #include "FileEntry.h"
49 #include "DlAbortEx.h"
50 #include "message.h"
51 #include "prefs.h"
52 #include "util.h"
53 #include "Option.h"
54 #include "Logger.h"
55 #include "LogFactory.h"
56 #include "Segment.h"
57 #include "DownloadContext.h"
58 #include "DefaultBtProgressInfoFile.h"
59 #include "RequestGroupMan.h"
60 #include "DownloadFailureException.h"
61 #include "SocketCore.h"
62 #include "fmt.h"
63 #include "DiskAdaptor.h"
64 #include "SegmentMan.h"
65 #include "AuthConfigFactory.h"
66 #include "AuthConfig.h"
67 #include "a2functional.h"
68 #include "URISelector.h"
69 #include "HttpConnection.h"
70 #include "HttpHeader.h"
71 #include "HttpRequest.h"
72 #include "HttpResponse.h"
73 #include "DlRetryEx.h"
74 #include "CheckIntegrityEntry.h"
75 #include "error_code.h"
76 #include "SocketRecvBuffer.h"
77 #include "NullProgressInfoFile.h"
78 #include "ChecksumCheckIntegrityEntry.h"
79
80 namespace aria2 {
81
FtpNegotiationCommand(cuid_t cuid,const std::shared_ptr<Request> & req,const std::shared_ptr<FileEntry> & fileEntry,RequestGroup * requestGroup,DownloadEngine * e,const std::shared_ptr<SocketCore> & socket,Seq seq,const std::string & baseWorkingDir)82 FtpNegotiationCommand::FtpNegotiationCommand(
83 cuid_t cuid, const std::shared_ptr<Request>& req,
84 const std::shared_ptr<FileEntry>& fileEntry, RequestGroup* requestGroup,
85 DownloadEngine* e, const std::shared_ptr<SocketCore>& socket, Seq seq,
86 const std::string& baseWorkingDir)
87 : AbstractCommand(cuid, req, fileEntry, requestGroup, e, socket),
88 sequence_(seq),
89 ftp_(std::make_shared<FtpConnection>(
90 cuid, socket, req,
91 e->getAuthConfigFactory()->createAuthConfig(
92 req, requestGroup->getOption().get()),
93 getOption().get())),
94 pasvPort_(0)
95 {
96 ftp_->setBaseWorkingDir(baseWorkingDir);
97 if (seq == SEQ_RECV_GREETING) {
98 setTimeout(
99 std::chrono::seconds(getOption()->getAsInt(PREF_CONNECT_TIMEOUT)));
100 }
101 setWriteCheckSocket(getSocket());
102 }
103
104 FtpNegotiationCommand::~FtpNegotiationCommand() = default;
105
executeInternal()106 bool FtpNegotiationCommand::executeInternal()
107 {
108 auto segment = getSegments().empty() ? std::shared_ptr<Segment>()
109 : getSegments().front();
110 while (processSequence(segment))
111 ;
112 if (sequence_ == SEQ_RETRY) {
113 return prepareForRetry(0);
114 }
115 else if (sequence_ == SEQ_NEGOTIATION_COMPLETED) {
116 auto command = make_unique<FtpDownloadCommand>(
117 getCuid(), getRequest(), getFileEntry(), getRequestGroup(), ftp_,
118 getDownloadEngine(), dataSocket_, getSocket());
119 command->setStartupIdleTime(
120 std::chrono::seconds(getOption()->getAsInt(PREF_STARTUP_IDLE_TIME)));
121 command->setLowestDownloadSpeedLimit(
122 getOption()->getAsInt(PREF_LOWEST_SPEED_LIMIT));
123 if (getFileEntry()->isUniqueProtocol()) {
124 getFileEntry()->removeURIWhoseHostnameIs(getRequest()->getHost());
125 }
126 getRequestGroup()->getURISelector()->tuneDownloadCommand(
127 getFileEntry()->getRemainingUris(), command.get());
128 getDownloadEngine()->addCommand(std::move(command));
129 return true;
130 }
131 else if (sequence_ == SEQ_HEAD_OK ||
132 sequence_ == SEQ_DOWNLOAD_ALREADY_COMPLETED) {
133 return true;
134 }
135 else if (sequence_ == SEQ_FILE_PREPARATION) {
136 if (getOption()->getAsBool(PREF_FTP_PASV)) {
137 sequence_ = SEQ_PREPARE_PASV;
138 }
139 else {
140 sequence_ = SEQ_PREPARE_PORT;
141 }
142 return false;
143 }
144 else if (sequence_ == SEQ_EXIT) {
145 return true;
146 }
147 else {
148 addCommandSelf();
149 return false;
150 }
151 }
152
recvGreeting()153 bool FtpNegotiationCommand::recvGreeting()
154 {
155 setTimeout(getRequestGroup()->getTimeout());
156 // socket->setBlockingMode();
157 disableWriteCheckSocket();
158 setReadCheckSocket(getSocket());
159
160 int status = ftp_->receiveResponse();
161 if (status == 0) {
162 return false;
163 }
164 if (status != 220) {
165 throw DL_ABORT_EX2(EX_CONNECTION_FAILED, error_code::FTP_PROTOCOL_ERROR);
166 }
167 sequence_ = SEQ_SEND_USER;
168
169 return true;
170 }
171
sendUser()172 bool FtpNegotiationCommand::sendUser()
173 {
174 if (ftp_->sendUser()) {
175 disableWriteCheckSocket();
176 sequence_ = SEQ_RECV_USER;
177 }
178 else {
179 setWriteCheckSocket(getSocket());
180 }
181 return false;
182 }
183
recvUser()184 bool FtpNegotiationCommand::recvUser()
185 {
186 int status = ftp_->receiveResponse();
187 switch (status) {
188 case 0:
189 return false;
190 case 230:
191 sequence_ = SEQ_SEND_TYPE;
192 break;
193 case 331:
194 sequence_ = SEQ_SEND_PASS;
195 break;
196 default:
197 throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, status),
198 error_code::FTP_PROTOCOL_ERROR);
199 }
200 return true;
201 }
202
sendPass()203 bool FtpNegotiationCommand::sendPass()
204 {
205 if (ftp_->sendPass()) {
206 disableWriteCheckSocket();
207 sequence_ = SEQ_RECV_PASS;
208 }
209 else {
210 setWriteCheckSocket(getSocket());
211 }
212 return false;
213 }
214
recvPass()215 bool FtpNegotiationCommand::recvPass()
216 {
217 int status = ftp_->receiveResponse();
218 if (status == 0) {
219 return false;
220 }
221 if (status != 230) {
222 throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, status),
223 error_code::FTP_PROTOCOL_ERROR);
224 }
225 sequence_ = SEQ_SEND_TYPE;
226 return true;
227 }
228
sendType()229 bool FtpNegotiationCommand::sendType()
230 {
231 if (ftp_->sendType()) {
232 disableWriteCheckSocket();
233 sequence_ = SEQ_RECV_TYPE;
234 }
235 else {
236 setWriteCheckSocket(getSocket());
237 }
238 return false;
239 }
240
recvType()241 bool FtpNegotiationCommand::recvType()
242 {
243 int status = ftp_->receiveResponse();
244 if (status == 0) {
245 return false;
246 }
247 if (status != 200) {
248 throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, status),
249 error_code::FTP_PROTOCOL_ERROR);
250 }
251 sequence_ = SEQ_SEND_PWD;
252 return true;
253 }
254
sendPwd()255 bool FtpNegotiationCommand::sendPwd()
256 {
257 if (ftp_->sendPwd()) {
258 disableWriteCheckSocket();
259 sequence_ = SEQ_RECV_PWD;
260 }
261 else {
262 setWriteCheckSocket(getSocket());
263 }
264 return false;
265 }
266
recvPwd()267 bool FtpNegotiationCommand::recvPwd()
268 {
269 std::string pwd;
270 int status = ftp_->receivePwdResponse(pwd);
271 if (status == 0) {
272 return false;
273 }
274 if (status != 257) {
275 throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, status),
276 error_code::FTP_PROTOCOL_ERROR);
277 }
278 ftp_->setBaseWorkingDir(pwd);
279 A2_LOG_INFO(fmt("CUID#%" PRId64 " - base working directory is '%s'",
280 getCuid(), pwd.c_str()));
281 sequence_ = SEQ_SEND_CWD_PREP;
282 return true;
283 }
284
sendCwdPrep()285 bool FtpNegotiationCommand::sendCwdPrep()
286 {
287 // Calling setReadCheckSocket() is needed when the socket is reused,
288 setReadCheckSocket(getSocket());
289 cwdDirs_.push_front(ftp_->getBaseWorkingDir());
290 util::split(getRequest()->getDir().begin(), getRequest()->getDir().end(),
291 std::back_inserter(cwdDirs_), '/');
292 sequence_ = SEQ_SEND_CWD;
293 return true;
294 }
295
sendCwd()296 bool FtpNegotiationCommand::sendCwd()
297 {
298 if (ftp_->sendCwd(cwdDirs_.front())) {
299 disableWriteCheckSocket();
300 sequence_ = SEQ_RECV_CWD;
301 }
302 else {
303 setWriteCheckSocket(getSocket());
304 }
305 return false;
306 }
307
recvCwd()308 bool FtpNegotiationCommand::recvCwd()
309 {
310 int status = ftp_->receiveResponse();
311 if (status == 0) {
312 return false;
313 }
314 if (status != 250) {
315 poolConnection();
316 getRequestGroup()->increaseAndValidateFileNotFoundCount();
317 if (status == 550)
318 throw DL_ABORT_EX2(MSG_RESOURCE_NOT_FOUND,
319 error_code::RESOURCE_NOT_FOUND);
320 else
321 throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, status),
322 error_code::FTP_PROTOCOL_ERROR);
323 }
324 cwdDirs_.pop_front();
325 if (cwdDirs_.empty()) {
326 if (getOption()->getAsBool(PREF_REMOTE_TIME)) {
327 sequence_ = SEQ_SEND_MDTM;
328 }
329 else {
330 sequence_ = SEQ_SEND_SIZE;
331 }
332 }
333 else {
334 sequence_ = SEQ_SEND_CWD;
335 }
336 return true;
337 }
338
sendMdtm()339 bool FtpNegotiationCommand::sendMdtm()
340 {
341 if (ftp_->sendMdtm()) {
342 disableWriteCheckSocket();
343 sequence_ = SEQ_RECV_MDTM;
344 }
345 else {
346 setWriteCheckSocket(getSocket());
347 }
348 return false;
349 }
350
recvMdtm()351 bool FtpNegotiationCommand::recvMdtm()
352 {
353 Time lastModifiedTime = Time::null();
354 int status = ftp_->receiveMdtmResponse(lastModifiedTime);
355 if (status == 0) {
356 return false;
357 }
358 if (status == 213) {
359 if (lastModifiedTime.good()) {
360 getRequestGroup()->updateLastModifiedTime(lastModifiedTime);
361 A2_LOG_DEBUG(fmt("MDTM result was parsed as: %s",
362 lastModifiedTime.toHTTPDate().c_str()));
363 }
364 else {
365 A2_LOG_DEBUG("MDTM response was returned, but it seems not to be"
366 " a time value as in specified in RFC3659.");
367 }
368 }
369 else {
370 A2_LOG_INFO(fmt("CUID#%" PRId64 " - MDTM command failed.", getCuid()));
371 }
372 sequence_ = SEQ_SEND_SIZE;
373 return true;
374 }
375
sendSize()376 bool FtpNegotiationCommand::sendSize()
377 {
378 if (ftp_->sendSize()) {
379 disableWriteCheckSocket();
380 sequence_ = SEQ_RECV_SIZE;
381 }
382 else {
383 setWriteCheckSocket(getSocket());
384 }
385 return false;
386 }
387
onFileSizeDetermined(int64_t totalLength)388 bool FtpNegotiationCommand::onFileSizeDetermined(int64_t totalLength)
389 {
390 getFileEntry()->setLength(totalLength);
391 if (getFileEntry()->getPath().empty()) {
392 auto suffixPath = util::createSafePath(
393 util::percentDecode(std::begin(getRequest()->getFile()),
394 std::end(getRequest()->getFile())));
395
396 getFileEntry()->setPath(
397 util::applyDir(getOption()->get(PREF_DIR), suffixPath));
398 getFileEntry()->setSuffixPath(suffixPath);
399 }
400 getRequestGroup()->preDownloadProcessing();
401 if (totalLength == 0) {
402
403 if (getOption()->getAsBool(PREF_FTP_PASV)) {
404 sequence_ = SEQ_PREPARE_PASV;
405 }
406 else {
407 sequence_ = SEQ_PREPARE_PORT;
408 }
409
410 if (getOption()->getAsBool(PREF_DRY_RUN)) {
411 getRequestGroup()->initPieceStorage();
412 onDryRunFileFound();
413 return false;
414 }
415
416 if (getDownloadContext()->knowsTotalLength() &&
417 getRequestGroup()->downloadFinishedByFileLength()) {
418 // TODO Known issue: if .aria2 file exists, it will not be
419 // deleted on successful verification, because .aria2 file is
420 // not loaded. See also
421 // HttpResponseCommand::handleOtherEncoding()
422 getRequestGroup()->initPieceStorage();
423 if (getDownloadContext()->isChecksumVerificationNeeded()) {
424 A2_LOG_DEBUG("Zero length file exists. Verify checksum.");
425 auto entry =
426 make_unique<ChecksumCheckIntegrityEntry>(getRequestGroup());
427 entry->initValidator();
428 getPieceStorage()->getDiskAdaptor()->openExistingFile();
429 getDownloadEngine()->getCheckIntegrityMan()->pushEntry(
430 std::move(entry));
431 sequence_ = SEQ_EXIT;
432 }
433 else {
434 getPieceStorage()->markAllPiecesDone();
435 getDownloadContext()->setChecksumVerified(true);
436 sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
437 A2_LOG_NOTICE(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
438 GroupId::toHex(getRequestGroup()->getGID()).c_str(),
439 getRequestGroup()->getFirstFilePath().c_str()));
440 }
441 poolConnection();
442 return false;
443 }
444
445 getRequestGroup()->adjustFilename(std::make_shared<NullProgressInfoFile>());
446 getRequestGroup()->initPieceStorage();
447 getPieceStorage()->getDiskAdaptor()->initAndOpenFile();
448
449 if (getDownloadContext()->knowsTotalLength()) {
450 A2_LOG_DEBUG("File length becomes zero and it means download completed.");
451 // TODO Known issue: if .aria2 file exists, it will not be
452 // deleted on successful verification, because .aria2 file is
453 // not loaded. See also
454 // HttpResponseCommand::handleOtherEncoding()
455 if (getDownloadContext()->isChecksumVerificationNeeded()) {
456 A2_LOG_DEBUG("Verify checksum for zero-length file");
457 auto entry =
458 make_unique<ChecksumCheckIntegrityEntry>(getRequestGroup());
459 entry->initValidator();
460 getDownloadEngine()->getCheckIntegrityMan()->pushEntry(
461 std::move(entry));
462 sequence_ = SEQ_EXIT;
463 }
464 else {
465 sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
466 getPieceStorage()->markAllPiecesDone();
467 }
468 poolConnection();
469 return false;
470 }
471 // We have to make sure that command that has Request object must
472 // have segment after PieceStorage is initialized. See
473 // AbstractCommand::execute()
474 getSegmentMan()->getSegmentWithIndex(getCuid(), 0);
475 return true;
476 }
477 else {
478 auto progressInfoFile = std::make_shared<DefaultBtProgressInfoFile>(
479 getDownloadContext(), nullptr, getOption().get());
480 getRequestGroup()->adjustFilename(progressInfoFile);
481 getRequestGroup()->initPieceStorage();
482
483 if (getOption()->getAsBool(PREF_DRY_RUN)) {
484 onDryRunFileFound();
485 return false;
486 }
487
488 auto checkIntegrityEntry = getRequestGroup()->createCheckIntegrityEntry();
489 if (!checkIntegrityEntry) {
490 sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
491 poolConnection();
492 return false;
493 }
494 // We have to make sure that command that has Request object must
495 // have segment after PieceStorage is initialized. See
496 // AbstractCommand::execute()
497 getSegmentMan()->getSegmentWithIndex(getCuid(), 0);
498
499 checkIntegrityEntry->pushNextCommand(std::unique_ptr<Command>(this));
500 prepareForNextAction(std::move(checkIntegrityEntry));
501
502 disableReadCheckSocket();
503 }
504 return false;
505 }
506
recvSize()507 bool FtpNegotiationCommand::recvSize()
508 {
509 int64_t size = 0;
510 int status = ftp_->receiveSizeResponse(size);
511 if (status == 0) {
512 return false;
513 }
514 if (status == 213) {
515 if (size > std::numeric_limits<a2_off_t>::max()) {
516 throw DL_ABORT_EX2(fmt(EX_TOO_LARGE_FILE, size),
517 error_code::FTP_PROTOCOL_ERROR);
518 }
519 if (!getPieceStorage()) {
520
521 sequence_ = SEQ_FILE_PREPARATION;
522 return onFileSizeDetermined(size);
523 }
524 else {
525 getRequestGroup()->validateTotalLength(getFileEntry()->getLength(), size);
526 }
527 }
528 else {
529 A2_LOG_INFO(fmt("CUID#%" PRId64
530 " - The remote FTP Server doesn't recognize SIZE"
531 " command. Continue.",
532 getCuid()));
533 // Even if one of the other servers waiting in the queue supports SIZE
534 // command, resuming and segmented downloading are disabled when the first
535 // contacted FTP server doesn't support it.
536 if (!getPieceStorage()) {
537 getDownloadContext()->markTotalLengthIsUnknown();
538 return onFileSizeDetermined(0);
539 }
540 // TODO Skipping RequestGroup::validateTotalLength(0) here will allow
541 // wrong file to be downloaded if user-specified URL is wrong.
542 }
543 if (getOption()->getAsBool(PREF_FTP_PASV)) {
544 sequence_ = SEQ_PREPARE_PASV;
545 }
546 else {
547 sequence_ = SEQ_PREPARE_PORT;
548 }
549 return true;
550 }
551
afterFileAllocation()552 void FtpNegotiationCommand::afterFileAllocation()
553 {
554 setReadCheckSocket(getSocket());
555 }
556
preparePort()557 bool FtpNegotiationCommand::preparePort()
558 {
559 afterFileAllocation();
560 if (getSocket()->getAddressFamily() == AF_INET6) {
561 sequence_ = SEQ_PREPARE_SERVER_SOCKET_EPRT;
562 }
563 else {
564 sequence_ = SEQ_PREPARE_SERVER_SOCKET;
565 }
566 return true;
567 }
568
prepareServerSocketEprt()569 bool FtpNegotiationCommand::prepareServerSocketEprt()
570 {
571 serverSocket_ = ftp_->createServerSocket();
572 sequence_ = SEQ_SEND_EPRT;
573 return true;
574 }
575
prepareServerSocket()576 bool FtpNegotiationCommand::prepareServerSocket()
577 {
578 serverSocket_ = ftp_->createServerSocket();
579 sequence_ = SEQ_SEND_PORT;
580 return true;
581 }
582
sendEprt()583 bool FtpNegotiationCommand::sendEprt()
584 {
585 if (ftp_->sendEprt(serverSocket_)) {
586 disableWriteCheckSocket();
587 sequence_ = SEQ_RECV_EPRT;
588 }
589 else {
590 setWriteCheckSocket(getSocket());
591 }
592 return false;
593 }
594
recvEprt()595 bool FtpNegotiationCommand::recvEprt()
596 {
597 int status = ftp_->receiveResponse();
598 if (status == 0) {
599 return false;
600 }
601 if (status == 200) {
602 sequence_ = SEQ_SEND_REST;
603 }
604 else {
605 sequence_ = SEQ_PREPARE_SERVER_SOCKET;
606 }
607 return true;
608 }
609
sendPort()610 bool FtpNegotiationCommand::sendPort()
611 {
612 if (ftp_->sendPort(serverSocket_)) {
613 disableWriteCheckSocket();
614 sequence_ = SEQ_RECV_PORT;
615 }
616 else {
617 setWriteCheckSocket(getSocket());
618 }
619 return false;
620 }
621
recvPort()622 bool FtpNegotiationCommand::recvPort()
623 {
624 int status = ftp_->receiveResponse();
625 if (status == 0) {
626 return false;
627 }
628 if (status != 200) {
629 throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, status),
630 error_code::FTP_PROTOCOL_ERROR);
631 }
632 sequence_ = SEQ_SEND_REST;
633 return true;
634 }
635
preparePasv()636 bool FtpNegotiationCommand::preparePasv()
637 {
638 afterFileAllocation();
639 if (getSocket()->getAddressFamily() == AF_INET6) {
640 sequence_ = SEQ_SEND_EPSV;
641 }
642 else {
643 sequence_ = SEQ_SEND_PASV;
644 }
645 return true;
646 }
647
sendEpsv()648 bool FtpNegotiationCommand::sendEpsv()
649 {
650 if (ftp_->sendEpsv()) {
651 disableWriteCheckSocket();
652 sequence_ = SEQ_RECV_EPSV;
653 }
654 else {
655 setWriteCheckSocket(getSocket());
656 }
657 return true;
658 }
659
recvEpsv()660 bool FtpNegotiationCommand::recvEpsv()
661 {
662 uint16_t port;
663 int status = ftp_->receiveEpsvResponse(port);
664 if (status == 0) {
665 return false;
666 }
667 if (status == 229) {
668 pasvPort_ = port;
669 return preparePasvConnect();
670 }
671 else {
672 sequence_ = SEQ_SEND_PASV;
673 return true;
674 }
675 }
676
sendPasv()677 bool FtpNegotiationCommand::sendPasv()
678 {
679 if (ftp_->sendPasv()) {
680 disableWriteCheckSocket();
681 sequence_ = SEQ_RECV_PASV;
682 }
683 else {
684 setWriteCheckSocket(getSocket());
685 }
686 return false;
687 }
688
recvPasv()689 bool FtpNegotiationCommand::recvPasv()
690 {
691 std::pair<std::string, uint16_t> dest;
692 int status = ftp_->receivePasvResponse(dest);
693 if (status == 0) {
694 return false;
695 }
696 if (status != 227) {
697 throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, status),
698 error_code::FTP_PROTOCOL_ERROR);
699 }
700 pasvPort_ = dest.second;
701 ;
702 return preparePasvConnect();
703 }
704
preparePasvConnect()705 bool FtpNegotiationCommand::preparePasvConnect()
706 {
707 if (isProxyDefined()) {
708 sequence_ = SEQ_RESOLVE_PROXY;
709 return true;
710 }
711 else {
712 auto endpoint = getSocket()->getPeerInfo();
713 // make a data connection to the server.
714 A2_LOG_INFO(fmt(MSG_CONNECTING_TO_SERVER, getCuid(), endpoint.addr.c_str(),
715 pasvPort_));
716 dataSocket_ = std::make_shared<SocketCore>();
717 dataSocket_->establishConnection(endpoint.addr, pasvPort_, false);
718 disableReadCheckSocket();
719 setWriteCheckSocket(dataSocket_);
720 sequence_ = SEQ_SEND_REST_PASV;
721 return false;
722 }
723 }
724
resolveProxy()725 bool FtpNegotiationCommand::resolveProxy()
726 {
727 std::shared_ptr<Request> proxyReq = createProxyRequest();
728 std::vector<std::string> addrs;
729 proxyAddr_ = resolveHostname(addrs, proxyReq->getHost(), proxyReq->getPort());
730 if (proxyAddr_.empty()) {
731 return false;
732 }
733 A2_LOG_INFO(fmt(MSG_CONNECTING_TO_SERVER, getCuid(), proxyAddr_.c_str(),
734 proxyReq->getPort()));
735 dataSocket_ = std::make_shared<SocketCore>();
736 dataSocket_->establishConnection(proxyAddr_, proxyReq->getPort());
737 disableReadCheckSocket();
738 setWriteCheckSocket(dataSocket_);
739 auto socketRecvBuffer = std::make_shared<SocketRecvBuffer>(dataSocket_);
740 http_ = std::make_shared<HttpConnection>(getCuid(), dataSocket_,
741 socketRecvBuffer);
742 sequence_ = SEQ_SEND_TUNNEL_REQUEST;
743 return false;
744 }
745
sendTunnelRequest()746 bool FtpNegotiationCommand::sendTunnelRequest()
747 {
748 if (http_->sendBufferIsEmpty()) {
749 if (dataSocket_->isReadable(0)) {
750 std::string error = getSocket()->getSocketError();
751 if (!error.empty()) {
752 std::shared_ptr<Request> proxyReq = createProxyRequest();
753 getDownloadEngine()->markBadIPAddress(proxyReq->getHost(), proxyAddr_,
754 proxyReq->getPort());
755 std::string nextProxyAddr = getDownloadEngine()->findCachedIPAddress(
756 proxyReq->getHost(), proxyReq->getPort());
757 if (nextProxyAddr.empty()) {
758 getDownloadEngine()->removeCachedIPAddress(proxyReq->getHost(),
759 proxyReq->getPort());
760 throw DL_RETRY_EX(
761 fmt(MSG_ESTABLISHING_CONNECTION_FAILED, error.c_str()));
762 }
763 else {
764 A2_LOG_INFO(fmt(MSG_CONNECT_FAILED_AND_RETRY, getCuid(),
765 proxyAddr_.c_str(), proxyReq->getPort()));
766 proxyAddr_ = nextProxyAddr;
767 A2_LOG_INFO(fmt(MSG_CONNECTING_TO_SERVER, getCuid(),
768 proxyAddr_.c_str(), proxyReq->getPort()));
769 dataSocket_->establishConnection(proxyAddr_, proxyReq->getPort());
770 return false;
771 }
772 }
773 }
774 auto httpRequest = make_unique<HttpRequest>();
775 httpRequest->setUserAgent(getOption()->get(PREF_USER_AGENT));
776 auto req = std::make_shared<Request>();
777 // Construct fake URI in order to use HttpRequest
778 std::pair<std::string, uint16_t> dataAddr;
779 uri::UriStruct us;
780 us.protocol = "ftp";
781 us.host = getRequest()->getHost();
782 us.port = pasvPort_;
783 us.ipv6LiteralAddress = getRequest()->isIPv6LiteralAddress();
784 if (!req->setUri(uri::construct(us))) {
785 throw DL_RETRY_EX("Something wrong with FTP URI");
786 }
787 httpRequest->setRequest(req);
788 httpRequest->setProxyRequest(createProxyRequest());
789 http_->sendProxyRequest(std::move(httpRequest));
790 }
791 else {
792 http_->sendPendingData();
793 }
794 if (http_->sendBufferIsEmpty()) {
795 disableWriteCheckSocket();
796 setReadCheckSocket(dataSocket_);
797 sequence_ = SEQ_RECV_TUNNEL_RESPONSE;
798 return false;
799 }
800 else {
801 setWriteCheckSocket(dataSocket_);
802 return false;
803 }
804 }
805
recvTunnelResponse()806 bool FtpNegotiationCommand::recvTunnelResponse()
807 {
808 std::shared_ptr<HttpResponse> httpResponse = http_->receiveResponse();
809 if (!httpResponse) {
810 return false;
811 }
812 if (httpResponse->getStatusCode() != 200) {
813 throw DL_RETRY_EX(EX_PROXY_CONNECTION_FAILED);
814 }
815 sequence_ = SEQ_SEND_REST_PASV;
816 return true;
817 }
818
sendRestPasv(const std::shared_ptr<Segment> & segment)819 bool FtpNegotiationCommand::sendRestPasv(
820 const std::shared_ptr<Segment>& segment)
821 {
822 // dataSocket_->setBlockingMode();
823 // Check connection is made properly
824 if (dataSocket_->isReadable(0)) {
825 std::string error = dataSocket_->getSocketError();
826 throw DL_ABORT_EX2(fmt(MSG_ESTABLISHING_CONNECTION_FAILED, error.c_str()),
827 error_code::FTP_PROTOCOL_ERROR);
828 }
829 setReadCheckSocket(getSocket());
830 disableWriteCheckSocket();
831 return sendRest(segment);
832 }
833
sendRest(const std::shared_ptr<Segment> & segment)834 bool FtpNegotiationCommand::sendRest(const std::shared_ptr<Segment>& segment)
835 {
836 if (ftp_->sendRest(segment)) {
837 disableWriteCheckSocket();
838 sequence_ = SEQ_RECV_REST;
839 }
840 else {
841 setWriteCheckSocket(getSocket());
842 }
843 return false;
844 }
845
recvRest(const std::shared_ptr<Segment> & segment)846 bool FtpNegotiationCommand::recvRest(const std::shared_ptr<Segment>& segment)
847 {
848 int status = ftp_->receiveResponse();
849 if (status == 0) {
850 return false;
851 }
852 // If we receive negative response and requested file position is not 0,
853 // then throw exception here.
854 if (status != 350) {
855 if (segment && segment->getPositionToWrite() != 0) {
856 throw DL_ABORT_EX2("FTP server doesn't support resuming.",
857 error_code::CANNOT_RESUME);
858 }
859 }
860 sequence_ = SEQ_SEND_RETR;
861 return true;
862 }
863
sendRetr()864 bool FtpNegotiationCommand::sendRetr()
865 {
866 if (ftp_->sendRetr()) {
867 disableWriteCheckSocket();
868 sequence_ = SEQ_RECV_RETR;
869 }
870 else {
871 setWriteCheckSocket(getSocket());
872 }
873 return false;
874 }
875
recvRetr()876 bool FtpNegotiationCommand::recvRetr()
877 {
878 int status = ftp_->receiveResponse();
879 if (status == 0) {
880 return false;
881 }
882 if (status != 150 && status != 125) {
883 getRequestGroup()->increaseAndValidateFileNotFoundCount();
884 if (status == 550)
885 throw DL_ABORT_EX2(MSG_RESOURCE_NOT_FOUND,
886 error_code::RESOURCE_NOT_FOUND);
887 else
888 throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, status),
889 error_code::FTP_PROTOCOL_ERROR);
890 }
891 if (getOption()->getAsBool(PREF_FTP_PASV)) {
892 sequence_ = SEQ_NEGOTIATION_COMPLETED;
893 return false;
894 }
895 else {
896 disableReadCheckSocket();
897 setReadCheckSocket(serverSocket_);
898 sequence_ = SEQ_WAIT_CONNECTION;
899 return false;
900 }
901 }
902
waitConnection()903 bool FtpNegotiationCommand::waitConnection()
904 {
905 disableReadCheckSocket();
906 setReadCheckSocket(getSocket());
907 dataSocket_ = serverSocket_->acceptConnection();
908 sequence_ = SEQ_NEGOTIATION_COMPLETED;
909 return false;
910 }
911
processSequence(const std::shared_ptr<Segment> & segment)912 bool FtpNegotiationCommand::processSequence(
913 const std::shared_ptr<Segment>& segment)
914 {
915 bool doNextSequence = true;
916 switch (sequence_) {
917 case SEQ_RECV_GREETING:
918 return recvGreeting();
919 case SEQ_SEND_USER:
920 return sendUser();
921 case SEQ_RECV_USER:
922 return recvUser();
923 case SEQ_SEND_PASS:
924 return sendPass();
925 case SEQ_RECV_PASS:
926 return recvPass();
927 case SEQ_SEND_TYPE:
928 return sendType();
929 case SEQ_RECV_TYPE:
930 return recvType();
931 case SEQ_SEND_PWD:
932 return sendPwd();
933 case SEQ_RECV_PWD:
934 return recvPwd();
935 case SEQ_SEND_CWD_PREP:
936 return sendCwdPrep();
937 case SEQ_SEND_CWD:
938 return sendCwd();
939 case SEQ_RECV_CWD:
940 return recvCwd();
941 case SEQ_SEND_MDTM:
942 return sendMdtm();
943 case SEQ_RECV_MDTM:
944 return recvMdtm();
945 case SEQ_SEND_SIZE:
946 return sendSize();
947 case SEQ_RECV_SIZE:
948 return recvSize();
949 case SEQ_PREPARE_PORT:
950 return preparePort();
951 case SEQ_PREPARE_SERVER_SOCKET_EPRT:
952 return prepareServerSocketEprt();
953 case SEQ_SEND_EPRT:
954 return sendEprt();
955 case SEQ_RECV_EPRT:
956 return recvEprt();
957 case SEQ_PREPARE_SERVER_SOCKET:
958 return prepareServerSocket();
959 case SEQ_SEND_PORT:
960 return sendPort();
961 case SEQ_RECV_PORT:
962 return recvPort();
963 case SEQ_PREPARE_PASV:
964 return preparePasv();
965 case SEQ_SEND_EPSV:
966 return sendEpsv();
967 case SEQ_RECV_EPSV:
968 return recvEpsv();
969 case SEQ_SEND_PASV:
970 return sendPasv();
971 case SEQ_RECV_PASV:
972 return recvPasv();
973 case SEQ_RESOLVE_PROXY:
974 return resolveProxy();
975 case SEQ_SEND_TUNNEL_REQUEST:
976 return sendTunnelRequest();
977 case SEQ_RECV_TUNNEL_RESPONSE:
978 return recvTunnelResponse();
979 case SEQ_SEND_REST_PASV:
980 return sendRestPasv(segment);
981 case SEQ_SEND_REST:
982 return sendRest(segment);
983 case SEQ_RECV_REST:
984 return recvRest(segment);
985 case SEQ_SEND_RETR:
986 return sendRetr();
987 case SEQ_RECV_RETR:
988 return recvRetr();
989 case SEQ_WAIT_CONNECTION:
990 return waitConnection();
991 default:
992 abort();
993 }
994 return doNextSequence;
995 }
996
poolConnection() const997 void FtpNegotiationCommand::poolConnection() const
998 {
999 if (getOption()->getAsBool(PREF_FTP_REUSE_CONNECTION)) {
1000 // Store ftp_->getBaseWorkingDir() as options
1001 getDownloadEngine()->poolSocket(getRequest(), ftp_->getUser(),
1002 createProxyRequest(), getSocket(),
1003 ftp_->getBaseWorkingDir());
1004 }
1005 }
1006
onDryRunFileFound()1007 void FtpNegotiationCommand::onDryRunFileFound()
1008 {
1009 getPieceStorage()->markAllPiecesDone();
1010 getDownloadContext()->setChecksumVerified(true);
1011 poolConnection();
1012 sequence_ = SEQ_HEAD_OK;
1013 }
1014
1015 } // namespace aria2
1016