From 2ea629f3f2ae07de9cb92baac13497a789d16ec3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?=
Date: Thu, 8 May 2025 14:52:53 +0000
Subject: [PATCH] 8353440: Disable FTP fallback for non-local file URLs by
default
Reviewed-by: dfuchs
---
.../java/net/doc-files/net-properties.html | 11 +++
.../www/protocol/file/FileURLConnection.java | 19 +++++
.../sun/net/www/protocol/file/Handler.java | 1 +
.../sun/net/www/protocol/file/Handler.java | 1 +
test/jdk/java/net/URL/OpenStream.java | 12 +--
.../protocol/file/NonLocalFtpFallback.java | 54 ++++++++++++--
.../file/NonLocalFtpFallbackDisabled.java | 73 +++++++++++++++++++
7 files changed, 155 insertions(+), 16 deletions(-)
create mode 100644 test/jdk/sun/net/www/protocol/file/NonLocalFtpFallbackDisabled.java
diff --git a/src/java.base/share/classes/java/net/doc-files/net-properties.html b/src/java.base/share/classes/java/net/doc-files/net-properties.html
index 684c90a8164..4b9b62f1ed8 100644
--- a/src/java.base/share/classes/java/net/doc-files/net-properties.html
+++ b/src/java.base/share/classes/java/net/doc-files/net-properties.html
@@ -169,6 +169,17 @@ to determine the proxy that should be used for connecting to a given URI.
globally through their user interface). Note that this property is
checked only once at startup.
+
+File URL stream protocol handler properties
+The following properties are used to configure the handler for URLs with the {@code file://} scheme:
+
+ {@systemProperty jdk.net.file.ftpfallback} (default: <false>)
+ The {@code file://} handler by default rejects any non-local file URL (as defined by RFC 8089)
+ as invalid. Setting this property to true enables a legacy feature where
+ the handler instead opens an FTP connection for such non-local URLs.
+ Any modern code should use explicit {@code ftp://} URLs instead and not rely on
+ enabling this legacy FTP fallback feature.
+
Misc HTTP URL stream protocol handler properties
diff --git a/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java
index 97e797040a0..98ff8709aa4 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java
@@ -25,6 +25,7 @@
package sun.net.www.protocol.file;
+import java.net.MalformedURLException;
import java.net.URL;
import java.net.FileNameMap;
import java.io.*;
@@ -46,6 +47,11 @@ public class FileURLConnection extends URLConnection {
private static final String TEXT_PLAIN = "text/plain";
private static final String LAST_MODIFIED = "last-modified";
+ // The feature of falling back to FTP for non-local file URLs is disabled
+ // by default and can be re-enabled by setting a system property
+ private static final boolean FTP_FALLBACK_ENABLED =
+ Boolean.getBoolean("jdk.net.file.ftpfallback");
+
private final File file;
private InputStream is;
private List directoryListing;
@@ -222,4 +228,17 @@ public class FileURLConnection extends URLConnection {
}
return permission;
}
+
+ /**
+ * Throw {@link MalformedURLException} if the FTP fallback feature for non-local
+ * file URLs is not explicitly enabled via system property.
+ *
+ * @see #FTP_FALLBACK_ENABLED
+ * @throws MalformedURLException if FTP fallback is not enabled
+ */
+ static void requireFtpFallbackEnabled() throws MalformedURLException {
+ if (!FTP_FALLBACK_ENABLED) {
+ throw new MalformedURLException("Unsupported non-local file URL");
+ }
+ }
}
diff --git a/src/java.base/unix/classes/sun/net/www/protocol/file/Handler.java b/src/java.base/unix/classes/sun/net/www/protocol/file/Handler.java
index efde6a809e9..81f898afc39 100644
--- a/src/java.base/unix/classes/sun/net/www/protocol/file/Handler.java
+++ b/src/java.base/unix/classes/sun/net/www/protocol/file/Handler.java
@@ -72,6 +72,7 @@ public class Handler extends URLStreamHandler {
/* If you reach here, it implies that you have a hostname
so attempt an ftp connection.
*/
+ FileURLConnection.requireFtpFallbackEnabled();
URLConnection uc;
URL ru;
diff --git a/src/java.base/windows/classes/sun/net/www/protocol/file/Handler.java b/src/java.base/windows/classes/sun/net/www/protocol/file/Handler.java
index 38ad16267e7..0cd32e600e9 100644
--- a/src/java.base/windows/classes/sun/net/www/protocol/file/Handler.java
+++ b/src/java.base/windows/classes/sun/net/www/protocol/file/Handler.java
@@ -88,6 +88,7 @@ public class Handler extends URLStreamHandler {
/*
* Now attempt an ftp connection.
*/
+ FileURLConnection.requireFtpFallbackEnabled();
URLConnection uc;
URL newurl;
diff --git a/test/jdk/java/net/URL/OpenStream.java b/test/jdk/java/net/URL/OpenStream.java
index 08ffd93b7fb..82fd5106056 100644
--- a/test/jdk/java/net/URL/OpenStream.java
+++ b/test/jdk/java/net/URL/OpenStream.java
@@ -25,6 +25,8 @@
* @bug 4064962 8202708
* @summary openStream should work even when not using proxies and
* UnknownHostException is thrown as expected.
+ * @comment For testing of non-local file URLs with the legacy FTP
+ * fallback feature enabled, see NonLocalFtpFallback.
*/
import java.io.*;
@@ -34,24 +36,15 @@ import java.net.*;
public class OpenStream {
private static final String badHttp = "http://foo.bar.baz/";
- private static final String badUnc = "file://h7qbp368oix47/not-exist.txt";
public static void main(String[] args) throws IOException {
testHttp();
- testUnc();
}
static void testHttp() throws IOException {
checkThrows(badHttp);
}
- static void testUnc() throws IOException {
- boolean isWindows = System.getProperty("os.name").startsWith("Windows");
- if (isWindows) {
- checkThrows(badUnc);
- }
- }
-
static void checkThrows(String url) throws IOException {
URL u = new URL(url);
try {
@@ -62,7 +55,6 @@ public class OpenStream {
}
throw new RuntimeException("Expected UnknownHostException to be " +
"thrown for " + url);
-
}
}
diff --git a/test/jdk/sun/net/www/protocol/file/NonLocalFtpFallback.java b/test/jdk/sun/net/www/protocol/file/NonLocalFtpFallback.java
index 49d09cfb507..fb7b0412063 100644
--- a/test/jdk/sun/net/www/protocol/file/NonLocalFtpFallback.java
+++ b/test/jdk/sun/net/www/protocol/file/NonLocalFtpFallback.java
@@ -40,14 +40,16 @@ import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import static org.junit.Assert.assertThrows;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @test
- * @bug 8353662
- * @summary Verify long-standing behavior of resolving non-local file URLs using FTP.
- * @run junit NonLocalFtpFallback
+ * @bug 8353662 8202708
+ * @summary Verify long-standing, disabled by default behavior of resolving non-local
+ * file URLs using FTP.
+ * @run junit/othervm -Djdk.net.file.ftpfallback=true NonLocalFtpFallback
*/
public class NonLocalFtpFallback {
@@ -112,9 +114,8 @@ public class NonLocalFtpFallback {
* Verifies the long-standing and unspecified FTP fallback feature where the file
* URL scheme handler attempts an FTP connection for non-local files.
*
- * The non-local file URL used here is of the form file://127.0.0.1/path. Since the
- * host component here is not equal to "localhost", this is considered a non-local
- * URL.
+ * The non-local file URL used here is of the form 'file://remotehost/path'. Since the
+ * host component is not equal to 'localhost', this is considered a non-local URL.
*
* @throws Exception
*/
@@ -142,4 +143,45 @@ public class NonLocalFtpFallback {
URL ftpURL = new URL("ftp", hostname, localURL.getFile());
assertEquals(ftpURL.toURI(), uris.iterator().next());
}
+
+ /**
+ * Sanity check that a local file URL (with a host component equal to 'localhost')
+ * does not open any FtpURLConnection when the FTP fallback feature is enabled.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyLocalFileURL() throws Exception {
+ URL localURL = file.toUri().toURL();
+ URL nonLocalURL = new URL("file", "localhost", localURL.getFile());
+
+ // Open the local file: URL connection supplying a proxy
+ Proxy proxy = new Proxy(Proxy.Type.HTTP,
+ new InetSocketAddress(proxyServer.getAddress().getAddress(),
+ proxyServer.getAddress().getPort()));
+ URLConnection con = nonLocalURL.openConnection(proxy);
+
+ // Assert that the expected file content is read
+ try (InputStream in = con.getInputStream()) {
+ byte[] retrived = in.readAllBytes();
+ assertArrayEquals(Files.readAllBytes(file), retrived);
+ }
+
+ // Assert that no FTP URIs were requested in the HTTP proxy
+ assertEquals(0, uris.size());
+ }
+
+ /**
+ * Verify that opening a stream on a non-proxy URLConnection for a non-local
+ * file URL with an unknown host fails with UnknownHostException
+ * when the fallback FtpURLConnection attempts to connect to the non-existing
+ * FTP server.
+ */
+ @Test
+ public void verifyFtpUnknownHost() throws IOException {
+ URL url = new URL("file://nonexistinghost/not-exist.txt");
+ assertThrows(UnknownHostException.class, () -> {
+ InputStream in = url.openConnection(Proxy.NO_PROXY).getInputStream();
+ });
+ }
}
diff --git a/test/jdk/sun/net/www/protocol/file/NonLocalFtpFallbackDisabled.java b/test/jdk/sun/net/www/protocol/file/NonLocalFtpFallbackDisabled.java
new file mode 100644
index 00000000000..52bc3a5d0ad
--- /dev/null
+++ b/test/jdk/sun/net/www/protocol/file/NonLocalFtpFallbackDisabled.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.junit.jupiter.api.Test;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Path;
+
+import static org.junit.Assert.assertThrows;
+
+/**
+ * @test
+ * @bug 8353440
+ * @summary Verify that non-local file URLs are rejected by default
+ * @run junit/othervm NonLocalFtpFallbackDisabled
+ * @run junit/othervm -Djdk.net.file.ftpfallback=false NonLocalFtpFallbackDisabled
+ * @run junit/othervm -Djdk.net.file.ftpfallback NonLocalFtpFallbackDisabled
+ */
+public class NonLocalFtpFallbackDisabled {
+
+ // The file requested in this test
+ private Path file = Path.of("ftp-file.txt");
+
+ /**
+ * Verifies that the long-standing and unspecified FTP fallback feature
+ * where the file URL scheme handler attempts an FTP connection for non-local
+ * files is disabled by default and that opening connections for such URLs
+ * is rejected with a MalformedURLException.
+ *
+ * @throws MalformedURLException if an unexpected URL exception occurs
+ * @throws URISyntaxException if an unexpected URI exception occurs
+ */
+ @Test
+ public void verifyNonLocalFileURLRejected() throws MalformedURLException, URISyntaxException {
+ // We can use a fake host name here, no actual FTP request will be made
+ String hostname = "remotehost";
+
+ URL local = file.toUri().toURL();
+
+ URL nonLocal = new URI("file", hostname, local.getFile(), "").toURL();
+ assertThrows(MalformedURLException.class, () -> {
+ nonLocal.openConnection();
+ });
+
+ URL nonLocalEmptyPath = new URI("file", hostname, "", "").toURL();
+ assertThrows(MalformedURLException.class, () -> {
+ nonLocalEmptyPath.openConnection();
+ });
+ }
+}