FtpConnection.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  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 "FtpConnection.h"
  36. #include <cstring>
  37. #include <cstdio>
  38. #include <cassert>
  39. #include "Request.h"
  40. #include "Segment.h"
  41. #include "Option.h"
  42. #include "util.h"
  43. #include "message.h"
  44. #include "prefs.h"
  45. #include "LogFactory.h"
  46. #include "Logger.h"
  47. #include "AuthConfigFactory.h"
  48. #include "AuthConfig.h"
  49. #include "DlRetryEx.h"
  50. #include "DlAbortEx.h"
  51. #include "Socket.h"
  52. #include "A2STR.h"
  53. #include "StringFormat.h"
  54. #include "AuthConfig.h"
  55. #include "a2functional.h"
  56. namespace aria2 {
  57. const std::string FtpConnection::A("A");
  58. const std::string FtpConnection::I("I");
  59. FtpConnection::FtpConnection(cuid_t cuid, const SocketHandle& socket,
  60. const SharedHandle<Request>& req,
  61. const SharedHandle<AuthConfig>& authConfig,
  62. const Option* op):
  63. cuid_(cuid), socket_(socket), req_(req),
  64. authConfig_(authConfig), option_(op),
  65. logger_(LogFactory::getInstance()),
  66. socketBuffer_(socket),
  67. baseWorkingDir_("/") {}
  68. FtpConnection::~FtpConnection() {}
  69. bool FtpConnection::sendUser()
  70. {
  71. if(socketBuffer_.sendBufferIsEmpty()) {
  72. std::string request = "USER ";
  73. request += authConfig_->getUser();
  74. request += "\r\n";
  75. if(logger_->info()) {
  76. logger_->info(MSG_SENDING_REQUEST,
  77. util::itos(cuid_).c_str(),"USER ********");
  78. }
  79. socketBuffer_.pushStr(request);
  80. }
  81. socketBuffer_.send();
  82. return socketBuffer_.sendBufferIsEmpty();
  83. }
  84. bool FtpConnection::sendPass()
  85. {
  86. if(socketBuffer_.sendBufferIsEmpty()) {
  87. std::string request = "PASS ";
  88. request += authConfig_->getPassword();
  89. request += "\r\n";
  90. if(logger_->info()) {
  91. logger_->info(MSG_SENDING_REQUEST,
  92. util::itos(cuid_).c_str(),"PASS ********");
  93. }
  94. socketBuffer_.pushStr(request);
  95. }
  96. socketBuffer_.send();
  97. return socketBuffer_.sendBufferIsEmpty();
  98. }
  99. bool FtpConnection::sendType()
  100. {
  101. if(socketBuffer_.sendBufferIsEmpty()) {
  102. std::string type;
  103. if(option_->get(PREF_FTP_TYPE) == V_ASCII) {
  104. type = FtpConnection::A;
  105. } else {
  106. type = FtpConnection::I;
  107. }
  108. std::string request = "TYPE ";
  109. request += type;
  110. request += "\r\n";
  111. if(logger_->info()) {
  112. logger_->info(MSG_SENDING_REQUEST,
  113. util::itos(cuid_).c_str(),request.c_str());
  114. }
  115. socketBuffer_.pushStr(request);
  116. }
  117. socketBuffer_.send();
  118. return socketBuffer_.sendBufferIsEmpty();
  119. }
  120. bool FtpConnection::sendPwd()
  121. {
  122. if(socketBuffer_.sendBufferIsEmpty()) {
  123. std::string request = "PWD\r\n";
  124. if(logger_->info()) {
  125. logger_->info(MSG_SENDING_REQUEST,
  126. util::itos(cuid_).c_str(),request.c_str());
  127. }
  128. socketBuffer_.pushStr(request);
  129. }
  130. socketBuffer_.send();
  131. return socketBuffer_.sendBufferIsEmpty();
  132. }
  133. bool FtpConnection::sendCwd(const std::string& dir)
  134. {
  135. if(socketBuffer_.sendBufferIsEmpty()) {
  136. std::string request = "CWD ";
  137. request += util::percentDecode(dir);
  138. request += "\r\n";
  139. if(logger_->info()) {
  140. logger_->info(MSG_SENDING_REQUEST,
  141. util::itos(cuid_).c_str(),request.c_str());
  142. }
  143. socketBuffer_.pushStr(request);
  144. }
  145. socketBuffer_.send();
  146. return socketBuffer_.sendBufferIsEmpty();
  147. }
  148. bool FtpConnection::sendMdtm()
  149. {
  150. if(socketBuffer_.sendBufferIsEmpty()) {
  151. std::string request = "MDTM ";
  152. request += util::percentDecode(req_->getFile());
  153. request += "\r\n";
  154. if(logger_->info()) {
  155. logger_->info(MSG_SENDING_REQUEST,
  156. util::itos(cuid_).c_str(), request.c_str());
  157. }
  158. socketBuffer_.pushStr(request);
  159. }
  160. socketBuffer_.send();
  161. return socketBuffer_.sendBufferIsEmpty();
  162. }
  163. bool FtpConnection::sendSize()
  164. {
  165. if(socketBuffer_.sendBufferIsEmpty()) {
  166. std::string request = "SIZE ";
  167. request += util::percentDecode(req_->getFile());
  168. request += "\r\n";
  169. if(logger_->info()) {
  170. logger_->info(MSG_SENDING_REQUEST,
  171. util::itos(cuid_).c_str(), request.c_str());
  172. }
  173. socketBuffer_.pushStr(request);
  174. }
  175. socketBuffer_.send();
  176. return socketBuffer_.sendBufferIsEmpty();
  177. }
  178. bool FtpConnection::sendEpsv()
  179. {
  180. if(socketBuffer_.sendBufferIsEmpty()) {
  181. static const std::string request("EPSV\r\n");
  182. if(logger_->info()) {
  183. logger_->info(MSG_SENDING_REQUEST,
  184. util::itos(cuid_).c_str(), request.c_str());
  185. }
  186. socketBuffer_.pushStr(request);
  187. }
  188. socketBuffer_.send();
  189. return socketBuffer_.sendBufferIsEmpty();
  190. }
  191. bool FtpConnection::sendPasv()
  192. {
  193. if(socketBuffer_.sendBufferIsEmpty()) {
  194. static const std::string request("PASV\r\n");
  195. if(logger_->info()) {
  196. logger_->info(MSG_SENDING_REQUEST,
  197. util::itos(cuid_).c_str(), request.c_str());
  198. }
  199. socketBuffer_.pushStr(request);
  200. }
  201. socketBuffer_.send();
  202. return socketBuffer_.sendBufferIsEmpty();
  203. }
  204. SharedHandle<SocketCore> FtpConnection::createServerSocket()
  205. {
  206. std::pair<std::string, uint16_t> addrinfo;
  207. socket_->getAddrInfo(addrinfo);
  208. SharedHandle<SocketCore> serverSocket(new SocketCore());
  209. serverSocket->bind(addrinfo.first, 0, AF_UNSPEC);
  210. serverSocket->beginListen();
  211. serverSocket->setNonBlockingMode();
  212. return serverSocket;
  213. }
  214. bool FtpConnection::sendEprt(const SharedHandle<SocketCore>& serverSocket)
  215. {
  216. if(socketBuffer_.sendBufferIsEmpty()) {
  217. struct sockaddr_storage sockaddr;
  218. socklen_t len = sizeof(sockaddr);
  219. serverSocket->getAddrInfo(sockaddr, len);
  220. std::pair<std::string, uint16_t> addrinfo = util::getNumericNameInfo
  221. (reinterpret_cast<const struct sockaddr*>(&sockaddr), len);
  222. std::string request = "EPRT ";
  223. request += "|";
  224. request += util::itos(sockaddr.ss_family == AF_INET?1:2);
  225. request += "|";
  226. request += addrinfo.first;
  227. request += "|";
  228. request += util::uitos(addrinfo.second);
  229. request += "|\r\n";
  230. if(logger_->info()) {
  231. logger_->info(MSG_SENDING_REQUEST,
  232. util::itos(cuid_).c_str(), request.c_str());
  233. }
  234. socketBuffer_.pushStr(request);
  235. }
  236. socketBuffer_.send();
  237. return socketBuffer_.sendBufferIsEmpty();
  238. }
  239. bool FtpConnection::sendPort(const SharedHandle<SocketCore>& serverSocket)
  240. {
  241. if(socketBuffer_.sendBufferIsEmpty()) {
  242. std::pair<std::string, uint16_t> addrinfo;
  243. socket_->getAddrInfo(addrinfo);
  244. unsigned int ipaddr[4];
  245. sscanf(addrinfo.first.c_str(), "%u.%u.%u.%u",
  246. &ipaddr[0], &ipaddr[1], &ipaddr[2], &ipaddr[3]);
  247. serverSocket->getAddrInfo(addrinfo);
  248. std::string request = "PORT ";
  249. request += util::uitos(ipaddr[0]);
  250. request += ",";
  251. request += util::uitos(ipaddr[1]);
  252. request += ",";
  253. request += util::uitos(ipaddr[2]);
  254. request += ",";
  255. request += util::uitos(ipaddr[3]);
  256. request += ",";
  257. request += util::uitos(addrinfo.second/256);
  258. request += ",";
  259. request += util::uitos(addrinfo.second%256);
  260. request += "\r\n";
  261. if(logger_->info()) {
  262. logger_->info(MSG_SENDING_REQUEST,
  263. util::itos(cuid_).c_str(), request.c_str());
  264. }
  265. socketBuffer_.pushStr(request);
  266. }
  267. socketBuffer_.send();
  268. return socketBuffer_.sendBufferIsEmpty();
  269. }
  270. bool FtpConnection::sendRest(const SharedHandle<Segment>& segment)
  271. {
  272. if(socketBuffer_.sendBufferIsEmpty()) {
  273. std::string request = "REST ";
  274. if(segment.isNull()) {
  275. request += "0";
  276. } else {
  277. request += util::itos(segment->getPositionToWrite());
  278. }
  279. request += "\r\n";
  280. if(logger_->info()) {
  281. logger_->info(MSG_SENDING_REQUEST,
  282. util::itos(cuid_).c_str(), request.c_str());
  283. }
  284. socketBuffer_.pushStr(request);
  285. }
  286. socketBuffer_.send();
  287. return socketBuffer_.sendBufferIsEmpty();
  288. }
  289. bool FtpConnection::sendRetr()
  290. {
  291. if(socketBuffer_.sendBufferIsEmpty()) {
  292. std::string request = "RETR ";
  293. request += util::percentDecode(req_->getFile());
  294. request += "\r\n";
  295. if(logger_->info()) {
  296. logger_->info(MSG_SENDING_REQUEST,
  297. util::itos(cuid_).c_str(), request.c_str());
  298. }
  299. socketBuffer_.pushStr(request);
  300. }
  301. socketBuffer_.send();
  302. return socketBuffer_.sendBufferIsEmpty();
  303. }
  304. unsigned int FtpConnection::getStatus(const std::string& response) const
  305. {
  306. unsigned int status;
  307. // When the response is not like "%u %*s",
  308. // we return 0.
  309. if(response.find_first_not_of("0123456789") != 3
  310. || !(response.find(" ") == 3 || response.find("-") == 3)) {
  311. return 0;
  312. }
  313. if(sscanf(response.c_str(), "%u %*s", &status) == 1) {
  314. return status;
  315. } else {
  316. return 0;
  317. }
  318. }
  319. // Returns the length of the reponse if the whole response has been received.
  320. // The length includes \r\n.
  321. // If the whole response has not been received, then returns std::string::npos.
  322. std::string::size_type
  323. FtpConnection::findEndOfResponse(unsigned int status,
  324. const std::string& buf) const
  325. {
  326. if(buf.size() <= 4) {
  327. return std::string::npos;
  328. }
  329. // if 4th character of buf is '-', then multi line response is expected.
  330. if(buf.at(3) == '-') {
  331. // multi line response
  332. std::string::size_type p;
  333. std::string endPattern = A2STR::CRLF;
  334. endPattern += util::uitos(status);
  335. endPattern += " ";
  336. p = buf.find(endPattern);
  337. if(p == std::string::npos) {
  338. return std::string::npos;
  339. }
  340. p = buf.find(A2STR::CRLF, p+6);
  341. if(p == std::string::npos) {
  342. return std::string::npos;
  343. } else {
  344. return p+2;
  345. }
  346. } else {
  347. // single line response
  348. std::string::size_type p = buf.find(A2STR::CRLF);
  349. if(p == std::string::npos) {
  350. return std::string::npos;
  351. } else {
  352. return p+2;
  353. }
  354. }
  355. }
  356. bool FtpConnection::bulkReceiveResponse
  357. (std::pair<unsigned int, std::string>& response)
  358. {
  359. char buf[1024];
  360. while(socket_->isReadable(0)) {
  361. size_t size = sizeof(buf);
  362. socket_->readData(buf, size);
  363. if(size == 0) {
  364. if(socket_->wantRead() || socket_->wantWrite()) {
  365. return false;
  366. }
  367. throw DL_RETRY_EX(EX_GOT_EOF);
  368. }
  369. if(strbuf_.size()+size > MAX_RECV_BUFFER) {
  370. throw DL_RETRY_EX
  371. (StringFormat("Max FTP recv buffer reached. length=%lu",
  372. static_cast<unsigned long>(strbuf_.size()+size)).str());
  373. }
  374. strbuf_.append(&buf[0], &buf[size]);
  375. }
  376. unsigned int status;
  377. if(strbuf_.size() >= 4) {
  378. status = getStatus(strbuf_);
  379. if(status == 0) {
  380. throw DL_ABORT_EX(EX_INVALID_RESPONSE);
  381. }
  382. } else {
  383. return false;
  384. }
  385. std::string::size_type length;
  386. if((length = findEndOfResponse(status, strbuf_)) != std::string::npos) {
  387. response.first = status;
  388. response.second = strbuf_.substr(0, length);
  389. if(logger_->info()) {
  390. logger_->info(MSG_RECEIVE_RESPONSE,
  391. util::itos(cuid_).c_str(), response.second.c_str());
  392. }
  393. strbuf_.erase(0, length);
  394. return true;
  395. } else {
  396. // didn't receive response fully.
  397. return false;
  398. }
  399. }
  400. unsigned int FtpConnection::receiveResponse()
  401. {
  402. std::pair<unsigned int, std::string> response;
  403. if(bulkReceiveResponse(response)) {
  404. return response.first;
  405. } else {
  406. return 0;
  407. }
  408. }
  409. #ifdef __MINGW32__
  410. # define LONGLONG_PRINTF "%I64d"
  411. # define ULONGLONG_PRINTF "%I64u"
  412. # define LONGLONG_SCANF "%I64d"
  413. # define ULONGLONG_SCANF "%I64u"
  414. #else
  415. # define LONGLONG_PRINTF "%lld"
  416. # define ULONGLONG_PRINTF "%llu"
  417. # define LONGLONG_SCANF "%Ld"
  418. // Mac OSX uses "%llu" for 64bits integer.
  419. # define ULONGLONG_SCANF "%Lu"
  420. #endif // __MINGW32__
  421. unsigned int FtpConnection::receiveSizeResponse(uint64_t& size)
  422. {
  423. std::pair<unsigned int, std::string> response;
  424. if(bulkReceiveResponse(response)) {
  425. if(response.first == 213) {
  426. std::pair<std::string, std::string> rp = util::split(response.second," ");
  427. size = util::parseULLInt(rp.second);
  428. }
  429. return response.first;
  430. } else {
  431. return 0;
  432. }
  433. }
  434. unsigned int FtpConnection::receiveMdtmResponse(Time& time)
  435. {
  436. // MDTM command, specified in RFC3659.
  437. std::pair<unsigned int, std::string> response;
  438. if(bulkReceiveResponse(response)) {
  439. if(response.first == 213) {
  440. char buf[15]; // YYYYMMDDhhmmss+\0, milli second part is dropped.
  441. sscanf(response.second.c_str(), "%*u %14s", buf);
  442. if(strlen(buf) == 14) {
  443. // We don't use Time::parse(buf,"%Y%m%d%H%M%S") here because Mac OS X
  444. // and included strptime doesn't parse data for this format.
  445. struct tm tm;
  446. memset(&tm, 0, sizeof(tm));
  447. tm.tm_sec = util::parseInt(&buf[12]);
  448. buf[12] = '\0';
  449. tm.tm_min = util::parseInt(&buf[10]);
  450. buf[10] = '\0';
  451. tm.tm_hour = util::parseInt(&buf[8]);
  452. buf[8] = '\0';
  453. tm.tm_mday = util::parseInt(&buf[6]);
  454. buf[6] = '\0';
  455. tm.tm_mon = util::parseInt(&buf[4])-1;
  456. buf[4] = '\0';
  457. tm.tm_year = util::parseInt(&buf[0])-1900;
  458. time = Time(timegm(&tm));
  459. } else {
  460. time = Time::null();
  461. }
  462. }
  463. return response.first;
  464. } else {
  465. return 0;
  466. }
  467. }
  468. unsigned int FtpConnection::receiveEpsvResponse(uint16_t& port)
  469. {
  470. std::pair<unsigned int, std::string> response;
  471. if(bulkReceiveResponse(response)) {
  472. if(response.first == 229) {
  473. port = 0;
  474. std::string::size_type leftParen = response.second.find("(");
  475. std::string::size_type rightParen = response.second.find(")");
  476. if(leftParen == std::string::npos || rightParen == std::string::npos ||
  477. leftParen > rightParen) {
  478. return response.first;
  479. }
  480. std::vector<std::string> rd;
  481. util::split(response.second.substr(leftParen+1, rightParen-(leftParen+1)),
  482. std::back_inserter(rd), "|", true, true);
  483. uint32_t portTemp = 0;
  484. if(rd.size() == 5 && util::parseUIntNoThrow(portTemp, rd[3])) {
  485. if(0 < portTemp && portTemp <= UINT16_MAX) {
  486. port = portTemp;
  487. }
  488. }
  489. }
  490. return response.first;
  491. } else {
  492. return 0;
  493. }
  494. }
  495. unsigned int FtpConnection::receivePasvResponse
  496. (std::pair<std::string, uint16_t>& dest)
  497. {
  498. std::pair<unsigned int, std::string> response;
  499. if(bulkReceiveResponse(response)) {
  500. if(response.first == 227) {
  501. // we assume the format of response is "227 Entering Passive
  502. // Mode (h1,h2,h3,h4,p1,p2)."
  503. unsigned int h1, h2, h3, h4, p1, p2;
  504. std::string::size_type p = response.second.find("(");
  505. if(p >= 4) {
  506. sscanf(response.second.substr(response.second.find("(")).c_str(),
  507. "(%u,%u,%u,%u,%u,%u).",
  508. &h1, &h2, &h3, &h4, &p1, &p2);
  509. // ip address
  510. dest.first = util::uitos(h1);
  511. dest.first += A2STR::DOT_C;
  512. dest.first += util::uitos(h2);
  513. dest.first += A2STR::DOT_C;
  514. dest.first += util::uitos(h3);
  515. dest.first += A2STR::DOT_C;
  516. dest.first += util::uitos(h4);
  517. // port number
  518. dest.second = 256*p1+p2;
  519. } else {
  520. throw DL_RETRY_EX(EX_INVALID_RESPONSE);
  521. }
  522. }
  523. return response.first;
  524. } else {
  525. return 0;
  526. }
  527. }
  528. unsigned int FtpConnection::receivePwdResponse(std::string& pwd)
  529. {
  530. std::pair<unsigned int, std::string> response;
  531. if(bulkReceiveResponse(response)) {
  532. if(response.first == 257) {
  533. std::string::size_type first;
  534. std::string::size_type last;
  535. if((first = response.second.find("\"")) != std::string::npos &&
  536. (last = response.second.find("\"", ++first)) != std::string::npos) {
  537. pwd = response.second.substr(first, last-first);
  538. } else {
  539. throw DL_ABORT_EX(EX_INVALID_RESPONSE);
  540. }
  541. }
  542. return response.first;
  543. } else {
  544. return 0;
  545. }
  546. }
  547. void FtpConnection::setBaseWorkingDir(const std::string& baseWorkingDir)
  548. {
  549. baseWorkingDir_ = baseWorkingDir;
  550. }
  551. const std::string& FtpConnection::getUser() const
  552. {
  553. return authConfig_->getUser();
  554. }
  555. } // namespace aria2