From 5460df7ee0405b2186d07620565d95dd9707e494 Mon Sep 17 00:00:00 2001 From: Alexey Bakhtin Date: Thu, 28 Aug 2025 18:45:20 -0700 Subject: [PATCH 1/3] Backport b6f827ef054959662190e21ce63fc3d3c45b92f3 --- .../share/classes/java/net/HostPortrange.java | 42 ++- .../share/classes/java/net/InetAddress.java | 46 ++- .../classes/java/net/NetworkInterface.java | 6 +- .../share/classes/java/net/Proxy.java | 12 +- .../classes/java/net/SocketPermission.java | 14 +- .../classes/java/net/SocksSocketImpl.java | 9 +- src/java.base/share/classes/java/net/URI.java | 14 +- src/java.base/share/classes/java/net/URL.java | 8 +- .../classes/java/net/URLStreamHandler.java | 11 +- .../classes/jdk/internal/util/Exceptions.java | 322 ++++++++++++++++++ src/java.base/share/classes/module-info.java | 4 + .../classes/sun/net/util/IPAddressUtil.java | 8 +- .../sun/net/util/SocketExceptions.java | 104 ------ .../share/classes/sun/net/www/ParseUtil.java | 4 +- .../net/www/protocol/https/HttpsClient.java | 9 +- .../sun/net/www/protocol/jar/Handler.java | 24 +- .../www/protocol/jar/JarURLConnection.java | 17 +- .../sun/net/www/protocol/jmod/Handler.java | 2 +- .../sun/nio/ch/DatagramSocketAdaptor.java | 7 +- .../classes/sun/nio/ch/NioSocketImpl.java | 9 +- .../classes/sun/nio/ch/SocketChannelImpl.java | 8 +- .../share/conf/security/java.security | 28 +- src/java.base/share/native/libnet/net_util.c | 29 +- src/java.base/share/native/libnet/net_util.h | 2 + .../ch/UnixAsynchronousSocketChannelImpl.java | 9 +- .../sun/nio/fs/UnixUserPrincipals.java | 12 +- .../unix/native/libnet/net_util_md.c | 31 +- .../WindowsAsynchronousSocketChannelImpl.java | 12 +- .../sun/nio/fs/WindowsSecurityDescriptor.java | 8 +- .../sun/nio/fs/WindowsUserPrincipals.java | 10 +- .../windows/native/libnet/Inet4AddressImpl.c | 7 +- .../windows/native/libnet/Inet6AddressImpl.c | 4 +- .../classes/com/sun/jndi/ldap/LdapURL.java | 5 +- .../classes/com/sun/jndi/toolkit/url/Uri.java | 32 +- .../net/http/HttpRequestBuilderImpl.java | 7 +- .../net/http/ResponseBodyHandlers.java | 10 +- .../net/http/websocket/OpeningHandshake.java | 10 +- .../share/classes/java/rmi/Naming.java | 29 +- test/jdk/java/net/URI/Test.java | 1 + .../TestJDKIncludeInExceptions.java | 6 +- test/jdk/sun/net/util/ExceptionsTest.java | 172 ++++++++++ 41 files changed, 839 insertions(+), 265 deletions(-) create mode 100644 src/java.base/share/classes/jdk/internal/util/Exceptions.java create mode 100644 test/jdk/sun/net/util/ExceptionsTest.java diff --git a/src/java.base/share/classes/java/net/HostPortrange.java b/src/java.base/share/classes/java/net/HostPortrange.java index 5e0696309d0..ab784f982d3 100644 --- a/src/java.base/share/classes/java/net/HostPortrange.java +++ b/src/java.base/share/classes/java/net/HostPortrange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -30,6 +30,9 @@ import java.util.Locale; import sun.net.util.IPAddressUtil; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; + /** * Parses a string containing a host/domain name and port range */ @@ -57,7 +60,7 @@ public int hashCode() { return hostname.hashCode() + portrange[0] + portrange[1]; } - HostPortrange(String scheme, String str) { + HostPortrange(String scheme, String host) { // Parse the host name. A name has up to three components, the // hostname, a port number, or two numbers representing a port // range. "www.example.com:8080-9090" is a valid host name. @@ -68,21 +71,23 @@ public int hashCode() { // Refer to RFC 2732 for more information. // first separate string into two fields: hoststr, portstr - String hoststr, portstr = null; + String hoststr = null, portstr = null; this.scheme = scheme; // check for IPv6 address - if (str.charAt(0) == '[') { + if (host.charAt(0) == '[') { ipv6 = literal = true; - int rb = str.indexOf(']'); + int rb = host.indexOf(']'); if (rb != -1) { - hoststr = str.substring(1, rb); + hoststr = host.substring(1, rb); } else { - throw new IllegalArgumentException("invalid IPv6 address: " + str); + throw new IllegalArgumentException( + formatMsg("invalid IPv6 address%s", + filterNonSocketInfo(host).prefixWith(": "))); } - int sep = str.indexOf(':', rb + 1); - if (sep != -1 && str.length() > sep) { - portstr = str.substring(sep + 1); + int sep = host.indexOf(':', rb + 1); + if (sep != -1 && host.length() > sep) { + portstr = host.substring(sep + 1); } // need to normalize hoststr now byte[] ip = IPAddressUtil.textToNumericFormatV6(hoststr); @@ -95,16 +100,16 @@ public int hashCode() { + "%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15]); - hostname = sb.toString(); + this.hostname = sb.toString(); } else { // not IPv6 therefore ':' is the port separator - int sep = str.indexOf(':'); - if (sep != -1 && str.length() > sep) { - hoststr = str.substring(0, sep); - portstr = str.substring(sep + 1); + int sep = host.indexOf(':'); + if (sep != -1 && host.length() > sep) { + hoststr = host.substring(0, sep); + portstr = host.substring(sep + 1); } else { - hoststr = sep == -1 ? str : str.substring(0, sep); + hoststr = sep == -1 ? host : host.substring(0, sep); } // is this a domain wildcard specification? if (hoststr.lastIndexOf('*') > 0) { @@ -151,13 +156,14 @@ public int hashCode() { } } } - hostname = hoststr; + this.hostname = hoststr; } try { portrange = parsePort(portstr); } catch (Exception e) { - throw new IllegalArgumentException("invalid port range: " + portstr); + throw new IllegalArgumentException( + formatMsg("invalid port range%s", filterNonSocketInfo(portstr).prefixWith(": "))); } } diff --git a/src/java.base/share/classes/java/net/InetAddress.java b/src/java.base/share/classes/java/net/InetAddress.java index ffc1e3cdbb2..7d462d9d839 100644 --- a/src/java.base/share/classes/java/net/InetAddress.java +++ b/src/java.base/share/classes/java/net/InetAddress.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -46,6 +46,7 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.Arrays; +import jdk.internal.util.Exceptions; import jdk.internal.access.JavaNetInetAddressAccess; import jdk.internal.access.SharedSecrets; import sun.security.action.*; @@ -53,6 +54,9 @@ import sun.net.util.IPAddressUtil; import sun.nio.cs.UTF_8; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; + /** * This class represents an Internet Protocol (IP) address. * @@ -340,6 +344,7 @@ public byte[] addressBytes(Inet6Address inet6Address) { } } ); + Exceptions.setup(); // needed for native exceptions init(); } @@ -798,7 +803,7 @@ private static final class CachedAddresses implements Addresses, Comparable res; @@ -1340,7 +1353,7 @@ private static InetAddress[] getAllByName(String host, InetAddress reqAddr) try { addr = IPAddressUtil.validateNumericFormatV4(host); } catch (IllegalArgumentException iae) { - var uhe = new UnknownHostException(host); + var uhe = new UnknownHostException(formatMsg("%s", filterNonSocketInfo(host))); uhe.initCause(iae); throw uhe; } @@ -1387,7 +1400,8 @@ private static InetAddress[] getAllByName(String host, InetAddress reqAddr) private static UnknownHostException invalidIPv6LiteralException(String host, boolean wrapInBrackets) { String hostString = wrapInBrackets ? "[" + host + "]" : host; - return new UnknownHostException(hostString + ": invalid IPv6 address literal"); + return new UnknownHostException(formatMsg("%sinvalid IPv6 address literal", + filterNonSocketInfo(hostString).suffixWith(": "))); } /** @@ -1667,8 +1681,8 @@ public static InetAddress getLocalHost() throws UnknownHostException { } catch (UnknownHostException uhe) { // Rethrow with a more informative error message. UnknownHostException uhe2 = - new UnknownHostException(local + ": " + - uhe.getMessage()); + new UnknownHostException(formatMsg(filterNonSocketInfo(local) + .suffixWith(": ") + uhe.getMessage())); uhe2.initCause(uhe); throw uhe2; } diff --git a/src/java.base/share/classes/java/net/NetworkInterface.java b/src/java.base/share/classes/java/net/NetworkInterface.java index c94d9034896..7602d0e605a 100644 --- a/src/java.base/share/classes/java/net/NetworkInterface.java +++ b/src/java.base/share/classes/java/net/NetworkInterface.java @@ -32,6 +32,8 @@ import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; /** * This class represents a Network Interface made up of a name, @@ -328,7 +330,9 @@ public static NetworkInterface getByInetAddress(InetAddress addr) throws SocketE + addr.holder.family); } } else { - throw new IllegalArgumentException("invalid address type: " + addr); + throw new IllegalArgumentException( + formatMsg("invalid address type%s", + filterNonSocketInfo(addr.toString()).prefixWith(": "))); } return getByInetAddress0(addr); } diff --git a/src/java.base/share/classes/java/net/Proxy.java b/src/java.base/share/classes/java/net/Proxy.java index eef964038b6..d4cec8f4652 100644 --- a/src/java.base/share/classes/java/net/Proxy.java +++ b/src/java.base/share/classes/java/net/Proxy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -25,6 +25,9 @@ package java.net; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; + /** * This class represents a proxy setting, typically a type (http, socks) and * a socket address. @@ -91,8 +94,11 @@ private Proxy() { * incompatible */ public Proxy(Type type, SocketAddress sa) { - if ((type == Type.DIRECT) || !(sa instanceof InetSocketAddress)) - throw new IllegalArgumentException("type " + type + " is not compatible with address " + sa); + if ((type == Type.DIRECT) || !(sa instanceof InetSocketAddress)) { + throw new IllegalArgumentException( + formatMsg("type " + type + " is not compatible with address %s", + filterNonSocketInfo(String.valueOf(sa)))); + } this.type = type; this.sa = sa; } diff --git a/src/java.base/share/classes/java/net/SocketPermission.java b/src/java.base/share/classes/java/net/SocketPermission.java index dab0b0427da..787a632a943 100644 --- a/src/java.base/share/classes/java/net/SocketPermission.java +++ b/src/java.base/share/classes/java/net/SocketPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -49,6 +49,8 @@ import sun.security.util.SecurityConstants; import sun.security.util.Debug; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; /** * This class represents access to a network via sockets. @@ -424,8 +426,8 @@ private void init(String host, int mask) { if (rb != -1) { host = host.substring(start, rb); } else { - throw new - IllegalArgumentException("invalid host/port: "+host); + throw new IllegalArgumentException( + formatMsg("invalid host/port%s", filterNonSocketInfo(host).prefixWith(": "))); } sep = hostport.indexOf(':', rb+1); } else { @@ -442,8 +444,8 @@ private void init(String host, int mask) { try { portrange = parsePort(port); } catch (Exception e) { - throw new - IllegalArgumentException("invalid port range: "+port); + throw new IllegalArgumentException( + formatMsg("invalid port range%s", filterNonSocketInfo(port).prefixWith(": "))); } } else { portrange = new int[] { PORT_MIN, PORT_MAX }; @@ -819,7 +821,7 @@ void getIP() throw uhe; } catch (IndexOutOfBoundsException iobe) { invalid = true; - throw new UnknownHostException(getName()); + throw new UnknownHostException(formatMsg("%s", filterNonSocketInfo(getName()))); } } diff --git a/src/java.base/share/classes/java/net/SocksSocketImpl.java b/src/java.base/share/classes/java/net/SocksSocketImpl.java index ce8e9a4ca33..841b3b6265d 100644 --- a/src/java.base/share/classes/java/net/SocksSocketImpl.java +++ b/src/java.base/share/classes/java/net/SocksSocketImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -37,6 +37,9 @@ import sun.net.spi.DefaultProxySelector; import sun.net.www.ParseUtil; +import static jdk.internal.util.Exceptions.filterSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; + /** * SOCKS (V4 & V5) TCP socket implementation (RFC 1928). */ @@ -375,7 +378,7 @@ public ProxySelector run() { // SOCKS Protocol version 4 doesn't know how to deal with // DOMAIN type of addresses (unresolved addresses here) if (epoint.isUnresolved()) - throw new UnknownHostException(epoint.toString()); + throw new UnknownHostException(formatMsg("%s", filterSocketInfo(epoint.toString()))); connectV4(in, out, epoint, deadlineMillis); return; } @@ -394,7 +397,7 @@ public ProxySelector run() { // SOCKS Protocol version 4 doesn't know how to deal with // DOMAIN type of addresses (unresolved addresses here) if (epoint.isUnresolved()) - throw new UnknownHostException(epoint.toString()); + throw new UnknownHostException(formatMsg("%s", filterSocketInfo(epoint.toString()))); connectV4(in, out, epoint, deadlineMillis); return; } diff --git a/src/java.base/share/classes/java/net/URI.java b/src/java.base/share/classes/java/net/URI.java index 644fc05bdc1..8a90f3e1cb2 100644 --- a/src/java.base/share/classes/java/net/URI.java +++ b/src/java.base/share/classes/java/net/URI.java @@ -42,11 +42,15 @@ import java.text.Normalizer; import jdk.internal.access.JavaNetUriAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.util.Exceptions; import sun.nio.cs.UTF_8; import java.lang.Character; // for javadoc import java.lang.NullPointerException; // for javadoc +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; + /** * Represents a Uniform Resource Identifier (URI) reference. * @@ -1995,7 +1999,8 @@ private static void checkPath(String s, String scheme, String path) { if (scheme != null) { if (path != null && !path.isEmpty() && path.charAt(0) != '/') - throw new URISyntaxException(s, "Relative path in absolute URI"); + throw new URISyntaxException(formatMsg("%s", filterNonSocketInfo(s)), + "Relative path in absolute URI"); } } @@ -2969,11 +2974,14 @@ private class Parser { // -- Methods for throwing URISyntaxException in various ways -- private void fail(String reason) throws URISyntaxException { - throw new URISyntaxException(input, reason); + throw new URISyntaxException(formatMsg("%s", filterNonSocketInfo(input)), reason); } private void fail(String reason, int p) throws URISyntaxException { - throw new URISyntaxException(input, reason, p); + if (!Exceptions.enhancedNonSocketExceptions()) { + p = -1; + } + throw new URISyntaxException(formatMsg("%s", filterNonSocketInfo(input)), reason, p); } private void failExpecting(String expected, int p) diff --git a/src/java.base/share/classes/java/net/URL.java b/src/java.base/share/classes/java/net/URL.java index 35d9503df19..af1d5b1c2aa 100644 --- a/src/java.base/share/classes/java/net/URL.java +++ b/src/java.base/share/classes/java/net/URL.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -50,6 +50,9 @@ import sun.security.util.SecurityConstants; import sun.security.action.GetPropertyAction; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; + /** * Class {@code URL} represents a Uniform Resource * Locator, a pointer to a "resource" on the World @@ -1056,7 +1059,8 @@ public URI toURI() throws URISyntaxException { URI uri = new URI(toString()); if (authority != null && isBuiltinStreamHandler(handler)) { String s = IPAddressUtil.checkAuthority(this); - if (s != null) throw new URISyntaxException(authority, s); + if (s != null) + throw new URISyntaxException(formatMsg("%s", filterNonSocketInfo(authority)), s); } return uri; } diff --git a/src/java.base/share/classes/java/net/URLStreamHandler.java b/src/java.base/share/classes/java/net/URLStreamHandler.java index 6b30d3cb43a..e41e8f83b24 100644 --- a/src/java.base/share/classes/java/net/URLStreamHandler.java +++ b/src/java.base/share/classes/java/net/URLStreamHandler.java @@ -34,6 +34,9 @@ import sun.net.util.IPAddressUtil; import sun.net.www.ParseUtil; +import static jdk.internal.util.Exceptions.formatMsg; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; + /** * The abstract class {@code URLStreamHandler} is the common * superclass for all stream protocol handlers. A stream protocol @@ -207,7 +210,7 @@ protected void parseURL(URL u, String spec, int start, int limit) { if (!IPAddressUtil. isIPv6LiteralAddress(host.substring(1, ind))) { throw new IllegalArgumentException( - "Invalid host: "+ host); + formatMsg("Invalid host%s", filterNonSocketInfo(host).prefixWith(": "))); } port = -1 ; @@ -221,12 +224,14 @@ protected void parseURL(URL u, String spec, int start, int limit) { } } else { throw new IllegalArgumentException( - "Invalid authority field: " + authority); + formatMsg("Invalid authority field%s", + filterNonSocketInfo(authority).prefixWith(": "))); } } } else { throw new IllegalArgumentException( - "Invalid authority field: " + authority); + formatMsg("Invalid authority field%s", + filterNonSocketInfo(authority).prefixWith(": "))); } } else { ind = host.indexOf(':'); diff --git a/src/java.base/share/classes/jdk/internal/util/Exceptions.java b/src/java.base/share/classes/jdk/internal/util/Exceptions.java new file mode 100644 index 00000000000..eb4286cd1af --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/util/Exceptions.java @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2018, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.internal.util; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnixDomainSocketAddress; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import sun.security.util.SecurityProperties; +import jdk.internal.misc.VM; + +/** + * Contains static utility methods which can filter exception + * message strings for sensitive information. + * + * Code using this mechanism should use formatMsg() + * to generate a formatted (enhanced or restricted) string for exception + * messages. + * + * The methods above take variable numbers of SensitiveInfo objects + * as parameters which contain the text that may have to be filtered. + * + * The SensitiveInfo objects should be generated with one of the following: + * public static SensitiveInfo filterSocketInfo(String s) + * public static SensitiveInfo filterNonSocketInfo(String s) + * public static SensitiveInfo filterJarName(String name) + * public static SensitiveInfo filterUserName(String name) + */ +public final class Exceptions { + private Exceptions() {} + + private static volatile boolean enhancedSocketExceptionText; + private static volatile boolean enhancedNonSocketExceptionText; + private static volatile boolean enhancedUserExceptionText; + private static volatile boolean enhancedJarExceptionText; + private static volatile boolean initialized = false; + + /** + * Base class for generating exception messages that may + * contain sensitive information which in certain contexts + * needs to be filtered out, in case it gets revealed in + * unexpected places. Exception messages are either enhanced + * or restricted. Enhanced messages include sensitive information. + * Restricted messages don't. + * + * Sub-class for any new category that needs to be independently + * controlled. Consider using a unique value for the + * SecurityProperties.includedInExceptions(String value) mechanism + * Current values defined are "jar", "userInfo" + * "hostInfo", "hostInfoExclSocket". + * + * New code can also piggy back on existing categories + * + * A SensitiveInfo contains the following components + * all of which default to empty strings. + * + * prefix, the sensitive info itself, a suffix + * and a replacement string. + * + * The composeFilteredText(boolean enhance) method generates + * an enhanced string when enhance is true. + * This comprises (enhance == true) + * prefix + info + suffix + * When (enhance == false), then by default the output is: + * "" empty string + * However, if a replacement is set, then when enhance == false + * the output is the replacement string. + */ + public abstract static class SensitiveInfo { + String info, suffix, prefix, replacement; + boolean enhanced; + + SensitiveInfo(String info) { + this.info = info; + prefix = suffix = replacement = ""; + } + public SensitiveInfo prefixWith(String prefix) { + this.prefix = prefix; + return this; + } + public SensitiveInfo suffixWith(String suffix) { + this.suffix = suffix; + return this; + } + public SensitiveInfo replaceWith(String replacement) { + this.replacement = replacement; + return this; + } + + public boolean enhanced() { + return enhanced; + } + + /** + * Implementation should call composeFilteredText(boolean flag) + * where flag contains the boolean value of whether + * the category is enabled or not. + */ + public abstract String output(); + + protected String composeFilteredText(boolean enhance) { + if (enhance) { + this.enhanced = true; + return prefix + info + suffix; + } else { + return replacement; + } + } + } + + static final class SocketInfo extends SensitiveInfo { + public SocketInfo(String host) { + super(host); + } + @Override + public String output() { + setup(); + return super.composeFilteredText(enhancedSocketExceptionText); + } + } + + static final class NonSocketInfo extends SensitiveInfo { + public NonSocketInfo(String host) { + super(host); + } + @Override + public String output() { + setup(); + return super.composeFilteredText(enhancedNonSocketExceptionText); + } + } + + static final class JarInfo extends SensitiveInfo { + public JarInfo(String name) { + super(name); + } + @Override + public String output() { + setup(); + return super.composeFilteredText(enhancedJarExceptionText); + } + } + + static final class UserInfo extends SensitiveInfo { + public UserInfo(String host) { + super(host); + } + @Override + public String output() { + setup(); + return super.composeFilteredText(enhancedUserExceptionText); + } + } + + // remove leading, trailing and duplicated space characters + static String trim(String s) { + int len = s.length(); + if (len == 0) return s; + + StringBuilder sb = new StringBuilder(); + + // initial value deals with leading spaces + boolean inSpace = true; + for (int i=0; i 0 && sb.charAt(sblen - 1) == ' ') + sb.deleteCharAt(sblen - 1); + return sb.toString(); + } + + public static SensitiveInfo filterSocketInfo(String host) { + return new SocketInfo(host); + } + + public static SensitiveInfo filterNonSocketInfo(String host) { + return new NonSocketInfo(host); + } + + public static SensitiveInfo filterJarName(String name) { + return new JarInfo(name); + } + + public static SensitiveInfo filterUserName(String name) { + return new UserInfo(name); + } + + /** + * Transform each SensitiveInfo into a String argument which is passed + * to String.format(). This string is then trimmed. + */ + public static String formatMsg(String format, SensitiveInfo... infos) { + String[] args = new String[infos.length]; + + int i = 0; + + for (SensitiveInfo info : infos) { + args[i++] = info.output(); + } + return trim(String.format(format, (Object[])args)); + } + + /** + * Simplification of above. Equivalent to: + * formatMsg("%s", SensitiveInfo[1]); // ie with one arg + */ + public static String formatMsg(SensitiveInfo info) { + return trim(info.output()); + } + + public static void setup() { + if (initialized || !VM.isBooted()) + return; + enhancedSocketExceptionText = SecurityProperties.includedInExceptions("hostInfo"); + enhancedNonSocketExceptionText = SecurityProperties.includedInExceptions("hostInfoExclSocket") + | enhancedSocketExceptionText; + + enhancedUserExceptionText = SecurityProperties.includedInExceptions("userInfo"); + enhancedJarExceptionText = SecurityProperties.INCLUDE_JAR_NAME_IN_EXCEPTIONS; + initialized = true; + } + + public static boolean enhancedNonSocketExceptions() { + setup(); + return enhancedNonSocketExceptionText; + } + + public static boolean enhancedSocketExceptions() { + setup(); + return enhancedSocketExceptionText; + } + + /** + * The enhanced message text is the socket address appended to + * the original IOException message + */ + public static IOException ioException(IOException e, SocketAddress addr) { + setup(); + if (addr == null) { + return e; + } + if (!enhancedSocketExceptionText) { + return create(e, e.getMessage()); + } + if (addr instanceof UnixDomainSocketAddress) { + return ofUnixDomain(e, (UnixDomainSocketAddress)addr); + } else if (addr instanceof InetSocketAddress) { + return ofInet(e, (InetSocketAddress)addr); + } else { + return e; + } + } + + private static IOException ofInet(IOException e, InetSocketAddress addr) { + return create(e, String.join(": ", e.getMessage(), addr.toString())); + } + + private static IOException ofUnixDomain(IOException e, UnixDomainSocketAddress addr) { + String path = addr.getPath().toString(); + StringBuilder sb = new StringBuilder(); + sb.append(e.getMessage()); + sb.append(": "); + sb.append(path); + String enhancedMsg = sb.toString(); + return create(e, enhancedMsg); + } + + // return a new instance of the same type with the given detail + // msg, or if the type doesn't support detail msgs, return given + // instance. + private static T create(T e, String msg) { + try { + Class clazz = e.getClass(); + @SuppressWarnings("unchecked") + Constructor ctor = (Constructor)clazz.getConstructor(String.class); + T e1 = (ctor.newInstance(msg)); + e1.setStackTrace(e.getStackTrace()); + return e1; + } catch (Exception e0) { + // Some eg AsynchronousCloseException have no detail msg + return e; + } + } +} diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 8f1ecae3ed1..58521244f78 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -336,9 +336,13 @@ java.security.jgss, java.security.sasl, java.smartcardio, + java.naming, + java.rmi, + java.net.http, java.xml.crypto, jdk.crypto.ec, jdk.crypto.cryptoki, + jdk.httpserver, jdk.jartool, jdk.security.auth, jdk.security.jgss; diff --git a/src/java.base/share/classes/sun/net/util/IPAddressUtil.java b/src/java.base/share/classes/sun/net/util/IPAddressUtil.java index 7edb843699f..d4220a62c78 100644 --- a/src/java.base/share/classes/sun/net/util/IPAddressUtil.java +++ b/src/java.base/share/classes/sun/net/util/IPAddressUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -42,6 +42,9 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; +import static jdk.internal.util.Exceptions.formatMsg; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; + public class IPAddressUtil { private static final int INADDR4SZ = 4; private static final int INADDR16SZ = 16; @@ -145,7 +148,8 @@ public static byte[] validateNumericFormatV4(String src) { byte[] parsedBytes = textToNumericFormatV4(src); if (!ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP_VALUE && parsedBytes == null && isBsdParsableV4(src)) { - throw new IllegalArgumentException("Invalid IP address literal: " + src); + throw new IllegalArgumentException( + formatMsg("Invalid IP address literal%s", filterNonSocketInfo(src).prefixWith(": "))); } return parsedBytes; } diff --git a/src/java.base/share/classes/sun/net/util/SocketExceptions.java b/src/java.base/share/classes/sun/net/util/SocketExceptions.java index ee70c58b9ba..e69de29bb2d 100644 --- a/src/java.base/share/classes/sun/net/util/SocketExceptions.java +++ b/src/java.base/share/classes/sun/net/util/SocketExceptions.java @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2018, 2023, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.net.util; - -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.net.InetSocketAddress; -import java.net.UnixDomainSocketAddress; -import java.net.SocketAddress; -import java.security.AccessController; -import java.security.PrivilegedAction; - -import sun.security.util.SecurityProperties; - -public final class SocketExceptions { - private SocketExceptions() {} - - private static final boolean enhancedExceptionText = - SecurityProperties.includedInExceptions("hostInfo"); - - /** - * Utility which takes an exception and returns either the same exception - * or a new exception of the same type with the same stack trace - * and detail message enhanced with addressing information from the - * given InetSocketAddress. - * - * If the system/security property "jdk.includeInExceptions" is not - * set or does not contain the category hostInfo, - * then the original exception is returned. - * - * Only specific IOException subtypes are supported. - */ - public static IOException of(IOException e, SocketAddress addr) { - if (!enhancedExceptionText || addr == null) { - return e; - } - if (addr instanceof UnixDomainSocketAddress) { - return ofUnixDomain(e, (UnixDomainSocketAddress)addr); - } else if (addr instanceof InetSocketAddress) { - return ofInet(e, (InetSocketAddress)addr); - } else { - return e; - } - } - - private static IOException ofInet(IOException e, InetSocketAddress addr) { - return create(e, String.join(": ", e.getMessage(), addr.toString())); - } - - private static IOException ofUnixDomain(IOException e, UnixDomainSocketAddress addr) { - String path = addr.getPath().toString(); - StringBuilder sb = new StringBuilder(); - sb.append(e.getMessage()); - sb.append(": "); - sb.append(path); - String enhancedMsg = sb.toString(); - return create(e, enhancedMsg); - } - - // return a new instance of the same type with the given detail - // msg, or if the type doesn't support detail msgs, return given - // instance. - - @SuppressWarnings("removal") - private static IOException create(IOException e, String msg) { - return AccessController.doPrivileged(new PrivilegedAction() { - public IOException run() { - try { - Class clazz = e.getClass(); - Constructor ctor = clazz.getConstructor(String.class); - IOException e1 = (IOException)(ctor.newInstance(msg)); - e1.setStackTrace(e.getStackTrace()); - return e1; - } catch (Exception e0) { - // Some eg AsynchronousCloseException have no detail msg - return e; - } - } - }); - } -} diff --git a/src/java.base/share/classes/sun/net/www/ParseUtil.java b/src/java.base/share/classes/sun/net/www/ParseUtil.java index 5b5885312d4..3957764d5ff 100644 --- a/src/java.base/share/classes/sun/net/www/ParseUtil.java +++ b/src/java.base/share/classes/sun/net/www/ParseUtil.java @@ -40,6 +40,8 @@ import java.util.HexFormat; import sun.nio.cs.UTF_8; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; /** * A class that contains useful routines common to sun.net.www @@ -496,7 +498,7 @@ private static void checkPath(String s, String scheme, String path) { if (scheme != null) { if (path != null && !path.isEmpty() && path.charAt(0) != '/') - throw new URISyntaxException(s, + throw new URISyntaxException(formatMsg("%s", filterNonSocketInfo(s)), "Relative path in absolute URI"); } } diff --git a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java index f6052c359a2..50acd280eeb 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java +++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java @@ -57,7 +57,8 @@ import sun.util.logging.PlatformLogger; import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*; - +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; /** * This class provides HTTPS client URL support, building on the standard @@ -666,8 +667,10 @@ private void checkURLSpoofing(HostnameVerifier hostnameVerifier) serverSocket.close(); session.invalidate(); - throw new IOException("HTTPS hostname wrong: should be <" - + url.getHost() + ">"); + throw new IOException(formatMsg("Wrong HTTPS hostname%s", + filterNonSocketInfo(url.getHost()) + .prefixWith(": should be <") + .suffixWith(">"))); } @Override diff --git a/src/java.base/share/classes/sun/net/www/protocol/jar/Handler.java b/src/java.base/share/classes/sun/net/www/protocol/jar/Handler.java index 48381c8a276..edf88d4d70f 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/jar/Handler.java +++ b/src/java.base/share/classes/sun/net/www/protocol/jar/Handler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -27,6 +27,8 @@ import java.io.IOException; import java.net.*; +import static jdk.internal.util.Exceptions.filterJarName; +import static jdk.internal.util.Exceptions.formatMsg; /* * Jar URL Handler @@ -181,8 +183,11 @@ private String parseAbsoluteSpec(String spec) { String innerSpec = spec.substring(0, index - 1); new URL(innerSpec); } catch (MalformedURLException e) { - throw new NullPointerException("invalid url: " + - spec + " (" + e + ")"); + throw new NullPointerException( + formatMsg("invalid url: %s %s", filterJarName(spec), + filterJarName(e.getMessage()) + .prefixWith("(") + .suffixWith(")"))); } return spec; } @@ -193,19 +198,18 @@ private String parseContextSpec(URL url, String spec) { if (spec.startsWith("/")) { int bangSlash = indexOfBangSlash(ctxFile); if (bangSlash == -1) { - throw new NullPointerException("malformed " + - "context url:" + - url + - ": no !/"); + throw new NullPointerException( + formatMsg("malformed context url%s : no !/", + filterJarName(String.valueOf(url)).prefixWith(": "))); } ctxFile = ctxFile.substring(0, bangSlash); } else { // chop up the last component int lastSlash = ctxFile.lastIndexOf('/'); if (lastSlash == -1) { - throw new NullPointerException("malformed " + - "context url:" + - url); + throw new NullPointerException( + formatMsg("malformed context url%s", + filterJarName(String.valueOf(url)).prefixWith(": "))); } else if (lastSlash < ctxFile.length() - 1) { ctxFile = ctxFile.substring(0, lastSlash + 1); } diff --git a/src/java.base/share/classes/sun/net/www/protocol/jar/JarURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/jar/JarURLConnection.java index 725b4cb95a0..06d09ce6e36 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/jar/JarURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/jar/JarURLConnection.java @@ -37,6 +37,9 @@ import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import static jdk.internal.util.Exceptions.filterJarName; +import static jdk.internal.util.Exceptions.formatMsg; + /** * @author Benjamin Renaud @@ -126,9 +129,10 @@ public void connect() throws IOException { factory.closeIfNotCached(url, jarFile); } catch (Exception e) { } - throw new FileNotFoundException("JAR entry " + entryName + - " not found in " + - jarFile.getName()); + throw new FileNotFoundException( + formatMsg("JAR entry %s not found in jar file %s", + filterJarName(entryName), + filterJarName(jarFile.getName()))); } } @@ -166,9 +170,10 @@ public InputStream getInputStream() throws IOException { throw new IOException("no entry name specified"); } else { if (jarEntry == null) { - throw new FileNotFoundException("JAR entry " + entryName + - " not found in " + - jarFile.getName()); + throw new FileNotFoundException( + formatMsg("JAR entry %s not found in jar file %s", + filterJarName(entryName), + filterJarName(jarFile.getName()))); } result = new JarURLInputStream (jarFile.getInputStream(jarEntry)); } diff --git a/src/java.base/share/classes/sun/net/www/protocol/jmod/Handler.java b/src/java.base/share/classes/sun/net/www/protocol/jmod/Handler.java index 95bc2179a31..51a88cb229d 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/jmod/Handler.java +++ b/src/java.base/share/classes/sun/net/www/protocol/jmod/Handler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * 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 diff --git a/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java b/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java index abe33000cda..e604da9729f 100644 --- a/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java +++ b/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -55,6 +55,8 @@ import java.util.concurrent.locks.ReentrantLock; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static jdk.internal.util.Exceptions.formatMsg; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; /** * A multicast datagram socket based on a datagram channel. @@ -562,7 +564,8 @@ public void setInterface(InetAddress inf) throws SocketException { NetworkInterface ni = NetworkInterface.getByInetAddress(inf); if (ni == null) { String address = inf.getHostAddress(); - throw new SocketException("No network interface with address " + address); + throw new SocketException(formatMsg("No network interface found with address %s", + filterNonSocketInfo(address))); } synchronized (outgoingInterfaceLock) { // set interface and update cached values diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java index f25002c8d69..31c58741cc7 100644 --- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java @@ -56,10 +56,12 @@ import sun.net.PlatformSocketImpl; import sun.net.ResourceManager; import sun.net.ext.ExtendedSocketOptions; -import sun.net.util.SocketExceptions; +import jdk.internal.util.Exceptions; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; /** * NIO based SocketImpl. @@ -569,7 +571,8 @@ protected void connect(SocketAddress remote, int millis) throws IOException { throw new IOException("Unsupported address type"); InetSocketAddress isa = (InetSocketAddress) remote; if (isa.isUnresolved()) { - throw new UnknownHostException(isa.getHostName()); + throw new UnknownHostException( + formatMsg(filterNonSocketInfo(isa.getHostName()))); } InetAddress address = isa.getAddress(); @@ -624,7 +627,7 @@ protected void connect(SocketAddress remote, int millis) throws IOException { } } catch (IOException ioe) { close(); - throw SocketExceptions.of(ioe, isa); + throw Exceptions.ioException(ioe, isa); } } diff --git a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java index 477fa1afca2..1d124287910 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -62,7 +62,7 @@ import sun.net.ConnectionResetException; import sun.net.NetHooks; import sun.net.ext.ExtendedSocketOptions; -import sun.net.util.SocketExceptions; +import jdk.internal.util.Exceptions; /** * An implementation of SocketChannels @@ -876,7 +876,7 @@ public boolean connect(SocketAddress remote) throws IOException { } catch (IOException ioe) { // connect failed, close the channel close(); - throw SocketExceptions.of(ioe, sa); + throw Exceptions.ioException(ioe, sa); } } @@ -965,7 +965,7 @@ public boolean finishConnect() throws IOException { } catch (IOException ioe) { // connect failed, close the channel close(); - throw SocketExceptions.of(ioe, remoteAddress); + throw Exceptions.ioException(ioe, remoteAddress); } } @@ -1197,7 +1197,7 @@ void blockingConnect(SocketAddress remote, long nanos) throws IOException { } catch (IOException ioe) { // connect failed, close the channel close(); - throw SocketExceptions.of(ioe, sa); + throw Exceptions.ioException(ioe, sa); } } diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index d14dcc45cb6..a622e082966 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1243,8 +1243,13 @@ jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep # # Enhanced exception message information # -# By default, exception messages should not include potentially sensitive -# information such as file names, host names, or port numbers. This property +# Exception messages may include potentially sensitive information such as file +# names, host names, or port numbers. By default, socket related exceptions +# have this information restricted (meaning the sensitive details are removed). +# Exception messages relating to JAR files and exceptions containing user +# identity information are also restricted by default. +# This property can be used to relax this restriction or to place further +# restrictions on other categories, defined below. The property # accepts one or more comma separated values, each of which represents a # category of enhanced exception message information to enable. Values are # case-insensitive. Leading and trailing whitespaces, surrounding each value, @@ -1257,17 +1262,28 @@ jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep # # The categories are: # -# hostInfo - IOExceptions thrown by java.net.Socket and the socket types in the -# java.nio.channels package will contain enhanced exception -# message information +# hostInfo - All networking related exceptions will contain enhanced +# exception message information. +# +# hostInfoExclSocket - The hostInfo category defined above, excluding +# IOExceptions thrown by java.net.Socket and the NetworkChannel +# types in the java.nio.channels package, will contain enhanced +# exception message information # # jar - enables more detailed information in the IOExceptions thrown # by classes in the java.util.jar package # +# userInfo - enables more detailed information in exceptions which may contain +# user identity information +# # The property setting in this file can be overridden by a system property of # the same name, with the same syntax and possible values. # -#jdk.includeInExceptions=hostInfo,jar +# If the property is not set or set to an empty string, then this is the most +# restricted setting with all categories disabled. The following is the default +# (out of the box) setting, meaning these categories are not restricted. +# +jdk.includeInExceptions=hostInfoExclSocket # # Disabled mechanisms for the Simple Authentication and Security Layer (SASL) diff --git a/src/java.base/share/native/libnet/net_util.c b/src/java.base/share/native/libnet/net_util.c index bedd7711c30..e943922f22f 100644 --- a/src/java.base/share/native/libnet/net_util.c +++ b/src/java.base/share/native/libnet/net_util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -82,9 +82,36 @@ DEF_JNI_OnLoad(JavaVM *vm, void *reserved) REUSEPORT_available = reuseport_supported(); platformInit(); + return JNI_VERSION_1_2; } +static int enhancedExceptionsInitialized = 0; +static int enhancedExceptionsAllowed = -1; + +#define CHECK_NULL_THROW_ERROR(X) \ + if (X == NULL) { \ + JNU_ThrowByName(env, "java/lang/InternalError", \ + "can't initialize enhanced exceptions"); \ + return -1; \ + } + +int getEnhancedExceptionsAllowed(JNIEnv *env) { + jclass cls; + jfieldID fid; + + if (enhancedExceptionsInitialized) { + return enhancedExceptionsAllowed; + } + cls = (*env)->FindClass(env, "jdk/internal/util/Exceptions"); + CHECK_NULL_THROW_ERROR(cls); + fid = (*env)->GetStaticFieldID(env, cls, "enhancedNonSocketExceptionText", "Z"); + CHECK_NULL_THROW_ERROR(fid); + enhancedExceptionsAllowed = (*env)->GetStaticBooleanField(env, cls, fid); + enhancedExceptionsInitialized = 1; + return enhancedExceptionsAllowed; +} + static int initialized = 0; JNIEXPORT void JNICALL initInetAddressIDs(JNIEnv *env) { diff --git a/src/java.base/share/native/libnet/net_util.h b/src/java.base/share/native/libnet/net_util.h index 0eb46911359..53743355486 100644 --- a/src/java.base/share/native/libnet/net_util.h +++ b/src/java.base/share/native/libnet/net_util.h @@ -200,4 +200,6 @@ unsigned short in_cksum(unsigned short *addr, int len); jint NET_Wait(JNIEnv *env, jint fd, jint flags, jint timeout); +int getEnhancedExceptionsAllowed(JNIEnv *env); + #endif /* NET_UTILS_H */ diff --git a/src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java b/src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java index 49960b7ed21..5ef9e64d2eb 100644 --- a/src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java +++ b/src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -32,9 +32,10 @@ import java.io.IOException; import java.io.FileDescriptor; +import jdk.internal.util.Exceptions; + import sun.net.ConnectionResetException; import sun.net.NetHooks; -import sun.net.util.SocketExceptions; import sun.security.action.GetPropertyAction; /** @@ -265,7 +266,7 @@ private void finishConnect(boolean mayInvokeDirect) { if (e != null) { if (e instanceof IOException) { var isa = (InetSocketAddress)pendingRemote; - e = SocketExceptions.of((IOException)e, isa); + e = Exceptions.ioException((IOException)e, isa); } // close channel if connection cannot be established try { @@ -362,7 +363,7 @@ Future implConnect(SocketAddress remote, // close channel if connect fails if (e != null) { if (e instanceof IOException) { - e = SocketExceptions.of((IOException)e, isa); + e = Exceptions.ioException((IOException)e, isa); } try { close(); diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixUserPrincipals.java b/src/java.base/unix/classes/sun/nio/fs/UnixUserPrincipals.java index 5b7612ac710..ea8be36a349 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixUserPrincipals.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixUserPrincipals.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -29,6 +29,9 @@ import java.io.IOException; import static sun.nio.fs.UnixNativeDispatcher.*; +import static jdk.internal.util.Exceptions.filterUserName; +import static jdk.internal.util.Exceptions.formatMsg; + /** * Unix implementation of java.nio.file.attribute.UserPrincipal */ @@ -138,18 +141,19 @@ private static int lookupName(String name, boolean isGroup) if (sm != null) { sm.checkPermission(new RuntimePermission("lookupUserInformation")); } - int id; + int id = -1; try { id = (isGroup) ? getgrnam(name) : getpwnam(name); } catch (UnixException x) { - throw new IOException(name + ": " + x.errorString()); + throw new IOException(formatMsg("%s " + x.errorString(), + filterUserName(name).suffixWith(": "))); } if (id == -1) { // lookup failed, allow input to be uid or gid try { id = Integer.parseInt(name); } catch (NumberFormatException ignore) { - throw new UserPrincipalNotFoundException(name); + throw new UserPrincipalNotFoundException(formatMsg("%s", filterUserName(name))); } } return id; diff --git a/src/java.base/unix/native/libnet/net_util_md.c b/src/java.base/unix/native/libnet/net_util_md.c index 139cb27b4fb..a6bb2c12c44 100644 --- a/src/java.base/unix/native/libnet/net_util_md.c +++ b/src/java.base/unix/native/libnet/net_util_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -212,23 +212,36 @@ void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env, { int size; char *buf; - const char *format = "%s: %s"; const char *error_string = gai_strerror(gai_error); if (error_string == NULL) error_string = "unknown error"; + int enhancedExceptions = getEnhancedExceptionsAllowed(env); + + if (enhancedExceptions) { + size = strlen(hostname); + } else { + size = 0; + } + size += strlen(error_string) + 3; - size = strlen(format) + strlen(hostname) + strlen(error_string) + 2; buf = (char *) malloc(size); if (buf) { jstring s; - snprintf(buf, size, format, hostname, error_string); - s = JNU_NewStringPlatform(env, buf); - if (s != NULL) { - jobject x = JNU_NewObjectByName(env, + int n; + if (enhancedExceptions) { + n = snprintf(buf, size, "%s: %s", hostname, error_string); + } else { + n = snprintf(buf, size, " %s", error_string); + } + if (n >= 0) { + s = JNU_NewStringPlatform(env, buf); + if (s != NULL) { + jobject x = JNU_NewObjectByName(env, "java/net/UnknownHostException", "(Ljava/lang/String;)V", s); - if (x != NULL) - (*env)->Throw(env, x); + if (x != NULL) + (*env)->Throw(env, x); + } } free(buf); } diff --git a/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java b/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java index a91b7726b58..869fceb70cd 100644 --- a/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java +++ b/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -34,8 +34,8 @@ import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; +import jdk.internal.util.Exceptions; import jdk.internal.misc.Unsafe; -import sun.net.util.SocketExceptions; /** * Windows implementation of AsynchronousSocketChannel using overlapped I/O. @@ -254,7 +254,7 @@ public void run() { if (exc != null) { closeChannel(); - exc = SocketExceptions.of(toIOException(exc), remote); + exc = Exceptions.ioException(toIOException(exc), remote); result.setFailure(exc); } Invoker.invoke(result); @@ -281,7 +281,7 @@ public void completed(int bytesTransferred, boolean canInvokeDirect) { if (exc != null) { closeChannel(); IOException ee = toIOException(exc); - ee = SocketExceptions.of(ee, remote); + ee = Exceptions.ioException(ee, remote); result.setFailure(ee); } @@ -297,12 +297,12 @@ public void completed(int bytesTransferred, boolean canInvokeDirect) { */ @Override public void failed(int error, IOException x) { - x = SocketExceptions.of(x, remote); + x = Exceptions.ioException(x, remote); if (isOpen()) { closeChannel(); result.setFailure(x); } else { - x = SocketExceptions.of(new AsynchronousCloseException(), remote); + x = Exceptions.ioException(new AsynchronousCloseException(), remote); result.setFailure(x); } Invoker.invoke(result); diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java b/src/java.base/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java index da3460592f4..8c3dfa5546e 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -33,6 +33,8 @@ import static sun.nio.fs.WindowsNativeDispatcher.*; import static sun.nio.fs.WindowsConstants.*; +import static jdk.internal.util.Exceptions.filterUserName; +import static jdk.internal.util.Exceptions.formatMsg; /** * A SecurityDescriptor for use when setting a file's ACL or creating a file @@ -136,8 +138,8 @@ private WindowsSecurityDescriptor(List acl) throws IOException { Math.max(SIZEOF_ACCESS_ALLOWED_ACE, SIZEOF_ACCESS_DENIED_ACE); } catch (WindowsException x) { - throw new IOException("Failed to get SID for " + user.getName() - + ": " + x.errorString()); + throw new IOException(formatMsg("Failed to get SID %s : " + x.errorString(), + filterUserName(user.getName()).prefixWith("for "))); } } diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsUserPrincipals.java b/src/java.base/windows/classes/sun/nio/fs/WindowsUserPrincipals.java index 2458443eb90..5d1549f2d7e 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsUserPrincipals.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsUserPrincipals.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -29,6 +29,8 @@ import static sun.nio.fs.WindowsConstants.*; import static sun.nio.fs.WindowsNativeDispatcher.*; +import static jdk.internal.util.Exceptions.formatMsg; +import static jdk.internal.util.Exceptions.filterUserName; class WindowsUserPrincipals { private WindowsUserPrincipals() { } @@ -146,7 +148,8 @@ static UserPrincipal lookup(String name) throws IOException { } catch (WindowsException x) { if (x.lastError() == ERROR_NONE_MAPPED) throw new UserPrincipalNotFoundException(name); - throw new IOException(name + ": " + x.errorString()); + throw new IOException(formatMsg("%s " + x.errorString(), + filterUserName(name).suffixWith(": "))); } assert size > 0; @@ -162,7 +165,8 @@ static UserPrincipal lookup(String name) throws IOException { // return user principal return fromSid(sidBuffer.address()); } catch (WindowsException x) { - throw new IOException(name + ": " + x.errorString()); + throw new IOException(formatMsg("%s " + x.errorString(), + filterUserName(name).suffixWith(": "))); } finally { sidBuffer.release(); } diff --git a/src/java.base/windows/native/libnet/Inet4AddressImpl.c b/src/java.base/windows/native/libnet/Inet4AddressImpl.c index 56adec28c72..ccc9d4c06ee 100644 --- a/src/java.base/windows/native/libnet/Inet4AddressImpl.c +++ b/src/java.base/windows/native/libnet/Inet4AddressImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -88,8 +88,9 @@ Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, if (error) { // report error - NET_ThrowByNameWithLastError(env, "java/net/UnknownHostException", - hostname); + NET_ThrowByNameWithLastError( + env, "java/net/UnknownHostException", + getEnhancedExceptionsAllowed(env) ? hostname : ""); goto cleanupAndReturn; } else { int i = 0; diff --git a/src/java.base/windows/native/libnet/Inet6AddressImpl.c b/src/java.base/windows/native/libnet/Inet6AddressImpl.c index 8941506da77..3cb37368131 100644 --- a/src/java.base/windows/native/libnet/Inet6AddressImpl.c +++ b/src/java.base/windows/native/libnet/Inet6AddressImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -83,7 +83,7 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, if (error) { // report error NET_ThrowByNameWithLastError(env, "java/net/UnknownHostException", - hostname); + getEnhancedExceptionsAllowed(env) ? hostname : ""); goto cleanupAndReturn; } else { int i = 0, inetCount = 0, inet6Count = 0, inetIndex = 0, diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java index f26e086d6a7..2c1e7c83dc1 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java @@ -35,6 +35,8 @@ import java.util.StringTokenizer; import com.sun.jndi.toolkit.url.Uri; import com.sun.jndi.toolkit.url.UrlUtil; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; /* * Extract components of an LDAP URL. @@ -124,7 +126,8 @@ public LdapURL(String url) throws NamingException { @Override protected MalformedURLException newInvalidURISchemeException(String uri) { - return new MalformedURLException("Not an LDAP URL: " + uri); + return new MalformedURLException(formatMsg("Not an LDAP URL%s", + filterNonSocketInfo(uri).prefixWith(": "))); } @Override diff --git a/src/java.naming/share/classes/com/sun/jndi/toolkit/url/Uri.java b/src/java.naming/share/classes/com/sun/jndi/toolkit/url/Uri.java index 33b9621d484..818dfac500c 100644 --- a/src/java.naming/share/classes/com/sun/jndi/toolkit/url/Uri.java +++ b/src/java.naming/share/classes/com/sun/jndi/toolkit/url/Uri.java @@ -29,6 +29,8 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; /** @@ -232,6 +234,12 @@ private void parse(String uri, ParseMode mode) throws MalformedURLException { } } + private MalformedURLException newMalformedURLException(String prefix, String msg) { + return new MalformedURLException(prefix + + formatMsg(filterNonSocketInfo(msg) + .prefixWith(prefix.isEmpty()? "" : ": "))); + } + /* * Parses a URI string and sets this object's fields accordingly. * Use java.net.URI to validate the uri string syntax @@ -241,7 +249,7 @@ private void parseStrict(String uri) throws MalformedURLException { if (!isSchemeOnly(uri)) { URI u = new URI(uri); scheme = u.getScheme(); - if (scheme == null) throw new MalformedURLException("Invalid URI: " + uri); + if (scheme == null) throw newMalformedURLException("Invalid URI", uri); var auth = u.getRawAuthority(); hasAuthority = auth != null; if (hasAuthority) { @@ -253,7 +261,7 @@ private void parseStrict(String uri) throws MalformedURLException { + (port == -1 ? "" : (":" + port)); if (!hostport.equals(auth)) { // throw if we have user info or regname - throw new MalformedURLException("unsupported authority: " + auth); + throw newMalformedURLException("unsupported authority", auth); } } path = u.getRawPath(); @@ -262,7 +270,7 @@ private void parseStrict(String uri) throws MalformedURLException { } if (u.getRawFragment() != null) { if (!acceptsFragment()) { - throw new MalformedURLException("URI fragments not supported: " + uri); + throw newMalformedURLException("URI fragments not supported", uri); } fragment = "#" + u.getRawFragment(); } @@ -279,7 +287,7 @@ private void parseStrict(String uri) throws MalformedURLException { path = ""; } } catch (URISyntaxException e) { - var mue = new MalformedURLException(e.getMessage()); + var mue = newMalformedURLException("", e.getMessage()); mue.initCause(e); throw mue; } @@ -299,11 +307,11 @@ private void parseCompat(String uri) throws MalformedURLException { int qmark = uri.indexOf('?'); int fmark = uri.indexOf('#'); if (i < 0 || slash > 0 && i > slash || qmark > 0 && i > qmark || fmark > 0 && i > fmark) { - throw new MalformedURLException("Invalid URI: " + uri); + throw newMalformedURLException("Invalid URI", uri); } if (fmark > -1) { if (!acceptsFragment()) { - throw new MalformedURLException("URI fragments not supported: " + uri); + throw newMalformedURLException("URI fragments not supported", uri); } } if (i == uri.length() - 1) { @@ -349,25 +357,25 @@ private void parseCompat(String uri) throws MalformedURLException { String f = u.getRawFragment(); String ui = u.getRawUserInfo(); if (ui != null) { - throw new MalformedURLException("user info not supported in authority: " + ui); + throw newMalformedURLException("user info not supported in authority: ", ui); } if (!"/".equals(p)) { - throw new MalformedURLException("invalid authority: " + auth); + throw newMalformedURLException("invalid authority: ", auth); } if (q != null) { - throw new MalformedURLException("invalid trailing characters in authority: ?" + q); + throw newMalformedURLException("invalid trailing characters in authority: ?", q); } if (f != null) { - throw new MalformedURLException("invalid trailing characters in authority: #" + f); + throw newMalformedURLException("invalid trailing characters in authority: #", f); } String hostport = (host == null ? "" : host) + (port == -1?"":(":" + port)); if (!auth.equals(hostport)) { // throw if we have user info or regname - throw new MalformedURLException("unsupported authority: " + auth); + throw newMalformedURLException("unsupported authority: ", auth); } } catch (URISyntaxException e) { - var mue = new MalformedURLException(e.getMessage()); + var mue = newMalformedURLException("", e.getMessage()); mue.initCause(e); throw mue; } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestBuilderImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestBuilderImpl.java index d11a18f7c58..5963081ea9e 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestBuilderImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -39,6 +39,8 @@ import static jdk.internal.net.http.common.Utils.isValidName; import static jdk.internal.net.http.common.Utils.isValidValue; import static jdk.internal.net.http.common.Utils.newIAE; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; public class HttpRequestBuilderImpl implements HttpRequest.Builder { @@ -82,7 +84,8 @@ static void checkURI(URI uri) { throw newIAE("invalid URI scheme %s", scheme); } if (uri.getHost() == null) { - throw newIAE("unsupported URI %s", uri); + throw new IllegalArgumentException( + formatMsg("unsupported URI %s", filterNonSocketInfo(uri.toString()))); } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java index 22e03238d21..110f6ab2bf6 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -50,6 +50,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import jdk.internal.net.http.ResponseSubscribers.PathSubscriber; +import jdk.internal.util.Exceptions; import static java.util.regex.Pattern.CASE_INSENSITIVE; public final class ResponseBodyHandlers { @@ -257,7 +258,12 @@ static boolean allowedInToken(char c) { static final UncheckedIOException unchecked(ResponseInfo rinfo, String msg) { - String s = String.format("%s in response [%d, %s]", msg, rinfo.statusCode(), rinfo.headers()); + String s; + if (Exceptions.enhancedNonSocketExceptions()) { + s = String.format("%s in response [%d, %s]", msg, rinfo.statusCode(), rinfo.headers()); + } else { + s = String.format("%s in response [%d]", msg, rinfo.statusCode()); + } return new UncheckedIOException(new IOException(s)); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/websocket/OpeningHandshake.java b/src/java.net.http/share/classes/jdk/internal/net/http/websocket/OpeningHandshake.java index 14cdd8854bb..8875248d5fa 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/websocket/OpeningHandshake.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/websocket/OpeningHandshake.java @@ -70,6 +70,8 @@ import static jdk.internal.net.http.common.Utils.isValidName; import static jdk.internal.net.http.common.Utils.permissionForProxy; import static jdk.internal.net.http.common.Utils.stringOf; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; public class OpeningHandshake { @@ -349,9 +351,13 @@ private static URI checkURI(URI uri) { if (!("ws".equalsIgnoreCase(scheme) || "wss".equalsIgnoreCase(scheme))) throw illegal("invalid URI scheme: " + scheme); if (uri.getHost() == null) - throw illegal("URI must contain a host: " + uri); + throw new IllegalArgumentException( + formatMsg("URI must contain a host%s", + filterNonSocketInfo(uri.toString()).prefixWith(": "))); if (uri.getFragment() != null) - throw illegal("URI must not contain a fragment: " + uri); + throw new IllegalArgumentException( + formatMsg("URI must not contain a fragment%s", + filterNonSocketInfo(uri.toString()).prefixWith(": "))); return uri; } diff --git a/src/java.rmi/share/classes/java/rmi/Naming.java b/src/java.rmi/share/classes/java/rmi/Naming.java index c4677991770..cb8766564a9 100644 --- a/src/java.rmi/share/classes/java/rmi/Naming.java +++ b/src/java.rmi/share/classes/java/rmi/Naming.java @@ -28,6 +28,8 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.formatMsg; /** * The Naming class provides methods for storing and obtaining @@ -221,6 +223,11 @@ private static Registry getRegistry(ParsedNamingURL parsed) return LocateRegistry.getRegistry(parsed.host, parsed.port); } + private static MalformedURLException newMalformedURLException(String prefix, String msg) { + return new MalformedURLException( + prefix + formatMsg(filterNonSocketInfo(msg).prefixWith(": "))); + } + /** * Dissect Naming URL strings to obtain referenced host, port and * object name. @@ -240,8 +247,8 @@ private static ParsedNamingURL parseURL(String str) * '//:' forms will result in a URI syntax exception * Convert the authority to a localhost: form */ - MalformedURLException mue = new MalformedURLException( - "invalid URL String: " + str); + MalformedURLException mue = newMalformedURLException( + "invalid URL String", str); mue.initCause(ex); int indexSchemeEnd = str.indexOf(':'); int indexAuthorityBegin = str.indexOf("//:"); @@ -272,22 +279,22 @@ private static ParsedNamingURL intParseURL(String str) { URI uri = new URI(str); if (uri.isOpaque()) { - throw new MalformedURLException( - "not a hierarchical URL: " + str); + throw newMalformedURLException( + "not a hierarchical URL", str); } if (uri.getFragment() != null) { - throw new MalformedURLException( - "invalid character, '#', in URL name: " + str); + throw newMalformedURLException( + "invalid character, '#', in URL name", str); } else if (uri.getQuery() != null) { - throw new MalformedURLException( - "invalid character, '?', in URL name: " + str); + throw newMalformedURLException( + "invalid character, '?', in URL name", str); } else if (uri.getUserInfo() != null) { - throw new MalformedURLException( - "invalid character, '@', in URL host: " + str); + throw newMalformedURLException( + "invalid character, '@', in URL host", str); } String scheme = uri.getScheme(); if (scheme != null && !scheme.equals("rmi")) { - throw new MalformedURLException("invalid URL scheme: " + str); + throw newMalformedURLException("invalid URL scheme", str); } String name = uri.getPath(); diff --git a/test/jdk/java/net/URI/Test.java b/test/jdk/java/net/URI/Test.java index c099f3bb552..27583044737 100644 --- a/test/jdk/java/net/URI/Test.java +++ b/test/jdk/java/net/URI/Test.java @@ -26,6 +26,7 @@ * @bug 4464135 4505046 4503239 4438319 4991359 4866303 7023363 7041800 * 7171415 6339649 6933879 8037396 8272072 * @author Mark Reinhold + * @run main/othervm Test */ import java.io.ByteArrayInputStream; diff --git a/test/jdk/jdk/security/JavaDotSecurity/TestJDKIncludeInExceptions.java b/test/jdk/jdk/security/JavaDotSecurity/TestJDKIncludeInExceptions.java index 353309c1654..011fc6bbaf4 100644 --- a/test/jdk/jdk/security/JavaDotSecurity/TestJDKIncludeInExceptions.java +++ b/test/jdk/jdk/security/JavaDotSecurity/TestJDKIncludeInExceptions.java @@ -28,7 +28,7 @@ * @bug 8207846 8208691 * @summary Test the default setting of the jdk.net.includeInExceptions * security property - * @comment In OpenJDK, this property is empty by default and on purpose. + * @comment In OpenJDK, this property has value "hostInfoExclSocket" by default * This test assures the default is not changed. * @run main TestJDKIncludeInExceptions */ @@ -36,9 +36,9 @@ public class TestJDKIncludeInExceptions { public static void main(String args[]) throws Exception { String incInExc = Security.getProperty("jdk.includeInExceptions"); - if (incInExc != null) { + if (incInExc == null || !incInExc.equals("hostInfoExclSocket")) { throw new RuntimeException("Test failed: default value of " + - "jdk.includeInExceptions security property is not null: " + + "jdk.includeInExceptions security property does not have expected value: " + incInExc); } } diff --git a/test/jdk/sun/net/util/ExceptionsTest.java b/test/jdk/sun/net/util/ExceptionsTest.java new file mode 100644 index 00000000000..d6717763afb --- /dev/null +++ b/test/jdk/sun/net/util/ExceptionsTest.java @@ -0,0 +1,172 @@ +/* + * 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 java.io.IOException; +import java.net.InetAddress; +import java.util.Arrays; +import jdk.internal.util.Exceptions; +import jdk.internal.util.Exceptions.SensitiveInfo; +import static jdk.internal.util.Exceptions.formatMsg; +import static jdk.internal.util.Exceptions.filterNonSocketInfo; +import static jdk.internal.util.Exceptions.enhancedNonSocketExceptions; + +/* + * @test + * @bug 8348986 + * @summary Improve coverage of enhanced exception messages + * @modules java.base/jdk.internal.util + * @run main/othervm -Djdk.includeInExceptions=hostInfo ExceptionsTest + * @run main/othervm ExceptionsTest + * @run main/othervm -Djdk.includeInExceptions=userInfo ExceptionsTest + * @run main/othervm -Djdk.net.hosts.file=does.not.exist -Djdk.includeInExceptions=userInfo ExceptionsTest + * @run main/othervm -Djdk.net.hosts.file=does.not.exist -Djdk.includeInExceptions=hostInfo ExceptionsTest + */ +public class ExceptionsTest { + + static boolean netEnabled() { + System.out.printf("netEnabled = %b\n", enhancedNonSocketExceptions()); + return enhancedNonSocketExceptions(); + } + + static boolean dnsEnabled() { + System.out.printf("dnsEnabled = %b\n", enhancedNonSocketExceptions()); + return enhancedNonSocketExceptions(); + } + + static boolean hostFileEnabled() { + return System.getProperty("jdk.net.hosts.file", "").length() > 0; + } + + static String[][][] tests = { + // + // If a format argument is of the form ".pre(xxx)" or ".suf(yyy)", then that is + // interpreted as a .prefixWith("xxx") or .suffixWith("yyy") call to the preceding + // argument. .rep() signifies .replaceWith() + // + // Number of elements in array + // --------------------------- + // 1 N 2 + // + // Format string args to format Enhanced o/p non-enhanced o/p + // + /* 1 */ {{"foo: %s bar"}, {"abc"}, {"foo: abc bar", "foo: bar"}}, + /* 2 */ {{"foo: %s bar"}, {"a", "b"}, {"foo: a bar", "foo: bar"}}, + /* 3 */ {{"foo: %s bar"}, {null}, {"foo: null bar", "foo: bar"}}, + /* 4 */ {{"foo: %s bar"}, {""}, {"foo: bar", "foo: bar"}}, + /* 5 */ {{"%s foo: %s bar"}, {"a", "b"}, {"a foo: b bar", "foo: bar"}}, + /* 6 */ {{"foo: %s bar %s"}, {"a", "b"}, {"foo: a bar b", "foo: bar"}}, + /* 7 */ {{"foo: %s bar %s"}, {"abc", "def"}, {"foo: abc bar def", "foo: bar"}}, + /* 8 */ {{"%s bar %s"}, {"abc", ".pre(foo: )", "def"}, {"foo: abc bar def", "bar"}}, + /* 9 */ {{"%s baz"}, {"abc", ".suf(: bar)"}, {"abc: bar baz", "baz"}}, + /* 10 */ {{"%s baz"}, {"abc", ".suf(: bar)" + , ".rep(bob)"}, {"abc: bar baz", "bob baz"}} + }; + + + static void dnsTest() { + String host = "fub.z.a.bar.foo"; + try { + var addr = InetAddress.getByName(host); + } catch (IOException e) { + if (!dnsEnabled() && e.toString().contains(host)) + throw new RuntimeException("Name lookup failed"); + } + } + + static void hostFileTest() { + String result1 = "Unable to resolve host www.rte.ie as hosts file does.not.exist not found"; + String result2 = "Unable to resolve host as hosts file " + + "from ${jdk.net.hosts.file} system property not found"; + + try { + var a = InetAddress.getByName("www.rte.ie"); + } catch (IOException e) { + if (dnsEnabled() && !e.toString().contains(result1)) { + System.out.println("Lookup failed: " + e.toString()); + throw new RuntimeException("Name lookup failed"); + } + if (!dnsEnabled() && !e.toString().contains(result2)) { + System.out.println("Lookup failed: " + e.toString()); + throw new RuntimeException("Name lookup failed"); + } + } + } + + + final static String PRE = ".pre("; + final static String SUF = ".suf("; + final static String REP = ".rep("; + + static SensitiveInfo[] getArgs(String[] args) { + SensitiveInfo[] sa = new SensitiveInfo[args.length]; + + int index = 0; + for (String s : args) { + if (s != null && s.startsWith(PRE)) { + var preArg = s.substring(PRE.length(), s.indexOf(')')); + sa[index-1] = sa[index-1].prefixWith(preArg); + } else if (s != null && s.startsWith(SUF)) { + var sufArg = s.substring(SUF.length(), s.indexOf(')')); + sa[index-1] = sa[index-1].suffixWith(sufArg); + } else if (s != null && s.startsWith(REP)) { + var repArg = s.substring(REP.length(), s.indexOf(')')); + sa[index-1] = sa[index-1].replaceWith(repArg); + } else { + sa[index++] = filterNonSocketInfo(s); + } + } + return Arrays.copyOf(sa, index); + } + + public static void main(String[] a) { + if (!hostFileEnabled()) { + dnsTest(); + } else { + hostFileTest(); + return; + } + + int count = 1; + for (String[][] test : tests) { + String format = test[0][0]; + String expectedEnhanced = test[2][0]; + String expectedNormal = test[2][1]; + SensitiveInfo[] args = getArgs(test[1]); + + String output = formatMsg(format, args); + if (netEnabled()) { + if (!output.equals(expectedEnhanced)) { + var msg = String.format("FAIL %d: got: \"%s\" Expected: \"%s\"", count, + output, expectedEnhanced); + throw new RuntimeException(msg); + } + } else { + if (!output.equals(expectedNormal)) { + var msg = String.format("FAIL %d: got: \"%s\" Expected: \"%s\"", count, + output, expectedNormal); + throw new RuntimeException(msg); + } + } + count++; + } + } +} From 33997ab2e1b61052225e6214e672528681cc8085 Mon Sep 17 00:00:00 2001 From: Alexey Bakhtin Date: Mon, 29 Sep 2025 14:21:58 -0700 Subject: [PATCH 2/3] Add accessClassInPackage.jdk.internal.util permission for java.net.http module --- src/java.base/share/lib/security/default.policy | 1 + 1 file changed, 1 insertion(+) diff --git a/src/java.base/share/lib/security/default.policy b/src/java.base/share/lib/security/default.policy index 9bd5dd53bd3..04efaa86896 100644 --- a/src/java.base/share/lib/security/default.policy +++ b/src/java.base/share/lib/security/default.policy @@ -20,6 +20,7 @@ grant codeBase "jrt:/java.net.http" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.ref"; + permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.util"; permission java.lang.RuntimePermission "modifyThread"; permission java.net.SocketPermission "*","connect,resolve"; permission java.net.URLPermission "http:*","*:*"; From 55181504ad555829c3092470bf150669edac5385 Mon Sep 17 00:00:00 2001 From: Alexey Bakhtin Date: Mon, 29 Sep 2025 19:08:51 -0700 Subject: [PATCH 3/3] Fix review findings --- .../classes/java/net/AbstractPlainSocketImpl.java | 4 ++-- src/java.base/share/classes/module-info.java | 10 +++++----- .../share/classes/sun/net/util/SocketExceptions.java | 0 .../classes/sun/net/www/protocol/jmod/Handler.java | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) delete mode 100644 src/java.base/share/classes/sun/net/util/SocketExceptions.java diff --git a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java index 43737455082..6c512a356a2 100644 --- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java +++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java @@ -44,7 +44,7 @@ import sun.net.ResourceManager; import sun.net.ext.ExtendedSocketOptions; import sun.net.util.IPAddressUtil; -import sun.net.util.SocketExceptions; +import jdk.internal.util.Exceptions; /** * Default Socket Implementation. This implementation does @@ -535,7 +535,7 @@ synchronized void doConnect(InetAddress address, int port, int timeout) throws I } } catch (IOException e) { close(); - throw SocketExceptions.of(e, new InetSocketAddress(address, port)); + throw Exceptions.ioException(e, new InetSocketAddress(address, port)); } } diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 58521244f78..98fe71e3da2 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -249,7 +249,11 @@ exports jdk.internal.vm.vector to jdk.incubator.vector; exports jdk.internal.util to - jdk.incubator.foreign; + java.naming, + java.rmi, + java.net.http, + jdk.httpserver, + jdk.incubator.foreign; exports jdk.internal.util.jar to jdk.jartool; exports jdk.internal.util.xml to @@ -336,13 +340,9 @@ java.security.jgss, java.security.sasl, java.smartcardio, - java.naming, - java.rmi, - java.net.http, java.xml.crypto, jdk.crypto.ec, jdk.crypto.cryptoki, - jdk.httpserver, jdk.jartool, jdk.security.auth, jdk.security.jgss; diff --git a/src/java.base/share/classes/sun/net/util/SocketExceptions.java b/src/java.base/share/classes/sun/net/util/SocketExceptions.java deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/java.base/share/classes/sun/net/www/protocol/jmod/Handler.java b/src/java.base/share/classes/sun/net/www/protocol/jmod/Handler.java index 51a88cb229d..7ea31f51032 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/jmod/Handler.java +++ b/src/java.base/share/classes/sun/net/www/protocol/jmod/Handler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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