This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-net.git


The following commit(s) were added to refs/heads/master by this push:
     new c9f1179d NET-642 using execPROT on FTPSClients with Proxy Settings 
removes Proxy Settings do not reset proxy settings when re-setting the socket 
factory create method identical to open _openDataConnection_ for FTPS where 
proxy is used and ssl socket is created from ssl context
     new f9e0035e Merge pull request #90 from YaniM/master
c9f1179d is described below

commit c9f1179dd95c642aab01ae23b8f1262d73f58b25
Author: Yani Mihaylov <[email protected]>
AuthorDate: Fri Oct 22 10:19:29 2021 +0300

    NET-642 using execPROT on FTPSClients with Proxy Settings removes Proxy 
Settings
    do not reset proxy settings when re-setting the socket factory
    create method identical to open _openDataConnection_ for FTPS where proxy 
is used and ssl socket is created from ssl context
---
 .../java/org/apache/commons/net/SocketClient.java  |   4 -
 .../java/org/apache/commons/net/ftp/FTPClient.java |   6 +-
 .../org/apache/commons/net/ftp/FTPSClient.java     | 185 ++++++++++++++++++++-
 3 files changed, 187 insertions(+), 8 deletions(-)

diff --git a/src/main/java/org/apache/commons/net/SocketClient.java 
b/src/main/java/org/apache/commons/net/SocketClient.java
index f8149c10..f3037d3a 100644
--- a/src/main/java/org/apache/commons/net/SocketClient.java
+++ b/src/main/java/org/apache/commons/net/SocketClient.java
@@ -821,10 +821,6 @@ public abstract class SocketClient
         } else {
             _socketFactory_ = factory;
         }
