@@ -279,7 +279,8 @@ public class FTPClient extends FTP
279
279
private FTPFileEntryParserFactory __parserFactory ;
280
280
private int __bufferSize ;
281
281
private boolean __listHiddenFiles ;
282
-
282
+ private boolean __useEPSVwithIPv4 ; // whether to attempt EPSV with an IPv4 connection
283
+
283
284
// __systemName is a cached value that should not be referenced directly
284
285
// except when assigned in getSystemName and __initDefaults.
285
286
private String __systemName ;
@@ -317,6 +318,7 @@ public FTPClient()
317
318
__parserFactory = new DefaultFTPFileEntryParserFactory ();
318
319
__configuration = null ;
319
320
__listHiddenFiles = false ;
321
+ __useEPSVwithIPv4 = false ;
320
322
__random = new Random ();
321
323
}
322
324
@@ -502,23 +504,32 @@ protected Socket _openDataConnection_(int command, String arg)
502
504
__dataConnectionMode != PASSIVE_LOCAL_DATA_CONNECTION_MODE )
503
505
return null ;
504
506
507
+ final boolean isInet6Address = getRemoteAddress () instanceof Inet6Address ;
508
+
505
509
if (__dataConnectionMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE )
506
510
{
511
+ // if no activePortRange was set (correctly) -> getActivePort() = 0
512
+ // -> new ServerSocket(0) -> bind to any free local port
507
513
ServerSocket server = _serverSocketFactory_ .createServerSocket (getActivePort (), 1 , getHostAddress ());
508
514
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 )
513
523
{
514
- if (getRemoteAddress () instanceof Inet6Address )
524
+ if (! FTPReply . isPositiveCompletion ( eprt ( getHostAddress (), server . getLocalPort ())) )
515
525
{
516
526
server .close ();
517
527
return null ;
518
528
}
519
-
520
- if (!FTPReply .isPositiveCompletion (port (getHostAddress (),
521
- server .getLocalPort ())))
529
+ }
530
+ else
531
+ {
532
+ if (!FTPReply .isPositiveCompletion (port (getHostAddress (), server .getLocalPort ())))
522
533
{
523
534
server .close ();
524
535
return null ;
@@ -552,15 +563,27 @@ protected Socket _openDataConnection_(int command, String arg)
552
563
else
553
564
{ // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE
554
565
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
+ {
559
576
__parseExtendedPassiveModeReply (_replyLines .get (0 ));
560
577
}
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 ) {
563
585
return null ;
586
+ }
564
587
__parsePassiveModeReply (_replyLines .get (0 ));
565
588
}
566
589
@@ -2620,6 +2643,34 @@ public void setListHiddenFiles(boolean listHiddenFiles) {
2620
2643
public boolean getListHiddenFiles () {
2621
2644
return this .__listHiddenFiles ;
2622
2645
}
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
+
2623
2674
}
2624
2675
2625
2676
/* Emacs configuration
0 commit comments