Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/main/java/org/apache/commons/net/SocketClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -821,10 +821,6 @@ public void setSocketFactory(final SocketFactory factory)
} 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;
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/apache/commons/net/ftp/FTPClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -1682,7 +1682,7 @@ public String[] featureValues(final String feature) throws IOException {
*
* @return The client port for active mode.
*/
private int getActivePort()
protected int getActivePort()
{
if (activeMinPort > 0 && activeMaxPort >= activeMinPort)
{
Expand Down Expand Up @@ -1848,7 +1848,7 @@ FTPFileEntryParser getEntryParser() {
* @return __activeExternalHost if non-null, else getLocalAddress()
* @see #setActiveExternalIPAddress(String)
*/
private InetAddress getHostAddress()
protected InetAddress getHostAddress()
{
if (activeExternalHost != null)
{
Expand Down Expand Up @@ -1969,7 +1969,7 @@ public int getReceiveDataSocketBufferSize() {
*
* @return __reportActiveExternalHost if non-null, else getHostAddress();
*/
private InetAddress getReportHostAddress() {
protected InetAddress getReportHostAddress() {
if (reportActiveExternalHost != null) {
return reportActiveExternalHost ;
}
Expand Down
185 changes: 184 additions & 1 deletion src/main/java/org/apache/commons/net/ftp/FTPSClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
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;
Expand Down Expand Up @@ -262,7 +266,7 @@ protected Socket _openDataConnection_(final int command, final String arg)
*/
@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;
Expand All @@ -287,6 +291,170 @@ protected Socket _openDataConnection_(final String command, final String arg) th
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
Expand Down Expand Up @@ -910,5 +1078,20 @@ protected void sslNegotiation() throws IOException {
}
}

/**
* 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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should have a method that takes a single argument and call it for each input.
Use final when you can.

if (socket != null) {
socket.close();
}
if (sslSocket != null) {
sslSocket.close();
}
}

}
/* kate: indent-width 4; replace-tabs on; */