FtpConnection.cc 14 KB

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