FtpConnection.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  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()
  134. {
  135. if(_socketBuffer.sendBufferIsEmpty()) {
  136. if(_logger->info()) {
  137. _logger->info("CUID#%s - Using base working directory '%s'",
  138. util::itos(_cuid).c_str(), _baseWorkingDir.c_str());
  139. }
  140. std::string request = "CWD ";
  141. if(_baseWorkingDir != "/") {
  142. request += _baseWorkingDir;
  143. }
  144. request += util::percentDecode(_req->getDir());
  145. request += "\r\n";
  146. if(_logger->info()) {
  147. _logger->info(MSG_SENDING_REQUEST,
  148. util::itos(_cuid).c_str(),request.c_str());
  149. }
  150. _socketBuffer.pushStr(request);
  151. }
  152. _socketBuffer.send();
  153. return _socketBuffer.sendBufferIsEmpty();
  154. }
  155. bool FtpConnection::sendMdtm()
  156. {
  157. if(_socketBuffer.sendBufferIsEmpty()) {
  158. std::string request = "MDTM ";
  159. request += util::percentDecode(_req->getFile());
  160. request += "\r\n";
  161. if(_logger->info()) {
  162. _logger->info(MSG_SENDING_REQUEST,
  163. util::itos(_cuid).c_str(), request.c_str());
  164. }
  165. _socketBuffer.pushStr(request);
  166. }
  167. _socketBuffer.send();
  168. return _socketBuffer.sendBufferIsEmpty();
  169. }
  170. bool FtpConnection::sendSize()
  171. {
  172. if(_socketBuffer.sendBufferIsEmpty()) {
  173. std::string request = "SIZE ";
  174. request += util::percentDecode(_req->getFile());
  175. request += "\r\n";
  176. if(_logger->info()) {
  177. _logger->info(MSG_SENDING_REQUEST,
  178. util::itos(_cuid).c_str(), request.c_str());
  179. }
  180. _socketBuffer.pushStr(request);
  181. }
  182. _socketBuffer.send();
  183. return _socketBuffer.sendBufferIsEmpty();
  184. }
  185. bool FtpConnection::sendPasv()
  186. {
  187. if(_socketBuffer.sendBufferIsEmpty()) {
  188. static const std::string request("PASV\r\n");
  189. if(_logger->info()) {
  190. _logger->info(MSG_SENDING_REQUEST,
  191. util::itos(_cuid).c_str(), request.c_str());
  192. }
  193. _socketBuffer.pushStr(request);
  194. }
  195. _socketBuffer.send();
  196. return _socketBuffer.sendBufferIsEmpty();
  197. }
  198. SharedHandle<SocketCore> FtpConnection::createServerSocket()
  199. {
  200. SharedHandle<SocketCore> serverSocket(new SocketCore());
  201. serverSocket->bind(0);
  202. serverSocket->beginListen();
  203. serverSocket->setNonBlockingMode();
  204. return serverSocket;
  205. }
  206. bool FtpConnection::sendPort(const SharedHandle<SocketCore>& serverSocket)
  207. {
  208. if(_socketBuffer.sendBufferIsEmpty()) {
  209. std::pair<std::string, uint16_t> addrinfo;
  210. _socket->getAddrInfo(addrinfo);
  211. unsigned int ipaddr[4];
  212. sscanf(addrinfo.first.c_str(), "%u.%u.%u.%u",
  213. &ipaddr[0], &ipaddr[1], &ipaddr[2], &ipaddr[3]);
  214. serverSocket->getAddrInfo(addrinfo);
  215. std::string request = "PORT ";
  216. request += util::uitos(ipaddr[0]);
  217. request += ",";
  218. request += util::uitos(ipaddr[1]);
  219. request += ",";
  220. request += util::uitos(ipaddr[2]);
  221. request += ",";
  222. request += util::uitos(ipaddr[3]);
  223. request += ",";
  224. request += util::uitos(addrinfo.second/256);
  225. request += ",";
  226. request += util::uitos(addrinfo.second%256);
  227. request += "\r\n";
  228. if(_logger->info()) {
  229. _logger->info(MSG_SENDING_REQUEST,
  230. util::itos(_cuid).c_str(), request.c_str());
  231. }
  232. _socketBuffer.pushStr(request);
  233. }
  234. _socketBuffer.send();
  235. return _socketBuffer.sendBufferIsEmpty();
  236. }
  237. bool FtpConnection::sendRest(const SharedHandle<Segment>& segment)
  238. {
  239. if(_socketBuffer.sendBufferIsEmpty()) {
  240. std::string request = "REST ";
  241. if(segment.isNull()) {
  242. request += "0";
  243. } else {
  244. request += util::itos(segment->getPositionToWrite());
  245. }
  246. request += "\r\n";
  247. if(_logger->info()) {
  248. _logger->info(MSG_SENDING_REQUEST,
  249. util::itos(_cuid).c_str(), request.c_str());
  250. }
  251. _socketBuffer.pushStr(request);
  252. }
  253. _socketBuffer.send();
  254. return _socketBuffer.sendBufferIsEmpty();
  255. }
  256. bool FtpConnection::sendRetr()
  257. {
  258. if(_socketBuffer.sendBufferIsEmpty()) {
  259. std::string request = "RETR ";
  260. request += util::percentDecode(_req->getFile());
  261. request += "\r\n";
  262. if(_logger->info()) {
  263. _logger->info(MSG_SENDING_REQUEST,
  264. util::itos(_cuid).c_str(), request.c_str());
  265. }
  266. _socketBuffer.pushStr(request);
  267. }
  268. _socketBuffer.send();
  269. return _socketBuffer.sendBufferIsEmpty();
  270. }
  271. unsigned int FtpConnection::getStatus(const std::string& response) const
  272. {
  273. unsigned int status;
  274. // When the response is not like "%u %*s",
  275. // we return 0.
  276. if(response.find_first_not_of("0123456789") != 3
  277. || !(response.find(" ") == 3 || response.find("-") == 3)) {
  278. return 0;
  279. }
  280. if(sscanf(response.c_str(), "%u %*s", &status) == 1) {
  281. return status;
  282. } else {
  283. return 0;
  284. }
  285. }
  286. // Returns the length of the reponse if the whole response has been received.
  287. // The length includes \r\n.
  288. // If the whole response has not been received, then returns std::string::npos.
  289. std::string::size_type
  290. FtpConnection::findEndOfResponse(unsigned int status,
  291. const std::string& buf) const
  292. {
  293. if(buf.size() <= 4) {
  294. return std::string::npos;
  295. }
  296. // if 4th character of buf is '-', then multi line response is expected.
  297. if(buf.at(3) == '-') {
  298. // multi line response
  299. std::string::size_type p;
  300. std::string endPattern = A2STR::CRLF;
  301. endPattern += util::uitos(status);
  302. endPattern += " ";
  303. p = buf.find(endPattern);
  304. if(p == std::string::npos) {
  305. return std::string::npos;
  306. }
  307. p = buf.find(A2STR::CRLF, p+6);
  308. if(p == std::string::npos) {
  309. return std::string::npos;
  310. } else {
  311. return p+2;
  312. }
  313. } else {
  314. // single line response
  315. std::string::size_type p = buf.find(A2STR::CRLF);
  316. if(p == std::string::npos) {
  317. return std::string::npos;
  318. } else {
  319. return p+2;
  320. }
  321. }
  322. }
  323. bool FtpConnection::bulkReceiveResponse
  324. (std::pair<unsigned int, std::string>& response)
  325. {
  326. char buf[1024];
  327. while(_socket->isReadable(0)) {
  328. size_t size = sizeof(buf);
  329. _socket->readData(buf, size);
  330. if(size == 0) {
  331. if(_socket->wantRead() || _socket->wantWrite()) {
  332. return false;
  333. }
  334. throw DL_RETRY_EX(EX_GOT_EOF);
  335. }
  336. if(_strbuf.size()+size > MAX_RECV_BUFFER) {
  337. throw DL_RETRY_EX
  338. (StringFormat("Max FTP recv buffer reached. length=%lu",
  339. static_cast<unsigned long>(_strbuf.size()+size)).str());
  340. }
  341. _strbuf.append(&buf[0], &buf[size]);
  342. }
  343. unsigned int status;
  344. if(_strbuf.size() >= 4) {
  345. status = getStatus(_strbuf);
  346. if(status == 0) {
  347. throw DL_ABORT_EX(EX_INVALID_RESPONSE);
  348. }
  349. } else {
  350. return false;
  351. }
  352. std::string::size_type length;
  353. if((length = findEndOfResponse(status, _strbuf)) != std::string::npos) {
  354. response.first = status;
  355. response.second = _strbuf.substr(0, length);
  356. if(_logger->info()) {
  357. _logger->info(MSG_RECEIVE_RESPONSE,
  358. util::itos(_cuid).c_str(), response.second.c_str());
  359. }
  360. _strbuf.erase(0, length);
  361. return true;
  362. } else {
  363. // didn't receive response fully.
  364. return false;
  365. }
  366. }
  367. unsigned int FtpConnection::receiveResponse()
  368. {
  369. std::pair<unsigned int, std::string> response;
  370. if(bulkReceiveResponse(response)) {
  371. return response.first;
  372. } else {
  373. return 0;
  374. }
  375. }
  376. #ifdef __MINGW32__
  377. # define LONGLONG_PRINTF "%I64d"
  378. # define ULONGLONG_PRINTF "%I64u"
  379. # define LONGLONG_SCANF "%I64d"
  380. # define ULONGLONG_SCANF "%I64u"
  381. #else
  382. # define LONGLONG_PRINTF "%lld"
  383. # define ULONGLONG_PRINTF "%llu"
  384. # define LONGLONG_SCANF "%Ld"
  385. // Mac OSX uses "%llu" for 64bits integer.
  386. # define ULONGLONG_SCANF "%Lu"
  387. #endif // __MINGW32__
  388. unsigned int FtpConnection::receiveSizeResponse(uint64_t& size)
  389. {
  390. std::pair<unsigned int, std::string> response;
  391. if(bulkReceiveResponse(response)) {
  392. if(response.first == 213) {
  393. std::pair<std::string, std::string> rp = util::split(response.second," ");
  394. size = util::parseULLInt(rp.second);
  395. }
  396. return response.first;
  397. } else {
  398. return 0;
  399. }
  400. }
  401. unsigned int FtpConnection::receiveMdtmResponse(Time& time)
  402. {
  403. // MDTM command, specified in RFC3659.
  404. std::pair<unsigned int, std::string> response;
  405. if(bulkReceiveResponse(response)) {
  406. if(response.first == 213) {
  407. char buf[15]; // YYYYMMDDhhmmss+\0, milli second part is dropped.
  408. sscanf(response.second.c_str(), "%*u %14s", buf);
  409. if(strlen(buf) == 14) {
  410. // We don't use Time::parse(buf,"%Y%m%d%H%M%S") here because Mac OS X
  411. // and included strptime doesn't parse data for this format.
  412. struct tm tm;
  413. memset(&tm, 0, sizeof(tm));
  414. tm.tm_sec = util::parseInt(&buf[12]);
  415. buf[12] = '\0';
  416. tm.tm_min = util::parseInt(&buf[10]);
  417. buf[10] = '\0';
  418. tm.tm_hour = util::parseInt(&buf[8]);
  419. buf[8] = '\0';
  420. tm.tm_mday = util::parseInt(&buf[6]);
  421. buf[6] = '\0';
  422. tm.tm_mon = util::parseInt(&buf[4])-1;
  423. buf[4] = '\0';
  424. tm.tm_year = util::parseInt(&buf[0])-1900;
  425. time = Time(timegm(&tm));
  426. } else {
  427. time = Time::null();
  428. }
  429. }
  430. return response.first;
  431. } else {
  432. return 0;
  433. }
  434. }
  435. unsigned int FtpConnection::receivePasvResponse
  436. (std::pair<std::string, uint16_t>& dest)
  437. {
  438. std::pair<unsigned int, std::string> response;
  439. if(bulkReceiveResponse(response)) {
  440. if(response.first == 227) {
  441. // we assume the format of response is "227 Entering Passive
  442. // Mode (h1,h2,h3,h4,p1,p2)."
  443. unsigned int h1, h2, h3, h4, p1, p2;
  444. std::string::size_type p = response.second.find("(");
  445. if(p >= 4) {
  446. sscanf(response.second.substr(response.second.find("(")).c_str(),
  447. "(%u,%u,%u,%u,%u,%u).",
  448. &h1, &h2, &h3, &h4, &p1, &p2);
  449. // ip address
  450. dest.first = util::uitos(h1);
  451. dest.first += A2STR::DOT_C;
  452. dest.first += util::uitos(h2);
  453. dest.first += A2STR::DOT_C;
  454. dest.first += util::uitos(h3);
  455. dest.first += A2STR::DOT_C;
  456. dest.first += util::uitos(h4);
  457. // port number
  458. dest.second = 256*p1+p2;
  459. } else {
  460. throw DL_RETRY_EX(EX_INVALID_RESPONSE);
  461. }
  462. }
  463. return response.first;
  464. } else {
  465. return 0;
  466. }
  467. }
  468. unsigned int FtpConnection::receivePwdResponse(std::string& pwd)
  469. {
  470. std::pair<unsigned int, std::string> response;
  471. if(bulkReceiveResponse(response)) {
  472. if(response.first == 257) {
  473. std::string::size_type first;
  474. std::string::size_type last;
  475. if((first = response.second.find("\"")) != std::string::npos &&
  476. (last = response.second.find("\"", ++first)) != std::string::npos) {
  477. pwd = response.second.substr(first, last-first);
  478. } else {
  479. throw DL_ABORT_EX(EX_INVALID_RESPONSE);
  480. }
  481. }
  482. return response.first;
  483. } else {
  484. return 0;
  485. }
  486. }
  487. void FtpConnection::setBaseWorkingDir(const std::string& baseWorkingDir)
  488. {
  489. _baseWorkingDir = baseWorkingDir;
  490. }
  491. const std::string& FtpConnection::getUser() const
  492. {
  493. return _authConfig->getUser();
  494. }
  495. } // namespace aria2