# HG changeset patch # User michaelm # Date 1529687420 -3600 # Node ID bc104aaf24e9ce76fb89f40b3c1220e51b69588e # Parent e541c1b68b893412a37aeea58a44faaa1c235883 8204233: Add configurable option for enhanced socket IOException messages Reviewed-by: alanb, chegar diff -r e541c1b68b89 -r bc104aaf24e9 src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java --- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Fri Jun 22 18:19:26 2018 +0200 +++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Fri Jun 22 18:10:20 2018 +0100 @@ -37,6 +37,7 @@ import sun.net.ConnectionResetException; import sun.net.NetHooks; import sun.net.ResourceManager; +import sun.net.util.SocketExceptions; /** * Default Socket Implementation. This implementation does @@ -415,7 +416,7 @@ } } catch (IOException e) { close(); - throw e; + throw SocketExceptions.of(e, new InetSocketAddress(address, port)); } } diff -r e541c1b68b89 -r bc104aaf24e9 src/java.base/share/classes/sun/net/util/SocketExceptions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/net/util/SocketExceptions.java Fri Jun 22 18:10:20 2018 +0100 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018, 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.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Security; + +public final class SocketExceptions { + private SocketExceptions() {} + + /** + * Security or system property which specifies categories of + * (potentially sensitive) information that may be included + * in exception text. This class only defines one category: + * "hostInfo" which represents the hostname and port number + * of the remote peer relating to a socket exception. + * The property value is a comma separated list of + * case insignificant category names. + */ + private static final String enhancedTextPropname = "jdk.net.includeInExceptions"; + + private static final boolean enhancedExceptionText = initTextProp(); + + private static boolean initTextProp() { + return AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + String val = System.getProperty(enhancedTextPropname); + if (val == null) { + val = Security.getProperty(enhancedTextPropname); + if (val == null) + return false; + } + String[] tokens = val.split(","); + for (String token : tokens) { + if (token.equalsIgnoreCase("hostinfo")) + return true; + } + return false; + } + }); + } + + + /** + * 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.net.enhanceExceptionText" is not + * set or is false, then the original exception is returned. + * + * Only specific IOException subtypes are supported. + */ + public static IOException of(IOException e, InetSocketAddress address) { + if (!enhancedExceptionText || address == null) + return e; + int port = address.getPort(); + String host = address.getHostString(); + StringBuilder sb = new StringBuilder(); + sb.append(e.getMessage()); + sb.append(": "); + sb.append(host); + sb.append(':'); + sb.append(Integer.toString(port)); + 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 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 -r e541c1b68b89 -r bc104aaf24e9 src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Fri Jun 22 18:19:26 2018 +0200 +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Fri Jun 22 18:10:20 2018 +0100 @@ -54,6 +54,7 @@ import sun.net.NetHooks; import sun.net.ext.ExtendedSocketOptions; +import sun.net.util.SocketExceptions; import static sun.net.ext.ExtendedSocketOptions.SOCK_STREAM; /** @@ -706,7 +707,7 @@ } catch (IOException ioe) { // connect failed, close the channel close(); - throw ioe; + throw SocketExceptions.of(ioe, isa); } } @@ -792,7 +793,7 @@ } catch (IOException ioe) { // connect failed, close the channel close(); - throw ioe; + throw SocketExceptions.of(ioe, remoteAddress); } } diff -r e541c1b68b89 -r bc104aaf24e9 src/java.base/share/conf/security/java.security --- a/src/java.base/share/conf/security/java.security Fri Jun 22 18:19:26 2018 +0200 +++ b/src/java.base/share/conf/security/java.security Fri Jun 22 18:10:20 2018 +0100 @@ -993,10 +993,10 @@ # # An IOR type check filter, if configured, is used by an ORB during # an ORB::string_to_object invocation to check the veracity of the type encoded -# in the ior string. +# in the ior string. # # The filter pattern consists of a semi-colon separated list of class names. -# The configured list contains the binary class names of the IDL interface types +# The configured list contains the binary class names of the IDL interface types # corresponding to the IDL stub class to be instantiated. # As such, a filter specifies a list of IDL stub classes that will be # allowed by an ORB when an ORB::string_to_object is invoked. @@ -1025,3 +1025,16 @@ # and javax.crypto.spec.SecretKeySpec and rejects all the others. jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep;\ java.base/java.security.KeyRep$Type;java.base/javax.crypto.spec.SecretKeySpec;!* + +# +# Enhanced exception message text +# +# By default, socket exception messages do not include potentially sensitive +# information such as hostnames or port numbers. This property may be set to one +# or more values, separated by commas, and with no white-space. Each value +# represents a category of enhanced information. Currently, the only category defined +# is "hostInfo" which enables more detailed information in the IOExceptions +# thrown by java.net.Socket and also the socket types in the java.nio.channels package. +# The setting in this file can be overridden by a system property of the same name +# and with the same syntax and possible values. +#jdk.net.includeInExceptions=hostInfo diff -r e541c1b68b89 -r bc104aaf24e9 src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java --- a/src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java Fri Jun 22 18:19:26 2018 +0200 +++ b/src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java Fri Jun 22 18:10:20 2018 +0100 @@ -32,6 +32,7 @@ import java.io.IOException; import java.io.FileDescriptor; import sun.net.NetHooks; +import sun.net.util.SocketExceptions; import sun.security.action.GetPropertyAction; /** @@ -258,6 +259,10 @@ end(); } if (e != null) { + if (e instanceof IOException) { + var isa = (InetSocketAddress)pendingRemote; + e = SocketExceptions.of((IOException)e, isa); + } // close channel if connection cannot be established try { close(); @@ -350,6 +355,9 @@ // close channel if connect fails if (e != null) { + if (e instanceof IOException) { + e = SocketExceptions.of((IOException)e, isa); + } try { close(); } catch (Throwable suppressed) { diff -r e541c1b68b89 -r bc104aaf24e9 src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java --- a/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java Fri Jun 22 18:19:26 2018 +0200 +++ b/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java Fri Jun 22 18:10:20 2018 +0100 @@ -35,6 +35,7 @@ import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import jdk.internal.misc.Unsafe; +import sun.net.util.SocketExceptions; /** * Windows implementation of AsynchronousSocketChannel using overlapped I/O. @@ -253,7 +254,8 @@ if (exc != null) { closeChannel(); - result.setFailure(toIOException(exc)); + exc = SocketExceptions.of(toIOException(exc), remote); + result.setFailure(exc); } Invoker.invoke(result); } @@ -278,7 +280,9 @@ // can't close channel while in begin/end block if (exc != null) { closeChannel(); - result.setFailure(toIOException(exc)); + IOException ee = toIOException(exc); + ee = SocketExceptions.of(ee, remote); + result.setFailure(ee); } if (canInvokeDirect) { @@ -293,11 +297,13 @@ */ @Override public void failed(int error, IOException x) { + x = SocketExceptions.of(x, remote); if (isOpen()) { closeChannel(); result.setFailure(x); } else { - result.setFailure(new AsynchronousCloseException()); + x = SocketExceptions.of(new AsynchronousCloseException(), remote); + result.setFailure(x); } Invoker.invoke(result); } diff -r e541c1b68b89 -r bc104aaf24e9 test/jdk/java/net/Socket/ExceptionText.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/Socket/ExceptionText.java Fri Jun 22 18:10:20 2018 +0100 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018, 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. + */ + +/* + * @test + * @library /test/lib + * @build jdk.test.lib.Utils + * @bug 8204233 + * @summary Add configurable option for enhanced socket IOException messages + * @run main/othervm ExceptionText + * @run main/othervm -Djdk.net.includeInExceptions= ExceptionText + * @run main/othervm -Djdk.net.includeInExceptions=hostInfo ExceptionText + * @run main/othervm -Djdk.net.includeInExceptions=somethingElse ExceptionText + */ + +import java.net.*; +import java.io.IOException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.*; +import java.util.concurrent.ExecutionException; +import jdk.test.lib.Utils; + +public class ExceptionText { + + enum TestTarget {SOCKET, CHANNEL, ASYNC_CHANNEL}; + + static boolean propEnabled() { + String val = System.getProperty("jdk.net.includeInExceptions"); + if ("hostinfo".equalsIgnoreCase(val)) + return true; + return false; + } + + public static void main(String args[]) throws Exception { + boolean prop = propEnabled(); + test(prop); + } + + static final InetSocketAddress dest = Utils.refusingEndpoint(); + static final String PORT = ":" + Integer.toString(dest.getPort()); + static final String HOST = dest.getHostString(); + + static void test(boolean withProperty) { + // Socket + IOException e = getException(TestTarget.SOCKET); + checkResult(e, withProperty); + // SocketChannel + e = getException(TestTarget.CHANNEL); + checkResult(e, withProperty); + // AsyncSocketChannel + e = getException(TestTarget.ASYNC_CHANNEL); + checkResult(e, withProperty); + } + + static void checkResult(IOException e, boolean withProperty) { + String msg = e.getMessage(); + if (!withProperty) { + if (msg.contains(HOST) || msg.contains(PORT)) { + System.err.println("msg = " + msg); + throw new RuntimeException("Test failed: exception contains address info"); + } + } else { + if (!msg.contains(HOST) || !msg.contains(PORT)) { + if (e instanceof ClosedChannelException) + return; // has no detail msg + System.err.println("msg = " + msg); + throw new RuntimeException("Test failed: exception does not contain address info"); + } + } + } + + static IOException getException(TestTarget target) { + try { + if (target == TestTarget.SOCKET) { + Socket s = new Socket(); + s.connect(dest); + } else if (target == TestTarget.CHANNEL) { + SocketChannel c = SocketChannel.open(dest); + } else if (target == TestTarget.ASYNC_CHANNEL) { + AsynchronousSocketChannel c = AsynchronousSocketChannel.open(); + try { + c.connect(dest).get(); + } catch (InterruptedException | ExecutionException ee) { + if (ee.getCause() instanceof IOException) + throw (IOException)ee.getCause(); + throw new RuntimeException(ee.getCause()); + } + } + return null; + } catch (IOException e) { + e.printStackTrace(); + return e; + } + } +}