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
pull/1/head
Tatsuhiro Tsujikawa 2010-07-30 14:45:35 +00:00
parent 20b7c8cd0e
commit 7958ce4366
8 changed files with 277 additions and 15 deletions

View File

@ -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,

View File

@ -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)
{

View File

@ -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);

View File

@ -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:

View File

@ -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();

View File

@ -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

View File

@ -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.

View File

@ -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