Skip to content

Commit 045baf9

Browse files
committed
NET-313 Optionally enable EPSV with IPv4
Only send EPRT with IPv6. Fix incorrect port used with EPRT. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/net/branches/NET_2_0@963335 13f79535-47bb-0310-9956-ffa450edef68
1 parent 906377e commit 045baf9

File tree

1 file changed

+66
-15
lines changed

1 file changed

+66
-15
lines changed

src/main/java/org/apache/commons/net/ftp/FTPClient.java

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ public class FTPClient extends FTP
279279
private FTPFileEntryParserFactory __parserFactory;
280280
private int __bufferSize;
281281
private boolean __listHiddenFiles;
282-
282+
private boolean __useEPSVwithIPv4; // whether to attempt EPSV with an IPv4 connection
283+
283284
// __systemName is a cached value that should not be referenced directly
284285
// except when assigned in getSystemName and __initDefaults.
285286
private String __systemName;
@@ -317,6 +318,7 @@ public FTPClient()
317318
__parserFactory = new DefaultFTPFileEntryParserFactory();
318319
__configuration = null;
319320
__listHiddenFiles = false;
321+
__useEPSVwithIPv4 = false;
320322
__random = new Random();
321323
}
322324

@@ -502,23 +504,32 @@ protected Socket _openDataConnection_(int command, String arg)
502504
__dataConnectionMode != PASSIVE_LOCAL_DATA_CONNECTION_MODE)
503505
return null;
504506

507+
final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address;
508+
505509
if (__dataConnectionMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE)
506510
{
511+
// if no activePortRange was set (correctly) -> getActivePort() = 0
512+
// -> new ServerSocket(0) -> bind to any free local port
507513
ServerSocket server = _serverSocketFactory_.createServerSocket(getActivePort(), 1, getHostAddress());
508514

509-
// try EPRT first. If that fails, and the connection is over IPv4
510-
// fallback to PORT
511-
if (!FTPReply.isPositiveCompletion(eprt(getHostAddress(),
512-
getActivePort())))
515+
// Try EPRT only if remote server is over IPv6, if not use PORT,
516+
// because EPRT has no advantage over PORT on IPv4.
517+
// It could even have the disadvantage,
518+
// that EPRT will make the data connection fail, because
519+
// today's intelligent NAT Firewalls are able to
520+
// substitute IP addresses in the PORT command,
521+
// but might not be able to recognize the EPRT command.
522+
if (isInet6Address)
513523
{
514-
if (getRemoteAddress() instanceof Inet6Address)
524+
if (!FTPReply.isPositiveCompletion(eprt(getHostAddress(), server.getLocalPort())))
515525
{
516526
server.close();
517527
return null;
518528
}
519-
520-
if (!FTPReply.isPositiveCompletion(port(getHostAddress(),
521-
server.getLocalPort())))
529+
}
530+
else
531+
{
532+
if (!FTPReply.isPositiveCompletion(port(getHostAddress(), server.getLocalPort())))
522533
{
523534
server.close();
524535
return null;
@@ -552,15 +563,27 @@ protected Socket _openDataConnection_(int command, String arg)
552563
else
553564
{ // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE
554565

555-
// If we are over an IPv6 connection, try EPSV
556-
if (getRemoteAddress() instanceof Inet6Address) {
557-
if (epsv() != FTPReply.ENTERING_EPSV_MODE)
558-
return null;
566+
// Try EPSV command first on IPv6 - and IPv4 if enabled.
567+
// When using IPv4 with NAT it has the advantage
568+
// to work with more rare configurations.
569+
// E.g. if FTP server has a static PASV address (external network)
570+
// and the client is coming from another internal network.
571+
// In that case the data connection after PASV command would fail,
572+
// while EPSV would make the client succeed by taking just the port.
573+
boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address;
574+
if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE)
575+
{
559576
__parseExtendedPassiveModeReply(_replyLines.get(0));
560577
}
561-
else {
562-
if (pasv() != FTPReply.ENTERING_PASSIVE_MODE)
578+
else
579+
{
580+
if (isInet6Address) {
581+
return null; // Must use EPSV for IPV6
582+
}
583+
// If EPSV failed on IPV4, revert to PASV
584+
if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) {
563585
return null;
586+
}
564587
__parsePassiveModeReply(_replyLines.get(0));
565588
}
566589

@@ -2620,6 +2643,34 @@ public void setListHiddenFiles(boolean listHiddenFiles) {
26202643
public boolean getListHiddenFiles() {
26212644
return this.__listHiddenFiles;
26222645
}
2646+
2647+
/**
2648+
* Whether should attempt to use EPSV with IPv4.
2649+
* Default (if not set) is <code>false</code>
2650+
* @return true if should attempt EPS
2651+
*/
2652+
public boolean isUseEPSVwithIPv4() {
2653+
return __useEPSVwithIPv4;
2654+
}
2655+
2656+
2657+
/**
2658+
* Set whether to use EPSV with IPv4.
2659+
* Might be worth enabling in some circumstances.
2660+
*
2661+
* For example, when using IPv4 with NAT it
2662+
* may work with some rare configurations.
2663+
* E.g. if FTP server has a static PASV address (external network)
2664+
* and the client is coming from another internal network.
2665+
* In that case the data connection after PASV command would fail,
2666+
* while EPSV would make the client succeed by taking just the port.
2667+
*
2668+
* @param selected value to set.
2669+
*/
2670+
public void setUseEPSVwithIPv4(boolean selected) {
2671+
this.__useEPSVwithIPv4 = selected;
2672+
}
2673+
26232674
}
26242675

26252676
/* Emacs configuration

0 commit comments

Comments
 (0)