FtpConnection.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  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(std::pair<unsigned int, std::string>& response)
  324. {
  325. char buf[1024];
  326. while(socket->isReadable(0)) {
  327. size_t size = sizeof(buf);
  328. socket->readData(buf, size);
  329. if(size == 0) {
  330. if(socket->wantRead() || socket->wantWrite()) {
  331. return false;
  332. }
  333. throw DL_RETRY_EX(EX_GOT_EOF);
  334. }
  335. if(strbuf.size()+size > MAX_RECV_BUFFER) {
  336. throw DL_RETRY_EX
  337. (StringFormat("Max FTP recv buffer reached. length=%lu",
  338. static_cast<unsigned long>(strbuf.size()+size)).str());
  339. }
  340. strbuf.append(&buf[0], &buf[size]);
  341. }
  342. unsigned int status;
  343. if(strbuf.size() >= 4) {
  344. status = getStatus(strbuf);
  345. if(status == 0) {
  346. throw DL_ABORT_EX(EX_INVALID_RESPONSE);
  347. }
  348. } else {
  349. return false;
  350. }
  351. std::string::size_type length;
  352. if((length = findEndOfResponse(status, strbuf)) != std::string::npos) {
  353. response.first = status;
  354. response.second = strbuf.substr(0, length);
  355. if(logger->info()) {
  356. logger->info(MSG_RECEIVE_RESPONSE,
  357. util::itos(cuid).c_str(), response.second.c_str());
  358. }
  359. strbuf.erase(0, length);
  360. return true;
  361. } else {
  362. // didn't receive response fully.
  363. return false;
  364. }
  365. }
  366. unsigned int FtpConnection::receiveResponse()
  367. {
  368. std::pair<unsigned int, std::string> response;
  369. if(bulkReceiveResponse(response)) {
  370. return response.first;
  371. } else {
  372. return 0;
  373. }
  374. }
  375. #ifdef __MINGW32__
  376. # define LONGLONG_PRINTF "%I64d"
  377. # define ULONGLONG_PRINTF "%I64u"
  378. # define LONGLONG_SCANF "%I64d"
  379. # define ULONGLONG_SCANF "%I64u"
  380. #else
  381. # define LONGLONG_PRINTF "%lld"
  382. # define ULONGLONG_PRINTF "%llu"
  383. # define LONGLONG_SCANF "%Ld"
  384. // Mac OSX uses "%llu" for 64bits integer.
  385. # define ULONGLONG_SCANF "%Lu"
  386. #endif // __MINGW32__
  387. unsigned int FtpConnection::receiveSizeResponse(uint64_t& size)
  388. {
  389. std::pair<unsigned int, std::string> response;
  390. if(bulkReceiveResponse(response)) {
  391. if(response.first == 213) {
  392. std::pair<std::string, std::string> rp = util::split(response.second," ");
  393. size = util::parseULLInt(rp.second);
  394. }
  395. return response.first;
  396. } else {
  397. return 0;
  398. }
  399. }
  400. unsigned int FtpConnection::receiveMdtmResponse(Time& time)
  401. {
  402. // MDTM command, specified in RFC3659.
  403. std::pair<unsigned int, std::string> response;
  404. if(bulkReceiveResponse(response)) {
  405. if(response.first == 213) {
  406. char buf[15]; // YYYYMMDDhhmmss+\0, milli second part is dropped.
  407. sscanf(response.second.c_str(), "%*u %14s", buf);
  408. if(strlen(buf) == 14) {
  409. // We don't use Time::parse(buf,"%Y%m%d%H%M%S") here because Mac OS X
  410. // and included strptime doesn't parse data for this format.
  411. struct tm tm;
  412. memset(&tm, 0, sizeof(tm));
  413. tm.tm_sec = util::parseInt(&buf[12]);
  414. buf[12] = '\0';
  415. tm.tm_min = util::parseInt(&buf[10]);
  416. buf[10] = '\0';
  417. tm.tm_hour = util::parseInt(&buf[8]);
  418. buf[8] = '\0';
  419. tm.tm_mday = util::parseInt(&buf[6]);
  420. buf[6] = '\0';
  421. tm.tm_mon = util::parseInt(&buf[4])-1;
  422. buf[4] = '\0';
  423. tm.tm_year = util::parseInt(&buf[0])-1900;
  424. time = Time(timegm(&tm));
  425. } else {
  426. time = Time::null();
  427. }
  428. }
  429. return response.first;
  430. } else {
  431. return 0;
  432. }
  433. }
  434. unsigned int FtpConnection::receivePasvResponse(std::pair<std::string, uint16_t>& dest)
  435. {
  436. std::pair<unsigned int, std::string> response;
  437. if(bulkReceiveResponse(response)) {
  438. if(response.first == 227) {
  439. // we assume the format of response is "227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)."
  440. unsigned int h1, h2, h3, h4, p1, p2;
  441. std::string::size_type p = response.second.find("(");
  442. if(p >= 4) {
  443. sscanf(response.second.substr(response.second.find("(")).c_str(),
  444. "(%u,%u,%u,%u,%u,%u).",
  445. &h1, &h2, &h3, &h4, &p1, &p2);
  446. // ip address
  447. dest.first = util::uitos(h1);
  448. dest.first += A2STR::DOT_C;
  449. dest.first += util::uitos(h2);
  450. dest.first += A2STR::DOT_C;
  451. dest.first += util::uitos(h3);
  452. dest.first += A2STR::DOT_C;
  453. dest.first += util::uitos(h4);
  454. // port number
  455. dest.second = 256*p1+p2;
  456. } else {
  457. throw DL_RETRY_EX(EX_INVALID_RESPONSE);
  458. }
  459. }
  460. return response.first;
  461. } else {
  462. return 0;
  463. }
  464. }
  465. unsigned int FtpConnection::receivePwdResponse(std::string& pwd)
  466. {
  467. std::pair<unsigned int, std::string> response;
  468. if(bulkReceiveResponse(response)) {
  469. if(response.first == 257) {
  470. std::string::size_type first;
  471. std::string::size_type last;
  472. if((first = response.second.find("\"")) != std::string::npos &&
  473. (last = response.second.find("\"", ++first)) != std::string::npos) {
  474. pwd = response.second.substr(first, last-first);
  475. } else {
  476. throw DL_ABORT_EX(EX_INVALID_RESPONSE);
  477. }
  478. }
  479. return response.first;
  480. } else {
  481. return 0;
  482. }
  483. }
  484. void FtpConnection::setBaseWorkingDir(const std::string& baseWorkingDir)
  485. {
  486. _baseWorkingDir = baseWorkingDir;
  487. }
  488. const std::string& FtpConnection::getUser() const
  489. {
  490. return _authConfig->getUser();
  491. }
  492. } // namespace aria2