mirror of https://github.com/aria2/aria2
2010-07-30 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Added FTP EPSV and EPRT command support. aria2 issues these commands when address family of local socket is AF_INET6. * src/FtpConnection.cc * src/FtpConnection.h * src/FtpNegotiationCommand.cc * src/FtpNegotiationCommand.h * src/SocketCore.cc * src/SocketCore.h * test/FtpConnectionTest.ccpull/1/head
parent
20b7c8cd0e
commit
7958ce4366
12
ChangeLog
12
ChangeLog
|
@ -1,3 +1,15 @@
|
|||
2010-07-30 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
||||
|
||||
Added FTP EPSV and EPRT command support. aria2 issues these
|
||||
commands when address family of local socket is AF_INET6.
|
||||
* src/FtpConnection.cc
|
||||
* src/FtpConnection.h
|
||||
* src/FtpNegotiationCommand.cc
|
||||
* src/FtpNegotiationCommand.h
|
||||
* src/SocketCore.cc
|
||||
* src/SocketCore.h
|
||||
* test/FtpConnectionTest.cc
|
||||
|
||||
2010-07-30 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
||||
|
||||
Fixed the bug that if hostname is numeric,
|
||||
|
|
|
@ -190,6 +190,20 @@ bool FtpConnection::sendSize()
|
|||
return socketBuffer_.sendBufferIsEmpty();
|
||||
}
|
||||
|
||||
bool FtpConnection::sendEpsv()
|
||||
{
|
||||
if(socketBuffer_.sendBufferIsEmpty()) {
|
||||
static const std::string request("EPSV\r\n");
|
||||
if(logger_->info()) {
|
||||
logger_->info(MSG_SENDING_REQUEST,
|
||||
util::itos(cuid_).c_str(), request.c_str());
|
||||
}
|
||||
socketBuffer_.pushStr(request);
|
||||
}
|
||||
socketBuffer_.send();
|
||||
return socketBuffer_.sendBufferIsEmpty();
|
||||
}
|
||||
|
||||
bool FtpConnection::sendPasv()
|
||||
{
|
||||
if(socketBuffer_.sendBufferIsEmpty()) {
|
||||
|
@ -206,13 +220,41 @@ bool FtpConnection::sendPasv()
|
|||
|
||||
SharedHandle<SocketCore> FtpConnection::createServerSocket()
|
||||
{
|
||||
std::pair<std::string, uint16_t> addrinfo;
|
||||
socket_->getAddrInfo(addrinfo);
|
||||
SharedHandle<SocketCore> serverSocket(new SocketCore());
|
||||
serverSocket->bind(0);
|
||||
serverSocket->bind(addrinfo.first, 0);
|
||||
serverSocket->beginListen();
|
||||
serverSocket->setNonBlockingMode();
|
||||
return serverSocket;
|
||||
}
|
||||
|
||||
bool FtpConnection::sendEprt(const SharedHandle<SocketCore>& serverSocket)
|
||||
{
|
||||
if(socketBuffer_.sendBufferIsEmpty()) {
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
serverSocket->getAddrInfo(sockaddr, len);
|
||||
std::pair<std::string, uint16_t> addrinfo = util::getNumericNameInfo
|
||||
(reinterpret_cast<const struct sockaddr*>(&sockaddr), len);
|
||||
std::string request = "EPRT ";
|
||||
request += "|";
|
||||
request += util::itos(sockaddr.ss_family == AF_INET?1:2);
|
||||
request += "|";
|
||||
request += addrinfo.first;
|
||||
request += "|";
|
||||
request += util::uitos(addrinfo.second);
|
||||
request += "|\r\n";
|
||||
if(logger_->info()) {
|
||||
logger_->info(MSG_SENDING_REQUEST,
|
||||
util::itos(cuid_).c_str(), request.c_str());
|
||||
}
|
||||
socketBuffer_.pushStr(request);
|
||||
}
|
||||
socketBuffer_.send();
|
||||
return socketBuffer_.sendBufferIsEmpty();
|
||||
}
|
||||
|
||||
bool FtpConnection::sendPort(const SharedHandle<SocketCore>& serverSocket)
|
||||
{
|
||||
if(socketBuffer_.sendBufferIsEmpty()) {
|
||||
|
@ -453,6 +495,34 @@ unsigned int FtpConnection::receiveMdtmResponse(Time& time)
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int FtpConnection::receiveEpsvResponse(uint16_t& port)
|
||||
{
|
||||
std::pair<unsigned int, std::string> response;
|
||||
if(bulkReceiveResponse(response)) {
|
||||
if(response.first == 229) {
|
||||
port = 0;
|
||||
std::string::size_type leftParen = response.second.find("(");
|
||||
std::string::size_type rightParen = response.second.find(")");
|
||||
if(leftParen == std::string::npos || rightParen == std::string::npos ||
|
||||
leftParen > rightParen) {
|
||||
return response.first;
|
||||
}
|
||||
std::vector<std::string> rd;
|
||||
util::split(response.second.substr(leftParen+1, rightParen-(leftParen+1)),
|
||||
std::back_inserter(rd), "|", true, true);
|
||||
uint32_t portTemp = 0;
|
||||
if(rd.size() == 5 && util::parseUIntNoThrow(portTemp, rd[3])) {
|
||||
if(0 < portTemp && portTemp <= UINT16_MAX) {
|
||||
port = portTemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return response.first;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int FtpConnection::receivePasvResponse
|
||||
(std::pair<std::string, uint16_t>& dest)
|
||||
{
|
||||
|
|
|
@ -95,8 +95,10 @@ public:
|
|||
bool sendCwd(const std::string& dir);
|
||||
bool sendMdtm();
|
||||
bool sendSize();
|
||||
bool sendEpsv();
|
||||
bool sendPasv();
|
||||
SharedHandle<SocketCore> createServerSocket();
|
||||
bool sendEprt(const SharedHandle<SocketCore>& serverSocket);
|
||||
bool sendPort(const SharedHandle<SocketCore>& serverSocket);
|
||||
bool sendRest(const SharedHandle<Segment>& segment);
|
||||
bool sendRetr();
|
||||
|
@ -110,6 +112,7 @@ public:
|
|||
// date cannot be parsed, then assign Time::null() to given time.
|
||||
// If reply is not received yet, returns 0.
|
||||
unsigned int receiveMdtmResponse(Time& time);
|
||||
unsigned int receiveEpsvResponse(uint16_t& port);
|
||||
unsigned int receivePasvResponse(std::pair<std::string, uint16_t>& dest);
|
||||
unsigned int receivePwdResponse(std::string& pwd);
|
||||
|
||||
|
|
|
@ -126,9 +126,9 @@ bool FtpNegotiationCommand::executeInternal() {
|
|||
return true;
|
||||
} else if(sequence_ == SEQ_FILE_PREPARATION) {
|
||||
if(getOption()->getAsBool(PREF_FTP_PASV)) {
|
||||
sequence_ = SEQ_SEND_PASV;
|
||||
sequence_ = SEQ_PREPARE_PASV;
|
||||
} else {
|
||||
sequence_ = SEQ_PREPARE_SERVER_SOCKET;
|
||||
sequence_ = SEQ_PREPARE_PORT;
|
||||
}
|
||||
return false;
|
||||
} else if(sequence_ == SEQ_EXIT) {
|
||||
|
@ -391,9 +391,9 @@ bool FtpNegotiationCommand::onFileSizeDetermined(uint64_t totalLength)
|
|||
if(totalLength == 0) {
|
||||
|
||||
if(getOption()->getAsBool(PREF_FTP_PASV)) {
|
||||
sequence_ = SEQ_SEND_PASV;
|
||||
sequence_ = SEQ_PREPARE_PASV;
|
||||
} else {
|
||||
sequence_ = SEQ_PREPARE_SERVER_SOCKET;
|
||||
sequence_ = SEQ_PREPARE_PORT;
|
||||
}
|
||||
|
||||
if(getOption()->getAsBool(PREF_DRY_RUN)) {
|
||||
|
@ -514,9 +514,9 @@ bool FtpNegotiationCommand::recvSize() {
|
|||
// wrong file to be downloaded if user-specified URL is wrong.
|
||||
}
|
||||
if(getOption()->getAsBool(PREF_FTP_PASV)) {
|
||||
sequence_ = SEQ_SEND_PASV;
|
||||
sequence_ = SEQ_PREPARE_PASV;
|
||||
} else {
|
||||
sequence_ = SEQ_PREPARE_SERVER_SOCKET;
|
||||
sequence_ = SEQ_PREPARE_PORT;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -526,6 +526,22 @@ void FtpNegotiationCommand::afterFileAllocation()
|
|||
setReadCheckSocket(getSocket());
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::preparePort() {
|
||||
afterFileAllocation();
|
||||
if(getSocket()->getAddressFamily() == AF_INET6) {
|
||||
sequence_ = SEQ_PREPARE_SERVER_SOCKET_EPRT;
|
||||
} else {
|
||||
sequence_ = SEQ_PREPARE_SERVER_SOCKET;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::prepareServerSocketEprt() {
|
||||
serverSocket_ = ftp_->createServerSocket();
|
||||
sequence_ = SEQ_SEND_EPRT;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::prepareServerSocket()
|
||||
{
|
||||
serverSocket_ = ftp_->createServerSocket();
|
||||
|
@ -533,8 +549,30 @@ bool FtpNegotiationCommand::prepareServerSocket()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::sendEprt() {
|
||||
if(ftp_->sendEprt(serverSocket_)) {
|
||||
disableWriteCheckSocket();
|
||||
sequence_ = SEQ_RECV_EPRT;
|
||||
} else {
|
||||
setWriteCheckSocket(getSocket());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::recvEprt() {
|
||||
unsigned int status = ftp_->receiveResponse();
|
||||
if(status == 0) {
|
||||
return false;
|
||||
}
|
||||
if(status == 200) {
|
||||
sequence_ = SEQ_SEND_REST;
|
||||
} else {
|
||||
sequence_ = SEQ_PREPARE_SERVER_SOCKET;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::sendPort() {
|
||||
afterFileAllocation();
|
||||
if(ftp_->sendPort(serverSocket_)) {
|
||||
disableWriteCheckSocket();
|
||||
sequence_ = SEQ_RECV_PORT;
|
||||
|
@ -556,8 +594,45 @@ bool FtpNegotiationCommand::recvPort() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::sendPasv() {
|
||||
bool FtpNegotiationCommand::preparePasv() {
|
||||
afterFileAllocation();
|
||||
if(getSocket()->getAddressFamily() == AF_INET6) {
|
||||
sequence_ = SEQ_SEND_EPSV;
|
||||
} else {
|
||||
sequence_ = SEQ_SEND_PASV;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::sendEpsv() {
|
||||
if(ftp_->sendEpsv()) {
|
||||
disableWriteCheckSocket();
|
||||
sequence_ = SEQ_RECV_EPSV;
|
||||
} else {
|
||||
setWriteCheckSocket(getSocket());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::recvEpsv() {
|
||||
uint16_t port;
|
||||
unsigned int status = ftp_->receiveEpsvResponse(port);
|
||||
if(status == 0) {
|
||||
return false;
|
||||
}
|
||||
if(status == 229) {
|
||||
std::pair<std::string, uint16_t> peerInfo;
|
||||
getSocket()->getPeerInfo(peerInfo);
|
||||
peerInfo.second = port;
|
||||
dataConnAddr_ = peerInfo;
|
||||
return preparePasvConnect();
|
||||
} else {
|
||||
sequence_ = SEQ_SEND_PASV;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::sendPasv() {
|
||||
if(ftp_->sendPasv()) {
|
||||
disableWriteCheckSocket();
|
||||
sequence_ = SEQ_RECV_PASV;
|
||||
|
@ -576,20 +651,26 @@ bool FtpNegotiationCommand::recvPasv() {
|
|||
if(status != 227) {
|
||||
throw DL_ABORT_EX(StringFormat(EX_BAD_STATUS, status).str());
|
||||
}
|
||||
// TODO Should we check to see that dest.first is not in noProxy list?
|
||||
dataConnAddr_ = dest;
|
||||
|
||||
return preparePasvConnect();
|
||||
}
|
||||
|
||||
bool FtpNegotiationCommand::preparePasvConnect() {
|
||||
// TODO Should we check to see that dataConnAddr_.first is not in
|
||||
// noProxy list?
|
||||
if(isProxyDefined()) {
|
||||
dataConnAddr_ = dest;
|
||||
sequence_ = SEQ_RESOLVE_PROXY;
|
||||
return true;
|
||||
} else {
|
||||
// make a data connection to the server.
|
||||
if(getLogger()->info()) {
|
||||
getLogger()->info(MSG_CONNECTING_TO_SERVER, util::itos(getCuid()).c_str(),
|
||||
dest.first.c_str(),
|
||||
dest.second);
|
||||
dataConnAddr_.first.c_str(),
|
||||
dataConnAddr_.second);
|
||||
}
|
||||
dataSocket_.reset(new SocketCore());
|
||||
dataSocket_->establishConnection(dest.first, dest.second);
|
||||
dataSocket_->establishConnection(dataConnAddr_.first, dataConnAddr_.second);
|
||||
disableReadCheckSocket();
|
||||
setWriteCheckSocket(dataSocket_);
|
||||
sequence_ = SEQ_SEND_REST_PASV;
|
||||
|
@ -691,6 +772,12 @@ bool FtpNegotiationCommand::recvTunnelResponse()
|
|||
|
||||
bool FtpNegotiationCommand::sendRestPasv(const SharedHandle<Segment>& segment) {
|
||||
//dataSocket_->setBlockingMode();
|
||||
// Check connection is made properly
|
||||
if(dataSocket_->isReadable(0)) {
|
||||
std::string error = dataSocket_->getSocketError();
|
||||
throw DL_ABORT_EX
|
||||
(StringFormat(MSG_ESTABLISHING_CONNECTION_FAILED, error.c_str()).str());
|
||||
}
|
||||
setReadCheckSocket(getSocket());
|
||||
disableWriteCheckSocket();
|
||||
return sendRest(segment);
|
||||
|
@ -803,12 +890,26 @@ bool FtpNegotiationCommand::processSequence
|
|||
return sendSize();
|
||||
case SEQ_RECV_SIZE:
|
||||
return recvSize();
|
||||
case SEQ_PREPARE_PORT:
|
||||
return preparePort();
|
||||
case SEQ_PREPARE_SERVER_SOCKET_EPRT:
|
||||
return prepareServerSocketEprt();
|
||||
case SEQ_SEND_EPRT:
|
||||
return sendEprt();
|
||||
case SEQ_RECV_EPRT:
|
||||
return recvEprt();
|
||||
case SEQ_PREPARE_SERVER_SOCKET:
|
||||
return prepareServerSocket();
|
||||
case SEQ_SEND_PORT:
|
||||
return sendPort();
|
||||
case SEQ_RECV_PORT:
|
||||
return recvPort();
|
||||
case SEQ_PREPARE_PASV:
|
||||
return preparePasv();
|
||||
case SEQ_SEND_EPSV:
|
||||
return sendEpsv();
|
||||
case SEQ_RECV_EPSV:
|
||||
return recvEpsv();
|
||||
case SEQ_SEND_PASV:
|
||||
return sendPasv();
|
||||
case SEQ_RECV_PASV:
|
||||
|
|
|
@ -62,9 +62,16 @@ public:
|
|||
SEQ_RECV_MDTM,
|
||||
SEQ_SEND_SIZE,
|
||||
SEQ_RECV_SIZE,
|
||||
SEQ_PREPARE_PORT,
|
||||
SEQ_PREPARE_SERVER_SOCKET_EPRT,
|
||||
SEQ_SEND_EPRT,
|
||||
SEQ_RECV_EPRT,
|
||||
SEQ_PREPARE_SERVER_SOCKET,
|
||||
SEQ_SEND_PORT,
|
||||
SEQ_RECV_PORT,
|
||||
SEQ_PREPARE_PASV,
|
||||
SEQ_SEND_EPSV,
|
||||
SEQ_RECV_EPSV,
|
||||
SEQ_SEND_PASV,
|
||||
SEQ_RECV_PASV,
|
||||
SEQ_RESOLVE_PROXY,
|
||||
|
@ -100,11 +107,19 @@ private:
|
|||
bool recvMdtm();
|
||||
bool sendSize();
|
||||
bool recvSize();
|
||||
bool preparePort();
|
||||
bool prepareServerSocketEprt();
|
||||
bool sendEprt();
|
||||
bool recvEprt();
|
||||
bool prepareServerSocket();
|
||||
bool sendPort();
|
||||
bool recvPort();
|
||||
bool preparePasv();
|
||||
bool sendEpsv();
|
||||
bool recvEpsv();
|
||||
bool sendPasv();
|
||||
bool recvPasv();
|
||||
bool preparePasvConnect();
|
||||
bool resolveProxy();
|
||||
bool sendTunnelRequest();
|
||||
bool recvTunnelResponse();
|
||||
|
|
|
@ -341,11 +341,26 @@ void SocketCore::getAddrInfo(std::pair<std::string, uint16_t>& addrinfo) const
|
|||
{
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
getAddrInfo(sockaddr, len);
|
||||
addrinfo = util::getNumericNameInfo
|
||||
(reinterpret_cast<const struct sockaddr*>(&sockaddr), len);
|
||||
}
|
||||
|
||||
void SocketCore::getAddrInfo
|
||||
(struct sockaddr_storage& sockaddr, socklen_t& len) const
|
||||
{
|
||||
struct sockaddr* addrp = reinterpret_cast<struct sockaddr*>(&sockaddr);
|
||||
if(getsockname(sockfd_, addrp, &len) == -1) {
|
||||
throw DL_ABORT_EX(StringFormat(EX_SOCKET_GET_NAME, errorMsg()).str());
|
||||
}
|
||||
addrinfo = util::getNumericNameInfo(addrp, len);
|
||||
}
|
||||
|
||||
int SocketCore::getAddressFamily() const
|
||||
{
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
getAddrInfo(sockaddr, len);
|
||||
return sockaddr.ss_family;
|
||||
}
|
||||
|
||||
void SocketCore::getPeerInfo(std::pair<std::string, uint16_t>& peerinfo) const
|
||||
|
|
|
@ -159,6 +159,21 @@ public:
|
|||
*/
|
||||
void getAddrInfo(std::pair<std::string, uint16_t>& addrinfo) const;
|
||||
|
||||
/**
|
||||
* Stores address of this socket to sockaddr. len must be
|
||||
* initialized to the size of sockaddr. On success, address data is
|
||||
* stored in sockaddr and actual size of address structure is stored
|
||||
* in len.
|
||||
*/
|
||||
void getAddrInfo
|
||||
(struct sockaddr_storage& sockaddr, socklen_t& len) const;
|
||||
|
||||
/**
|
||||
* Returns address family of this socket.
|
||||
* The socket must be connected or bounded to address.
|
||||
*/
|
||||
int getAddressFamily() const;
|
||||
|
||||
/**
|
||||
* Stores peer's address and port to peerinfo.
|
||||
* @param peerinfo placeholder to store peer's address and port.
|
||||
|
|
|
@ -32,6 +32,7 @@ class FtpConnectionTest:public CppUnit::TestFixture {
|
|||
CPPUNIT_TEST(testSendSize);
|
||||
CPPUNIT_TEST(testReceiveSizeResponse);
|
||||
CPPUNIT_TEST(testSendRetr);
|
||||
CPPUNIT_TEST(testReceiveEpsvResponse);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
private:
|
||||
SharedHandle<SocketCore> serverSocket_;
|
||||
|
@ -85,6 +86,7 @@ public:
|
|||
void testSendSize();
|
||||
void testReceiveSizeResponse();
|
||||
void testSendRetr();
|
||||
void testReceiveEpsvResponse();
|
||||
};
|
||||
|
||||
|
||||
|
@ -299,4 +301,33 @@ void FtpConnectionTest::testSendRetr()
|
|||
std::string(&data[0], &data[len]));
|
||||
}
|
||||
|
||||
void FtpConnectionTest::testReceiveEpsvResponse()
|
||||
{
|
||||
serverSocket_->writeData("229 Success (|||12000|)\r\n");
|
||||
waitRead(clientSocket_);
|
||||
uint16_t port = 0;
|
||||
CPPUNIT_ASSERT_EQUAL((unsigned int)229, ftp_->receiveEpsvResponse(port));
|
||||
CPPUNIT_ASSERT_EQUAL((uint16_t)12000, port);
|
||||
|
||||
serverSocket_->writeData("229 Success |||12000|)\r\n");
|
||||
CPPUNIT_ASSERT_EQUAL((unsigned int)229, ftp_->receiveEpsvResponse(port));
|
||||
CPPUNIT_ASSERT_EQUAL((uint16_t)0, port);
|
||||
|
||||
serverSocket_->writeData("229 Success (|||12000|\r\n");
|
||||
CPPUNIT_ASSERT_EQUAL((unsigned int)229, ftp_->receiveEpsvResponse(port));
|
||||
CPPUNIT_ASSERT_EQUAL((uint16_t)0, port);
|
||||
|
||||
serverSocket_->writeData("229 Success ()|||12000|\r\n");
|
||||
CPPUNIT_ASSERT_EQUAL((unsigned int)229, ftp_->receiveEpsvResponse(port));
|
||||
CPPUNIT_ASSERT_EQUAL((uint16_t)0, port);
|
||||
|
||||
serverSocket_->writeData("229 Success )(|||12000|)\r\n");
|
||||
CPPUNIT_ASSERT_EQUAL((unsigned int)229, ftp_->receiveEpsvResponse(port));
|
||||
CPPUNIT_ASSERT_EQUAL((uint16_t)0, port);
|
||||
|
||||
serverSocket_->writeData("229 Success )(||12000|)\r\n");
|
||||
CPPUNIT_ASSERT_EQUAL((unsigned int)229, ftp_->receiveEpsvResponse(port));
|
||||
CPPUNIT_ASSERT_EQUAL((uint16_t)0, port);
|
||||
}
|
||||
|
||||
} // namespace aria2
|
||||
|
|
Loading…
Reference in New Issue