From a75edc29c6ce41116cc99530aa1710efb62c6d5a Mon Sep 17 00:00:00 2001 From: Aleksei Efimov Date: Fri, 25 Sep 2020 12:46:08 +0000 Subject: [PATCH] 8251188: Update LDAP tests not to use wildcard addresses Reviewed-by: dfuchs --- test/jdk/ProblemList.txt | 2 - .../sun/jndi/ldap/BalancedParentheses.java | 23 ++- .../sun/jndi/ldap/DeadSSLLdapTimeoutTest.java | 170 ++++++++++++------ .../sun/jndi/ldap/DeadSSLSocketFactory.java | 93 ++++++++++ .../jndi/ldap/blits/AddTests/AddNewEntry.java | 25 ++- .../com/sun/jndi/ldap/lib/LDAPTestUtils.java | 16 +- 6 files changed, 258 insertions(+), 71 deletions(-) create mode 100644 test/jdk/com/sun/jndi/ldap/DeadSSLSocketFactory.java diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index cbae666f1452c..602ea3cb1506c 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -889,8 +889,6 @@ sun/tools/jhsdb/BasicLauncherTest.java 8211767 linux-pp # jdk_other -com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java 8169942 linux-i586,macosx-all,windows-x64 - javax/rmi/ssl/SSLSocketParametersTest.sh 8162906 generic-all javax/script/Test7.java 8239361 generic-all diff --git a/test/jdk/com/sun/jndi/ldap/BalancedParentheses.java b/test/jdk/com/sun/jndi/ldap/BalancedParentheses.java index 2b62888f75afa..b4ea10313d85a 100644 --- a/test/jdk/com/sun/jndi/ldap/BalancedParentheses.java +++ b/test/jdk/com/sun/jndi/ldap/BalancedParentheses.java @@ -24,18 +24,23 @@ /** * @test * @bug 6449574 + * @library /test/lib * @summary Invalid ldap filter is accepted and processed */ import java.io.*; import javax.naming.*; import javax.naming.directory.*; -import java.util.Properties; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.Hashtable; import java.net.Socket; import java.net.ServerSocket; +import jdk.test.lib.net.URIBuilder; + public class BalancedParentheses { // Should we run the client or server in a separate thread? // @@ -54,7 +59,13 @@ public class BalancedParentheses { // If the server prematurely exits, serverReady will be set to true // to avoid infinite hangs. void doServerSide() throws Exception { - ServerSocket serverSock = new ServerSocket(serverPort); + // Create unbound server socket + ServerSocket serverSock = new ServerSocket(); + + // And bind it to the loopback address + SocketAddress sockAddr = new InetSocketAddress( + InetAddress.getLoopbackAddress(), 0); + serverSock.bind(sockAddr); // signal client, it's ready to accecpt connection serverPort = serverSock.getLocalPort(); @@ -106,7 +117,13 @@ void doClientSide() throws Exception { Hashtable env = new Hashtable<>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - env.put(Context.PROVIDER_URL, "ldap://localhost:" + serverPort); + // Construct the provider URL + String providerURL = URIBuilder.newBuilder() + .scheme("ldap") + .loopback() + .port(serverPort) + .build().toString(); + env.put(Context.PROVIDER_URL, providerURL); env.put("com.sun.jndi.ldap.read.timeout", "1000"); // env.put(Context.SECURITY_AUTHENTICATION, "simple"); diff --git a/test/jdk/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java b/test/jdk/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java index ea056f252373a..e72683391ab74 100644 --- a/test/jdk/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java +++ b/test/jdk/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java @@ -21,34 +21,35 @@ * questions. */ -/** +/* * @test - * @run main/othervm DeadSSLLdapTimeoutTest * @bug 8141370 * @key intermittent + * @library /test/lib + * @build DeadSSLSocketFactory + * @run main/othervm DeadSSLLdapTimeoutTest */ -import java.net.Socket; +import java.io.EOFException; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; import java.net.SocketTimeoutException; -import java.io.*; -import javax.naming.*; -import javax.naming.directory.*; -import java.util.List; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; import java.util.Hashtable; -import java.util.ArrayList; import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeoutException; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import javax.naming.directory.InitialDirContext; import javax.net.ssl.SSLHandshakeException; -import static java.util.concurrent.TimeUnit.MILLISECONDS; +import jdk.test.lib.net.URIBuilder; + import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -57,26 +58,26 @@ class DeadServerTimeoutSSLTest implements Callable { Hashtable env; DeadSSLServer server; boolean passed = false; - private int HANGING_TEST_TIMEOUT = 20_000; public DeadServerTimeoutSSLTest(Hashtable env) throws IOException { - this.server = new DeadSSLServer(); + SocketAddress sockAddr = new InetSocketAddress( + InetAddress.getLoopbackAddress(), 0); + this.server = new DeadSSLServer(sockAddr); this.env = env; } - public void performOp(InitialContext ctx) throws NamingException {} - - public void handleNamingException(NamingException e, long start, long end) { + public void handleNamingException(NamingException e) { if (e.getCause() instanceof SocketTimeoutException || e.getCause().getCause() instanceof SocketTimeoutException) { // SSL connect will timeout via readReply using // SocketTimeoutException - e.printStackTrace(); + System.out.println("PASS: Observed expected SocketTimeoutException"); pass(); } else if (e.getCause() instanceof SSLHandshakeException && e.getCause().getCause() instanceof EOFException) { // test seems to be failing intermittently on some // platforms. + System.out.println("PASS: Observed expected SSLHandshakeException/EOFException"); pass(); } else { fail(e); @@ -92,6 +93,7 @@ public void fail() { } public void fail(Exception e) { + System.err.println("FAIL: Unexpected exception was observed:" + e.getMessage()); throw new RuntimeException("Test failed", e); } @@ -106,34 +108,36 @@ boolean shutItDown(InitialContext ctx) { public Boolean call() { InitialContext ctx = null; - ScheduledFuture killer = null; - long start = System.nanoTime(); try { - while(!server.accepting()) - Thread.sleep(200); // allow the server to start up + server.serverStarted.await(); // Wait for the server to start-up Thread.sleep(200); // to be sure - env.put(Context.PROVIDER_URL, "ldap://localhost:" + - server.getLocalPort()); + env.put(Context.PROVIDER_URL, + URIBuilder.newBuilder() + .scheme("ldap") + .loopback() + .port(server.getLocalPort()) + .buildUnchecked().toString() + ); + long start = System.nanoTime(); try { ctx = new InitialDirContext(env); - performOp(ctx); fail(); } catch (NamingException e) { long end = System.nanoTime(); System.out.println(this.getClass().toString() + " - elapsed: " + NANOSECONDS.toMillis(end - start)); - handleNamingException(e, start, end); + handleNamingException(e); } finally { - if (killer != null && !killer.isDone()) - killer.cancel(true); + // Stop the server side thread + server.testDone.countDown(); shutItDown(ctx); server.close(); } return passed; - } catch (IOException|InterruptedException e) { + } catch (IOException | InterruptedException e) { throw new RuntimeException(e); } } @@ -141,20 +145,69 @@ public Boolean call() { class DeadSSLServer extends Thread { ServerSocket serverSock; - boolean accepting = false; - - public DeadSSLServer() throws IOException { - this.serverSock = new ServerSocket(0); + // Latch to be used by client to wait for server to start + CountDownLatch serverStarted = new CountDownLatch(1); + + // Latch to be used by server thread to wait for client to finish testing + CountDownLatch testDone = new CountDownLatch(1); + + public DeadSSLServer(SocketAddress socketAddress) throws IOException { + // create unbound server socket + var srvSock = new ServerSocket(); + // bind it to the address provided + srvSock.bind(socketAddress); + this.serverSock = srvSock; start(); } public void run() { - while(true) { - try { - accepting = true; - Socket socket = serverSock.accept(); + // Signal client to proceed with the test + serverStarted.countDown(); + while (true) { + try (var acceptedSocket = serverSock.accept()) { + System.err.println("Accepted connection:" + acceptedSocket); + int iteration = 0; + // Wait for socket to get opened by DeadSSLSocketFactory and connected to the test server + while (iteration++ < 20) { + if (DeadSSLSocketFactory.firstCreatedSocket.get() != null && + DeadSSLSocketFactory.firstCreatedSocket.get().isConnected()) { + break; + } + try { + TimeUnit.MILLISECONDS.sleep(50); + } catch (InterruptedException ie) { + } + } + Socket clientSideSocket = DeadSSLSocketFactory.firstCreatedSocket.get(); + System.err.printf("Got SSLSocketFactory connection after %d iterations: %s%n", + iteration, clientSideSocket); + + if (clientSideSocket == null || !clientSideSocket.isConnected()) { + // If after 1000 ms client side connection is not opened - probably other local process + // tried to connect to the test server socket. Close current connection and retry accept. + continue; + } else { + // Check if accepted socket is connected to the LDAP client + if (acceptedSocket.getLocalPort() == clientSideSocket.getPort() && + acceptedSocket.getPort() == clientSideSocket.getLocalPort() && + acceptedSocket.getInetAddress().equals(clientSideSocket.getLocalAddress())) { + System.err.println("Accepted connection is originated from LDAP client:" + acceptedSocket); + try { + // Give LDAP client time to fully establish the connection. + // When client is done - the accepted socket will be closed + testDone.await(); + } catch (InterruptedException e) { + } + break; + } else { + // If accepted socket is not from the LDAP client - the accepted connection will be closed and new + // one will be accepted + System.err.println("SSLSocketFactory connection has been established, but originated not from" + + " the test's LDAP client:" + acceptedSocket); + } + } } catch (Exception e) { - break; + System.err.println("Server socket. Failure to accept connection:" + e.getMessage()); } } } @@ -163,28 +216,26 @@ public int getLocalPort() { return serverSock.getLocalPort(); } - public boolean accepting() { - return accepting; - } - public void close() throws IOException { serverSock.close(); } } public class DeadSSLLdapTimeoutTest { + // com.sun.jndi.ldap.connect.timeout value to set + static final String CONNECT_TIMEOUT_MS = "10"; + + // com.sun.jndi.ldap.read.timeout value to set + static final String READ_TIMEOUT_MS = "3000"; static Hashtable createEnv() { Hashtable env = new Hashtable<>(11); env.put(Context.INITIAL_CONTEXT_FACTORY, - "com.sun.jndi.ldap.LdapCtxFactory"); + "com.sun.jndi.ldap.LdapCtxFactory"); return env; } public static void main(String[] args) throws Exception { - - InitialContext ctx = null; - // // Running this test serially as it seems to tickle a problem // on older kernels @@ -193,19 +244,24 @@ public static void main(String[] args) throws Exception { // and ssl enabled // this should exit with a SocketTimeoutException as the root cause // it should also use the connect timeout instead of the read timeout - System.out.println("Running connect timeout test with 10ms connect timeout, 3000ms read timeout & SSL"); + System.out.printf("Running connect timeout test with %sms connect timeout," + + " %sms read timeout & SSL%n", + CONNECT_TIMEOUT_MS, READ_TIMEOUT_MS); + Hashtable sslenv = createEnv(); - sslenv.put("com.sun.jndi.ldap.connect.timeout", "10"); - sslenv.put("com.sun.jndi.ldap.read.timeout", "3000"); + // Setup connect timeout environment property + sslenv.put("com.sun.jndi.ldap.connect.timeout", CONNECT_TIMEOUT_MS); + // Setup read timeout environment property + sslenv.put("com.sun.jndi.ldap.read.timeout", READ_TIMEOUT_MS); + // Setup DeadSSLSocketFactory to track the client's first LDAP connection + sslenv.put("java.naming.ldap.factory.socket", "DeadSSLSocketFactory"); + // Use SSL protocol sslenv.put(Context.SECURITY_PROTOCOL, "ssl"); - boolean testFailed = - (new DeadServerTimeoutSSLTest(sslenv).call()) ? false : true; + boolean testFailed = !new DeadServerTimeoutSSLTest(sslenv).call(); if (testFailed) { throw new AssertionError("some tests failed"); } - } - } diff --git a/test/jdk/com/sun/jndi/ldap/DeadSSLSocketFactory.java b/test/jdk/com/sun/jndi/ldap/DeadSSLSocketFactory.java new file mode 100644 index 0000000000000..2966abe95541c --- /dev/null +++ b/test/jdk/com/sun/jndi/ldap/DeadSSLSocketFactory.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020, 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 javax.net.SocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +/* + * A custom socket factory used to override the default socket factory and track the LDAP client connection. + * Factory can create only one SSLSocket. See the DeadServerTimeoutSSLTest test. + */ +public class DeadSSLSocketFactory extends SocketFactory { + // Client socket that is used by LDAP connection + public static AtomicReference firstCreatedSocket = new AtomicReference<>(); + + // Boolean to track if connection socket has been opened + public static AtomicBoolean isConnectionOpened = new AtomicBoolean(false); + + // Default SSLSocketFactory that will be used for SSL socket creation + final SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault(); + + // Create unconnected socket + public Socket createSocket() throws IOException { + if (!isConnectionOpened.getAndSet(true)) { + System.err.println("DeadSSLSocketFactory: Creating unconnected socket"); + firstCreatedSocket.set((SSLSocket) factory.createSocket()); + return firstCreatedSocket.get(); + } else { + throw new RuntimeException("DeadSSLSocketFactory only allows creation of one SSL socket"); + } + } + + public DeadSSLSocketFactory() { + System.err.println("DeadSSLSocketFactory: Constructor call"); + } + + public static SocketFactory getDefault() { + System.err.println("DeadSSLSocketFactory: acquiring DeadSSLSocketFactory as default socket factory"); + return new DeadSSLSocketFactory(); + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + // Not used by DeadSSLLdapTimeoutTest + return factory.createSocket(host, port); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, + int localPort) throws IOException { + // Not used by DeadSSLLdapTimeoutTest + return factory.createSocket(host, port, localHost, localPort); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + // Not used by DeadSSLLdapTimeoutTest + return factory.createSocket(host, port); + } + + @Override + public Socket createSocket(InetAddress address, int port, + InetAddress localAddress, int localPort) throws IOException { + // Not used by DeadSSLLdapTimeoutTest + return factory.createSocket(address, port, localAddress, localPort); + } +} + diff --git a/test/jdk/com/sun/jndi/ldap/blits/AddTests/AddNewEntry.java b/test/jdk/com/sun/jndi/ldap/blits/AddTests/AddNewEntry.java index 925286774535d..3b9f7c1b9a5ba 100644 --- a/test/jdk/com/sun/jndi/ldap/blits/AddTests/AddNewEntry.java +++ b/test/jdk/com/sun/jndi/ldap/blits/AddTests/AddNewEntry.java @@ -27,7 +27,7 @@ * @summary Verify capability to add a new entry to the directory using the * ADD operation. * @modules java.naming/com.sun.jndi.ldap - * @library ../../lib/ /javax/naming/module/src/test/test/ + * @library /test/lib ../../lib/ /javax/naming/module/src/test/test/ * @build LDAPServer LDAPTestUtils * @run main/othervm AddNewEntry */ @@ -41,19 +41,36 @@ import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.ServerSocket; +import java.net.SocketAddress; import java.util.Hashtable; +import jdk.test.lib.net.URIBuilder; public class AddNewEntry { public static void main(String[] args) throws Exception { - ServerSocket serverSocket = new ServerSocket(0); + // Create unbound server socket + ServerSocket serverSocket = new ServerSocket(); + + // Bind it to the loopback address + SocketAddress sockAddr = new InetSocketAddress( + InetAddress.getLoopbackAddress(), 0); + serverSocket.bind(sockAddr); + + // Construct the provider URL for LDAPTestUtils + String providerURL = URIBuilder.newBuilder() + .scheme("ldap") + .loopback() + .port(serverSocket.getLocalPort()) + .buildUnchecked().toString(); Hashtable env; // initialize test - env = LDAPTestUtils - .initEnv(serverSocket, AddNewEntry.class.getName(), args, true); + env = LDAPTestUtils.initEnv(serverSocket, providerURL, + AddNewEntry.class.getName(), args, true); /* Build attribute set */ String[] ids = { "objectClass", "sn", "cn", "telephoneNumber", "mail", diff --git a/test/jdk/com/sun/jndi/ldap/lib/LDAPTestUtils.java b/test/jdk/com/sun/jndi/ldap/lib/LDAPTestUtils.java index dc3ec74e06c5f..c44cc4aed98b2 100644 --- a/test/jdk/com/sun/jndi/ldap/lib/LDAPTestUtils.java +++ b/test/jdk/com/sun/jndi/ldap/lib/LDAPTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, 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,12 +50,17 @@ public class LDAPTestUtils { * Process command line arguments and return properties in a Hashtable. */ public static Hashtable initEnv(String testname, - String[] args) { + String[] args) { return initEnv(null, testname, args, false); } public static Hashtable initEnv(ServerSocket socket, - String testname, String[] args, boolean authInfo) { + String testname, String[] args, boolean authInfo) { + return initEnv(socket, null, testname, args, authInfo); + } + + public static Hashtable initEnv(ServerSocket socket, String providerUrl, + String testname, String[] args, boolean authInfo) { Hashtable env = new Hashtable<>(); String root = "o=IMC,c=US"; @@ -103,8 +108,9 @@ public static Hashtable initEnv(ServerSocket socket, if (socket != null) { env.put(TEST_LDAP_SERVER_THREAD, startLDAPServer(socket, getCaptureFile(testname))); - env.put("java.naming.provider.url", - "ldap://localhost:" + socket.getLocalPort()); + String url = providerUrl != null ? providerUrl : + "ldap://localhost:" + socket.getLocalPort(); + env.put("java.naming.provider.url", url); } else { // for tests which run against remote server or no server // required