JDK-7077696 : java.net.Socket closes when "PASV" is sent on an authenticated FTP connection
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 7,7u6
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: windows_7
  • CPU: x86
  • Submitted: 2011-08-11
  • Updated: 2013-04-10
  • Resolved: 2012-11-07
Description
FULL PRODUCT VERSION :
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) 64-Bit Server VM (build 21.0-b17, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Windows7: Microsoft Windows [Version 6.1.7601]
Windows Vista: Microsoft Windows [Version 6.0.6000]

EXTRA RELEVANT SYSTEM CONFIGURATION :
Windows Firewall on with no applicable exceptions

A DESCRIPTION OF THE PROBLEM :
After the establishment of an authenticated, unencrypted FTP session, an instance of java.net.Socket will close its connection immediately when the string "PASV" is sent.

REGRESSION.  Last worked in version 6u26

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. connect to FTP server (e.g. ftp.oracle.com) using java.net.Socket
2. read welcome message
3. send "USER <username>\r\n"
4. read reply
5. send "PASS <password>\r\n"
6. read reply
7. send "PASV\r\n"
8. read reply

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Successfully read reply in step 8
ACTUAL -
java.net.SocketException: Permission denied: recv failed

in step 8

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.net.SocketException: Permission denied: recv failed
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.read(Unknown Source)
        at java.net.SocketInputStream.read(Unknown Source)
        at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
        at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
        at sun.nio.cs.StreamDecoder.read(Unknown Source)
        at java.io.InputStreamReader.read(Unknown Source)
        at java.io.BufferedReader.fill(Unknown Source)
        at java.io.BufferedReader.readLine(Unknown Source)
        at java.io.BufferedReader.readLine(Unknown Source)
        at com.enterprisedt.client.oracle.Java7WinFirewallTest.readReply(Java7WinFirewallTest.java:48)
        at com.enterprisedt.client.oracle.Java7WinFirewallTest.main(Java7WinFirewallTest.java:21)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.*;
import java.net.*;

/*
 * This class can be used to illustrate a problem in JRE SE 1.7.0-b147.
 *
 * Environment: Windows Vista and Windows 7 with Windows Firewall enabled.
 *
 * When run on Java 6 the test completes successfully.
 *
 * When run on Java 7 the test fails with the following exception:
 *
 * java.net.SocketException: Permission denied: recv failed
 *         at java.net.SocketInputStream.socketRead0(Native Method)
 *         at java.net.SocketInputStream.read(Unknown Source)
 *         at java.net.SocketInputStream.read(Unknown Source)
 *         at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
 *         at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
 *         at sun.nio.cs.StreamDecoder.read(Unknown Source)
 *         at java.io.InputStreamReader.read(Unknown Source)
 *         at java.io.BufferedReader.fill(Unknown Source)
 *         at java.io.BufferedReader.readLine(Unknown Source)
 *         at java.io.BufferedReader.readLine(Unknown Source)
 *         at Java7WinFirewallTest.readReply(Java7WinFirewallTest.java:48)
 *         at Java7WinFirewallTest.main(Java7WinFirewallTest.java:21) *
 */
public class Java7WinFirewallTest {

	public static void main(String[] args) {
		try {
			Socket socket = new Socket("ftp.oracle.com", 21);
			BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			Writer out = new OutputStreamWriter(socket.getOutputStream());

			readWelcomeMessage(in);
			
			sendCommand(out, "USER anonymous");
			readReply(in);
			
			sendCommand(out, "PASS test");
			readReply(in);
			
			sendCommand(out, "PASV");
			readReply(in);

			sendCommand(out, "QUIT");
			readReply(in);

			socket.close();
			System.out.println("\nTest completed - no problems");
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	private static void readWelcomeMessage(BufferedReader in)
			throws IOException {
		String line;
		do {
			line = readReply(in);
		} while (!line.trim().equals("220"));
	}

	private static void sendCommand(Writer out, String command) throws IOException {
		System.out.println("> " + command);
		out.write(command + "\r\n");
		out.flush();
	}

	private static String readReply(BufferedReader in) throws IOException {
		String line = in.readLine();
		System.out.println("< " + line);
		return line;
	}
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Add rule to firewall that allows the connection by, for example, accepting all traffic from java.exe.
Same issue reported by a CAP member -

It's a 64 bit Windows installation running 32 bit Java 7, just downloaded from http://www.java.com today. I guess that means I've been testing Version 7 Update 5. The "report.txt" file you asked me to use when reporting in May 1 isn't much help. It isn't a bug report form, just another bug report from another user having some DNS and reverse lookup related issue. I don't think that problem relates to the ftp connection problem I've experienced with jAlbum on Java 7.

Here are detailed steps on how to reproduce the problem:
* Use a Windows 7 machine with an out-of-the-box configuration (I used Home Premium 64 bit)
* Install Java 7, 32 bit. (I installed from http://www.java.com)
* Install jAlbum from http://jalbum.net/download/10.8.1/Windows/NoVM/jAlbum-install.exe
* Start jAlbum and select Tools->Upload/Manage
* Press the "Add" button and add a "Connection profile" to an ftp server of your choice. The ftp server should allow PASV mode (most do)
* Connect to the ftp server by pressing jAlbum's "Connect" button. jAlbum should now properly show you a folder tree from that ftp server as jAlbum 10.8.1 is bundled with a properly working Java 1.6.

Now, let's trigger the problem by trying to display the folder tree on that ftp server by running jAlbum on Java 1.7 instead. Do like this:

* Open jAlbum's installation folder (Tools->Open directories->Program directory)
* Close jAlbum
* Open the file "Jalbum.ini" for editing in a text editor
* Ensure that the "JRE path" is changed from:
JRE Path=C:\Program Files (x86)\jAlbum\jre\

to

JRE Path=C:\Program Files (x86)\Java\jre7\

(or to wherever you've installed Java 1.7 runtime)

* Save the changes
* Restart jAlbum
* Verify that jAlbum is indeed running on Java 7 by peeking at Help->About jAlbum->System->java.version
* Select Tools->Upload/Manage to view the ftp connection profiles again
* Connect to the same ftp server as before. Now you probably don't see the existing ftp folders as the data connection has been blocked
* To see the supressed error message and stack trace, select Tools->Open system console. The stack trace clearly states that a "Connection reset" occurs at the socket level when trying to read data (a readLine call) over a data connection that should just have been established from the local computer to the remote ftp server (ordinary PASV mode)

I've been investigating if Windows Firewall is somewhat guilty. It is, but it's confusing. If I completely switch it off, it works on Java 7, but I can't get it working with the firewall active, even though it's configured to allow outgoing connections from programs by default. I've even set up explicit firewall rules to allow outgoing connections from the "jAlbum.exe" software, but it doesn't help. When running on top of Java 6 it works perfectly.

I've been using two popuar 3:rd party ftp libraries when testing this problem: EdtFTPj and FTP4j. Both fail with the same "Connection reset" when trying to establish or read from the data connection.

Comments
Fixed by Microsoft in http://support.microsoft.com/kb/2754804
07-11-2012

Microsoft have fixed this and we won't be taking any specific action. See the following KB article http://support.microsoft.com/kb/2754804 So, I'm going to close this report.
07-11-2012

If this is not a jdk7u regression, can we get it closed as not a bug?
04-11-2012

WORK AROUND - add a firewall exception for the Java binary. - run with -Djava.net.preferIPv4Stack=true (which disables IPv6 and uses AF_INET sockets exclusively) - or disable stateful FTP inspection in the firewall (registry setting or netsh command)
10-11-2011

EVALUATION Yes, it's definitely a firewall problem. I have confirmed this using a native C app that does the same as the test Java program. If you create an AF_INET6 socket, switch off the IPV6_ONLY socket option, and then connect to an IPv4 destination FTP server using an IPv6 address of the form "::FFFF:A.B.C.D" where A.B.C.D is the ip address of the server, then the same behavior will be seen. Windows firewall contains a filter that examines the contents of socket connections to port 21 (ftp) ostensibly to keep track of incoming data connections from the server, so that the relevant ports can opened (dynamically) in the firewall. However, what's happening here is cleary wrong, and the firewall should not be interfering with the PASV command (and it doesn't do that when a regular AF_INET socket is used). See workaround.
10-11-2011

WORK AROUND Run with -Djava.net.preferIPv4Stack=true
09-11-2011

EVALUATION This does not appear to be a JDK bug, rather it's just the Windows firewall recognizing and blocking the ftp protocol. The only difference between JDK7 and older releases is that the JDK is using IPv6 sockets when IPv6 is enabled and so IPv4-mapped IPv6 addresses are used. it may be that Windows or the firewall is not configured to allow IPv6 sockets.
09-11-2011