-        // re-setting the socket factory makes the proxy setting useless,
-        // so set the field to null so that getProxy() doesn't return a
-        // Proxy that we're actually not using.
-        connProxy = null;
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPClient.java 
b/src/main/java/org/apache/commons/net/ftp/FTPClient.java
index f2184fe9..24f0bc0c 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPClient.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPClient.java
@@ -1682,7 +1682,7 @@ public class FTPClient extends FTP implements 
Configurable {
      *
      * @return The client port for active mode.
      */
-    private int getActivePort()
+    protected int getActivePort()
     {
         if (activeMinPort > 0 && activeMaxPort >= activeMinPort)
         {
@@ -1848,7 +1848,7 @@ public class FTPClient extends FTP implements 
Configurable {
      * @return __activeExternalHost if non-null, else getLocalAddress()
      * @see #setActiveExternalIPAddress(String)
      */
-    private InetAddress getHostAddress()
+    protected InetAddress getHostAddress()
     {
         if (activeExternalHost != null)
         {
@@ -1969,7 +1969,7 @@ public class FTPClient extends FTP implements 
Configurable {
      *
      * @return __reportActiveExternalHost if non-null, else getHostAddress();
      */
-    private InetAddress getReportHostAddress() {
+    protected InetAddress getReportHostAddress() {
         if (reportActiveExternalHost != null) {
             return reportActiveExternalHost ;
         }
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPSClient.java 
b/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
index 7f0875b8..e1673abd 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
@@ -22,6 +22,10 @@ import java.io.BufferedWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
 import java.net.Socket;
 
 import javax.net.ssl.HostnameVerifier;
@@ -262,7 +266,7 @@ public class FTPSClient extends FTPClient {
      */
     @Override
     protected Socket _openDataConnection_(final String command, final String 
arg) throws IOException {
-        final Socket socket = super._openDataConnection_(command, arg);
+        final Socket socket = openDataSecureConnection(command, arg);
         _prepareDataSocket_(socket);
         if (socket instanceof SSLSocket) {
             final SSLSocket sslSocket = (SSLSocket) socket;
@@ -287,6 +291,170 @@ public class FTPSClient extends FTPClient {
         return socket;
     }
 
+    /**
+     * Establishes a data connection with the FTP server, returning
+     * a Socket for the connection if successful.  If a restart
+     * offset has been set with {@link #setRestartOffset(long)},
+     * a REST command is issued to the server with the offset as
+     * an argument before establishing the data connection.  Active
+     * mode connections also cause a local PORT command to be issued.
+     *
+     * @param command  The text representation of the FTP command to send.
+     * @param arg The arguments to the FTP command.  If this parameter is
+     *             set to null, then the command is sent with no argument.
+     * @return A Socket corresponding to the established data connection.
+     *         Null is returned if an FTP protocol error is reported at
+     *         any point during the establishment and initialization of
+     *         the connection.
+     * @throws IOException  If an I/O error occurs while either sending a
+     *      command to the server or receiving a reply from the server.
+     * @since 3.1
+     */
+    private Socket openDataSecureConnection(String command, String arg) throws 
IOException {
+        if (getDataConnectionMode() != ACTIVE_LOCAL_DATA_CONNECTION_MODE &&
+                getDataConnectionMode() != PASSIVE_LOCAL_DATA_CONNECTION_MODE) 
{
+            return null;
+        }
+
+        final boolean isInet6Address = getRemoteAddress() instanceof 
Inet6Address;
+
+        final Socket socket;
+        Socket sslSocket = null;
+        final int soTimeoutMillis = 
DurationUtils.toMillisInt(getDataTimeout());
+        if (getDataConnectionMode() == ACTIVE_LOCAL_DATA_CONNECTION_MODE)
+        {
+            // if no activePortRange was set (correctly) -> getActivePort() = 0
+            // -> new ServerSocket(0) -> bind to any free local port
+            try (final ServerSocket server = 
_serverSocketFactory_.createServerSocket(getActivePort(), 1, getHostAddress())) 
{
+                // Try EPRT only if remote server is over IPv6, if not use 
PORT,
+                // because EPRT has no advantage over PORT on IPv4.
+                // It could even have the disadvantage,
+                // that EPRT will make the data connection fail, because
+                // today's intelligent NAT Firewalls are able to
+                // substitute IP addresses in the PORT command,
+                // but might not be able to recognize the EPRT command.
+                if (isInet6Address) {
+                    if 
(!FTPReply.isPositiveCompletion(eprt(getReportHostAddress(), 
server.getLocalPort()))) {
+                        return null;
+                    }
+                } else if 
(!FTPReply.isPositiveCompletion(port(getReportHostAddress(), 
server.getLocalPort()))) {
+                    return null;
+                }
+
+                if ((getRestartOffset() > 0) && !restart(getRestartOffset())) {
+                    return null;
+                }
+
+                if (!FTPReply.isPositivePreliminary(sendCommand(command, 
arg))) {
+                    return null;
+                }
+
+                // For now, let's just use the data timeout value for waiting 
for
+                // the data connection.  It may be desirable to let this be a
+                // separately configurable value.  In any case, we really want
+                // to allow preventing the accept from blocking indefinitely.
+                if (soTimeoutMillis >= 0) {
+                    server.setSoTimeout(soTimeoutMillis);
+                }
+                socket = server.accept();
+
+                // Ensure the timeout is set before any commands are issued on 
the new socket
+                if (soTimeoutMillis >= 0) {
+                    socket.setSoTimeout(soTimeoutMillis);
+                }
+                if (getReceiveDataSocketBufferSize() > 0) {
+                    
socket.setReceiveBufferSize(getReceiveDataSocketBufferSize());
+                }
+                if (getSendDataSocketBufferSize() > 0) {
+                    socket.setSendBufferSize(getSendDataSocketBufferSize());
+                }
+            }
+        }
+        else
+        { // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE
+
+            // Try EPSV command first on IPv6 - and IPv4 if enabled.
+            // When using IPv4 with NAT it has the advantage
+            // to work with more rare configurations.
+            // E.g. if FTP server has a static PASV address (external network)
+            // and the client is coming from another internal network.
+            // In that case the data connection after PASV command would fail,
+            // while EPSV would make the client succeed by taking just the 
port.
+            final boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address;
+            if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE)
+            {
+                _parseExtendedPassiveModeReply(_replyLines.get(0));
+            }
+            else
+            {
+                if (isInet6Address) {
+                    return null; // Must use EPSV for IPV6
+                }
+                // If EPSV failed on IPV4, revert to PASV
+                if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) {
+                    return null;
+                }
+                _parsePassiveModeReply(_replyLines.get(0));
+            }
+
+            if (getProxy() != null) {
+                socket = new Socket(getProxy());
+            } else {
+                socket = _socketFactory_.createSocket();
+            }
+
+            if (getReceiveDataSocketBufferSize() > 0) {
+                socket.setReceiveBufferSize(getReceiveDataSocketBufferSize());
+            }
+            if (getSendDataSocketBufferSize() > 0) {
+                socket.setSendBufferSize(getSendDataSocketBufferSize());
+            }
+            if (getPassiveLocalIPAddress() != null) {
+                socket.bind(new InetSocketAddress(getPassiveLocalIPAddress(), 
0));
+            }
+
+            // For now, let's just use the data timeout value for waiting for
+            // the data connection.  It may be desirable to let this be a
+            // separately configurable value.  In any case, we really want
+            // to allow preventing the accept from blocking indefinitely.
+            if (soTimeoutMillis >= 0) {
+                socket.setSoTimeout(soTimeoutMillis);
+            }
+
+            socket.connect(new InetSocketAddress(getPassiveHost(), 
getPassivePort()), connectTimeout);
+
+            if (getProxy() != null) {
+                sslSocket = context.getSocketFactory().createSocket(socket, 
getPassiveHost(), getPassivePort(), true);
+            }
+
+            if ((getRestartOffset() > 0) && !restart(getRestartOffset()))
+            {
+                closeSockets(socket, sslSocket);
+                return null;
+            }
+
+            if (!FTPReply.isPositivePreliminary(sendCommand(command, arg)))
+            {
+                closeSockets(socket, sslSocket);
+                return null;
+            }
+        }
+
+        if (isRemoteVerificationEnabled() && !verifyRemote(socket))
+        {
+            // Grab the host before we close the socket to avoid NET-663
+            final InetAddress socketHost = socket.getInetAddress();
+
+            closeSockets(socket, sslSocket);
+
+            throw new IOException(
+                    "Host attempting data connection " + 
socketHost.getHostAddress() +
+                            " is not same as server " + 
getRemoteAddress().getHostAddress());
+        }
+
+        return getProxy() != null ? sslSocket : socket;
+    }
+
         /**
          * Performs any custom initialization for a newly created SSLSocket 
(before the SSL handshake happens). Called
          * by {@link #_openDataConnection_(int, String)} immediately after 
creating the socket. The default
@@ -910,5 +1078,20 @@ public class FTPSClient extends FTPClient {
         }
     }
 
+    /**
+     * Close open sockets.
+     * @param socket main socket for proxy if enabled
+     * @param sslSocket ssl socket
+     * @throws IOException closing sockets is not successful
+     */
+    private void closeSockets(Socket socket, Socket sslSocket) throws 
IOException {
+        if (socket != null) {
+            socket.close();
+        }
+        if (sslSocket != null) {
+            sslSocket.close();
+        }
+    }
+
 }
 /* kate: indent-width 4; replace-tabs on; */

Reply via email to