summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2010-07-30 14:45:35 +0000
committerTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2010-07-30 14:45:35 +0000
commit7958ce43662d8dd070f2ca71c06ca630198568ef (patch)
tree023ab57b1c8f575ef3b55895447d9bba49474e26 /src
parent20b7c8cd0e058ba74bc311c855db6111726276ef (diff)
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
Diffstat (limited to 'src')
-rw-r--r--src/FtpConnection.cc72
-rw-r--r--src/FtpConnection.h3
-rw-r--r--src/FtpNegotiationCommand.cc127
-rw-r--r--src/FtpNegotiationCommand.h15
-rw-r--r--src/SocketCore.cc17
-rw-r--r--src/SocketCore.h15
6 files changed, 234 insertions, 15 deletions
diff --git a/src/FtpConnection.cc b/src/FtpConnection.cc
index 8a54f935..d0b22e33 100644
--- a/src/FtpConnection.cc
+++ b/src/FtpConnection.cc
@@ -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)
{
diff --git a/src/FtpConnection.h b/src/FtpConnection.h
index 891b6feb..463c2af7 100644
--- a/src/FtpConnection.h
+++ b/src/FtpConnection.h
@@ -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);
diff --git a/src/FtpNegotiationCommand.cc b/src/FtpNegotiationCommand.cc
index 2c070274..89bf5211 100644
--- a/src/FtpNegotiationCommand.cc
+++ b/src/FtpNegotiationCommand.cc
@@ -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:
diff --git a/src/FtpNegotiationCommand.h b/src/FtpNegotiationCommand.h
index badd14a3..bf80f33b 100644
--- a/src/FtpNegotiationCommand.h
+++ b/src/FtpNegotiationCommand.h
@@ -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();
diff --git a/src/SocketCore.cc b/src/SocketCore.cc
index cb6602f8..7d8b076b 100644
--- a/src/SocketCore.cc
+++ b/src/SocketCore.cc
@@ -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
diff --git a/src/SocketCore.h b/src/SocketCore.h
index 67585bc3..7de5a0eb 100644
--- a/src/SocketCore.h
+++ b/src/SocketCore.h
@@ -160,6 +160,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.
*/