Browse Source

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
Tatsuhiro Tsujikawa 15 years ago
parent
commit
7958ce4366
8 changed files with 277 additions and 15 deletions
  1. 12 0
      ChangeLog
  2. 71 1
      src/FtpConnection.cc
  3. 3 0
      src/FtpConnection.h
  4. 114 13
      src/FtpNegotiationCommand.cc
  5. 15 0
      src/FtpNegotiationCommand.h
  6. 16 1
      src/SocketCore.cc
  7. 15 0
      src/SocketCore.h
  8. 31 0
      test/FtpConnectionTest.cc

+ 12 - 0
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>
 2010-07-30  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
 
 
 	Fixed the bug that if hostname is numeric,
 	Fixed the bug that if hostname is numeric,

+ 71 - 1
src/FtpConnection.cc

@@ -190,6 +190,20 @@ bool FtpConnection::sendSize()
   return socketBuffer_.sendBufferIsEmpty();
   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()
 bool FtpConnection::sendPasv()
 {
 {
   if(socketBuffer_.sendBufferIsEmpty()) {
   if(socketBuffer_.sendBufferIsEmpty()) {
@@ -206,13 +220,41 @@ bool FtpConnection::sendPasv()
 
 
 SharedHandle<SocketCore> FtpConnection::createServerSocket()
 SharedHandle<SocketCore> FtpConnection::createServerSocket()
 {
 {
+  std::pair<std::string, uint16_t> addrinfo;
+  socket_->getAddrInfo(addrinfo);
   SharedHandle<SocketCore> serverSocket(new SocketCore());
   SharedHandle<SocketCore> serverSocket(new SocketCore());
-  serverSocket->bind(0);
+  serverSocket->bind(addrinfo.first, 0);
   serverSocket->beginListen();
   serverSocket->beginListen();
   serverSocket->setNonBlockingMode();
   serverSocket->setNonBlockingMode();
   return serverSocket;
   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)
 bool FtpConnection::sendPort(const SharedHandle<SocketCore>& serverSocket)
 {
 {
   if(socketBuffer_.sendBufferIsEmpty()) {
   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
 unsigned int FtpConnection::receivePasvResponse
 (std::pair<std::string, uint16_t>& dest)
 (std::pair<std::string, uint16_t>& dest)
 {
 {

+ 3 - 0
src/FtpConnection.h

@@ -95,8 +95,10 @@ public:
   bool sendCwd(const std::string& dir);
   bool sendCwd(const std::string& dir);
   bool sendMdtm();
   bool sendMdtm();
   bool sendSize();
   bool sendSize();
+  bool sendEpsv();
   bool sendPasv();
   bool sendPasv();
   SharedHandle<SocketCore> createServerSocket();
   SharedHandle<SocketCore> createServerSocket();
+  bool sendEprt(const SharedHandle<SocketCore>& serverSocket);
   bool sendPort(const SharedHandle<SocketCore>& serverSocket);
   bool sendPort(const SharedHandle<SocketCore>& serverSocket);
   bool sendRest(const SharedHandle<Segment>& segment);
   bool sendRest(const SharedHandle<Segment>& segment);
   bool sendRetr();
   bool sendRetr();
@@ -110,6 +112,7 @@ public:
   // date cannot be parsed, then assign Time::null() to given time.
   // date cannot be parsed, then assign Time::null() to given time.
   // If reply is not received yet, returns 0.
   // If reply is not received yet, returns 0.
   unsigned int receiveMdtmResponse(Time& time);
   unsigned int receiveMdtmResponse(Time& time);
+  unsigned int receiveEpsvResponse(uint16_t& port);
   unsigned int receivePasvResponse(std::pair<std::string, uint16_t>& dest);
   unsigned int receivePasvResponse(std::pair<std::string, uint16_t>& dest);
   unsigned int receivePwdResponse(std::string& pwd);
   unsigned int receivePwdResponse(std::string& pwd);
 
 

+ 114 - 13
src/FtpNegotiationCommand.cc

@@ -126,9 +126,9 @@ bool FtpNegotiationCommand::executeInternal() {
     return true;
     return true;
   } else if(sequence_ == SEQ_FILE_PREPARATION) {
   } else if(sequence_ == SEQ_FILE_PREPARATION) {
     if(getOption()->getAsBool(PREF_FTP_PASV)) {
     if(getOption()->getAsBool(PREF_FTP_PASV)) {
-      sequence_ = SEQ_SEND_PASV;
+      sequence_ = SEQ_PREPARE_PASV;
     } else {
     } else {
-      sequence_ = SEQ_PREPARE_SERVER_SOCKET;
+      sequence_ = SEQ_PREPARE_PORT;
     }
     }
     return false;
     return false;
   } else if(sequence_ == SEQ_EXIT) {
   } else if(sequence_ == SEQ_EXIT) {
@@ -391,9 +391,9 @@ bool FtpNegotiationCommand::onFileSizeDetermined(uint64_t totalLength)
   if(totalLength == 0) {
   if(totalLength == 0) {
 
 
     if(getOption()->getAsBool(PREF_FTP_PASV)) {
     if(getOption()->getAsBool(PREF_FTP_PASV)) {
-      sequence_ = SEQ_SEND_PASV;
+      sequence_ = SEQ_PREPARE_PASV;
     } else {
     } else {
-      sequence_ = SEQ_PREPARE_SERVER_SOCKET;
+      sequence_ = SEQ_PREPARE_PORT;
     }
     }
 
 
     if(getOption()->getAsBool(PREF_DRY_RUN)) {
     if(getOption()->getAsBool(PREF_DRY_RUN)) {
@@ -514,9 +514,9 @@ bool FtpNegotiationCommand::recvSize() {
     // wrong file to be downloaded if user-specified URL is wrong.
     // wrong file to be downloaded if user-specified URL is wrong.
   }
   }
   if(getOption()->getAsBool(PREF_FTP_PASV)) {
   if(getOption()->getAsBool(PREF_FTP_PASV)) {
-    sequence_ = SEQ_SEND_PASV;
+    sequence_ = SEQ_PREPARE_PASV;
   } else {
   } else {
-    sequence_ = SEQ_PREPARE_SERVER_SOCKET;
+    sequence_ = SEQ_PREPARE_PORT;
   }
   }
   return true;
   return true;
 }
 }
@@ -526,6 +526,22 @@ void FtpNegotiationCommand::afterFileAllocation()
   setReadCheckSocket(getSocket());
   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()
 bool FtpNegotiationCommand::prepareServerSocket()
 {
 {
   serverSocket_ = ftp_->createServerSocket();
   serverSocket_ = ftp_->createServerSocket();
@@ -533,8 +549,30 @@ bool FtpNegotiationCommand::prepareServerSocket()
   return true;
   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() {
 bool FtpNegotiationCommand::sendPort() {
-  afterFileAllocation();
   if(ftp_->sendPort(serverSocket_)) {
   if(ftp_->sendPort(serverSocket_)) {
     disableWriteCheckSocket();
     disableWriteCheckSocket();
     sequence_ = SEQ_RECV_PORT;
     sequence_ = SEQ_RECV_PORT;
@@ -556,8 +594,45 @@ bool FtpNegotiationCommand::recvPort() {
   return true;
   return true;
 }
 }
 
 
-bool FtpNegotiationCommand::sendPasv() {
+bool FtpNegotiationCommand::preparePasv() {
   afterFileAllocation();
   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()) {
   if(ftp_->sendPasv()) {
     disableWriteCheckSocket();
     disableWriteCheckSocket();
     sequence_ = SEQ_RECV_PASV;
     sequence_ = SEQ_RECV_PASV;
@@ -576,20 +651,26 @@ bool FtpNegotiationCommand::recvPasv() {
   if(status != 227) {
   if(status != 227) {
     throw DL_ABORT_EX(StringFormat(EX_BAD_STATUS, status).str());
     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()) {
   if(isProxyDefined()) {
-    dataConnAddr_ = dest;
     sequence_ = SEQ_RESOLVE_PROXY;
     sequence_ = SEQ_RESOLVE_PROXY;
     return true;
     return true;
   } else {
   } else {
     // make a data connection to the server.
     // make a data connection to the server.
     if(getLogger()->info()) {
     if(getLogger()->info()) {
       getLogger()->info(MSG_CONNECTING_TO_SERVER, util::itos(getCuid()).c_str(),
       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_.reset(new SocketCore());
-    dataSocket_->establishConnection(dest.first, dest.second);
+    dataSocket_->establishConnection(dataConnAddr_.first, dataConnAddr_.second);
     disableReadCheckSocket();
     disableReadCheckSocket();
     setWriteCheckSocket(dataSocket_);
     setWriteCheckSocket(dataSocket_);
     sequence_ = SEQ_SEND_REST_PASV;
     sequence_ = SEQ_SEND_REST_PASV;
@@ -691,6 +772,12 @@ bool FtpNegotiationCommand::recvTunnelResponse()
 
 
 bool FtpNegotiationCommand::sendRestPasv(const SharedHandle<Segment>& segment) {
 bool FtpNegotiationCommand::sendRestPasv(const SharedHandle<Segment>& segment) {
   //dataSocket_->setBlockingMode();
   //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());
   setReadCheckSocket(getSocket());
   disableWriteCheckSocket();
   disableWriteCheckSocket();
   return sendRest(segment);
   return sendRest(segment);
@@ -803,12 +890,26 @@ bool FtpNegotiationCommand::processSequence
     return sendSize();
     return sendSize();
   case SEQ_RECV_SIZE:
   case SEQ_RECV_SIZE:
     return recvSize();
     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:
   case SEQ_PREPARE_SERVER_SOCKET:
     return prepareServerSocket();
     return prepareServerSocket();
   case SEQ_SEND_PORT:
   case SEQ_SEND_PORT:
     return sendPort();
     return sendPort();
   case SEQ_RECV_PORT:
   case SEQ_RECV_PORT:
     return recvPort();
     return recvPort();
+  case SEQ_PREPARE_PASV:
+    return preparePasv();
+  case SEQ_SEND_EPSV:
+    return sendEpsv();
+  case SEQ_RECV_EPSV:
+    return recvEpsv();
   case SEQ_SEND_PASV:
   case SEQ_SEND_PASV:
     return sendPasv();
     return sendPasv();
   case SEQ_RECV_PASV:
   case SEQ_RECV_PASV:

+ 15 - 0
src/FtpNegotiationCommand.h

@@ -62,9 +62,16 @@ public:
     SEQ_RECV_MDTM,
     SEQ_RECV_MDTM,
     SEQ_SEND_SIZE,
     SEQ_SEND_SIZE,
     SEQ_RECV_SIZE,
     SEQ_RECV_SIZE,
+    SEQ_PREPARE_PORT,
+    SEQ_PREPARE_SERVER_SOCKET_EPRT,
+    SEQ_SEND_EPRT,
+    SEQ_RECV_EPRT,
     SEQ_PREPARE_SERVER_SOCKET,
     SEQ_PREPARE_SERVER_SOCKET,
     SEQ_SEND_PORT,
     SEQ_SEND_PORT,
     SEQ_RECV_PORT,
     SEQ_RECV_PORT,
+    SEQ_PREPARE_PASV,
+    SEQ_SEND_EPSV,
+    SEQ_RECV_EPSV,
     SEQ_SEND_PASV,
     SEQ_SEND_PASV,
     SEQ_RECV_PASV,
     SEQ_RECV_PASV,
     SEQ_RESOLVE_PROXY,
     SEQ_RESOLVE_PROXY,
@@ -100,11 +107,19 @@ private:
   bool recvMdtm();
   bool recvMdtm();
   bool sendSize();
   bool sendSize();
   bool recvSize();
   bool recvSize();
+  bool preparePort();
+  bool prepareServerSocketEprt();
+  bool sendEprt();
+  bool recvEprt();
   bool prepareServerSocket();
   bool prepareServerSocket();
   bool sendPort();
   bool sendPort();
   bool recvPort();
   bool recvPort();
+  bool preparePasv();
+  bool sendEpsv();
+  bool recvEpsv();
   bool sendPasv();
   bool sendPasv();
   bool recvPasv();
   bool recvPasv();
+  bool preparePasvConnect();
   bool resolveProxy();
   bool resolveProxy();
   bool sendTunnelRequest();
   bool sendTunnelRequest();
   bool recvTunnelResponse();
   bool recvTunnelResponse();

+ 16 - 1
src/SocketCore.cc

@@ -341,11 +341,26 @@ void SocketCore::getAddrInfo(std::pair<std::string, uint16_t>& addrinfo) const
 {
 {
   struct sockaddr_storage sockaddr;
   struct sockaddr_storage sockaddr;
   socklen_t len = sizeof(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);
   struct sockaddr* addrp = reinterpret_cast<struct sockaddr*>(&sockaddr);
   if(getsockname(sockfd_, addrp, &len) == -1) {
   if(getsockname(sockfd_, addrp, &len) == -1) {
     throw DL_ABORT_EX(StringFormat(EX_SOCKET_GET_NAME, errorMsg()).str());
     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
 void SocketCore::getPeerInfo(std::pair<std::string, uint16_t>& peerinfo) const

+ 15 - 0
src/SocketCore.h

@@ -159,6 +159,21 @@ public:
    */
    */
   void getAddrInfo(std::pair<std::string, uint16_t>& addrinfo) const;
   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.
    * Stores peer's address and port to peerinfo.
    * @param peerinfo placeholder to store peer's address and port.
    * @param peerinfo placeholder to store peer's address and port.

+ 31 - 0
test/FtpConnectionTest.cc

@@ -32,6 +32,7 @@ class FtpConnectionTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testSendSize);
   CPPUNIT_TEST(testSendSize);
   CPPUNIT_TEST(testReceiveSizeResponse);
   CPPUNIT_TEST(testReceiveSizeResponse);
   CPPUNIT_TEST(testSendRetr);
   CPPUNIT_TEST(testSendRetr);
+  CPPUNIT_TEST(testReceiveEpsvResponse);
   CPPUNIT_TEST_SUITE_END();
   CPPUNIT_TEST_SUITE_END();
 private:
 private:
   SharedHandle<SocketCore> serverSocket_;
   SharedHandle<SocketCore> serverSocket_;
@@ -85,6 +86,7 @@ public:
   void testSendSize();
   void testSendSize();
   void testReceiveSizeResponse();
   void testReceiveSizeResponse();
   void testSendRetr();
   void testSendRetr();
+  void testReceiveEpsvResponse();
 };
 };
 
 
 
 
@@ -299,4 +301,33 @@ void FtpConnectionTest::testSendRetr()
                        std::string(&data[0], &data[len]));
                        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
 } // namespace aria2