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