1 /* 2 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @run main/othervm DeadSSLLdapTimeoutTest 27 * @bug 8141370 28 * @key intermittent 29 */ 30 31 import java.net.Socket; 32 import java.net.ServerSocket; 33 import java.net.SocketTimeoutException; 34 import java.io.*; 35 import javax.naming.*; 36 import javax.naming.directory.*; 37 import java.util.List; 38 import java.util.Hashtable; 39 import java.util.ArrayList; 40 import java.util.concurrent.Callable; 41 import java.util.concurrent.ExecutionException; 42 import java.util.concurrent.Executors; 43 import java.util.concurrent.ExecutorService; 44 import java.util.concurrent.Future; 45 import java.util.concurrent.ScheduledExecutorService; 46 import java.util.concurrent.ScheduledFuture; 47 import java.util.concurrent.TimeoutException; 48 import java.util.concurrent.TimeUnit; 49 import javax.net.ssl.SSLHandshakeException; 50 51 import static java.util.concurrent.TimeUnit.MILLISECONDS; 52 import static java.util.concurrent.TimeUnit.NANOSECONDS; 53 54 55 class DeadServerTimeoutSSLTest implements Callable { 56 57 Hashtable env; 58 DeadSSLServer server; 59 boolean passed = false; 60 private int HANGING_TEST_TIMEOUT = 20_000; 61 62 public DeadServerTimeoutSSLTest(Hashtable env) throws IOException { 63 this.server = new DeadSSLServer(); 64 this.env = env; 65 } 66 67 public void performOp(InitialContext ctx) throws NamingException {} 68 69 public void handleNamingException(NamingException e, long start, long end) { 70 if (e.getCause() instanceof SocketTimeoutException 71 || e.getCause().getCause() instanceof SocketTimeoutException) { 72 // SSL connect will timeout via readReply using 73 // SocketTimeoutException 74 e.printStackTrace(); 75 pass(); 76 } else if (e.getCause() instanceof SSLHandshakeException 77 && e.getCause().getCause() instanceof EOFException) { 78 // test seems to be failing intermittently on some 79 // platforms. 80 pass(); 81 } else { 82 fail(e); 83 } 84 } 85 86 public void pass() { 87 this.passed = true; 88 } 89 90 public void fail() { 91 throw new RuntimeException("Test failed"); 92 } 93 94 public void fail(Exception e) { 95 throw new RuntimeException("Test failed", e); 96 } 97 98 boolean shutItDown(InitialContext ctx) { 99 try { 100 if (ctx != null) ctx.close(); 101 return true; 102 } catch (NamingException ex) { 103 return false; 104 } 105 } 106 107 public Boolean call() { 108 InitialContext ctx = null; 109 ScheduledFuture killer = null; 110 long start = System.nanoTime(); 111 112 try { 113 while(!server.accepting()) 114 Thread.sleep(200); // allow the server to start up 115 Thread.sleep(200); // to be sure 116 117 env.put(Context.PROVIDER_URL, "ldap://localhost:" + 118 server.getLocalPort()); 119 120 try { 121 ctx = new InitialDirContext(env); 122 performOp(ctx); 123 fail(); 124 } catch (NamingException e) { 125 long end = System.nanoTime(); 126 System.out.println(this.getClass().toString() + " - elapsed: " 127 + NANOSECONDS.toMillis(end - start)); 128 handleNamingException(e, start, end); 129 } finally { 130 if (killer != null && !killer.isDone()) 131 killer.cancel(true); 132 shutItDown(ctx); 133 server.close(); 134 } 135 return passed; 136 } catch (IOException|InterruptedException e) { 137 throw new RuntimeException(e); 138 } 139 } 140 } 141 142 class DeadSSLServer extends Thread { 143 ServerSocket serverSock; 144 boolean accepting = false; 145 146 public DeadSSLServer() throws IOException { 147 this.serverSock = new ServerSocket(0); 148 start(); 149 } 150 151 public void run() { 152 while(true) { 153 try { 154 accepting = true; 155 Socket socket = serverSock.accept(); 156 } catch (Exception e) { 157 break; 158 } 159 } 160 } 161 162 public int getLocalPort() { 163 return serverSock.getLocalPort(); 164 } 165 166 public boolean accepting() { 167 return accepting; 168 } 169 170 public void close() throws IOException { 171 serverSock.close(); 172 } 173 } 174 175 public class DeadSSLLdapTimeoutTest { 176 177 static Hashtable createEnv() { 178 Hashtable env = new Hashtable(11); 179 env.put(Context.INITIAL_CONTEXT_FACTORY, 180 "com.sun.jndi.ldap.LdapCtxFactory"); 181 return env; 182 } 183 184 public static void main(String[] args) throws Exception { 185 186 InitialContext ctx = null; 187 188 // 189 // Running this test serially as it seems to tickle a problem 190 // on older kernels 191 // 192 // run the DeadServerTest with connect / read timeouts set 193 // and ssl enabled 194 // this should exit with a SocketTimeoutException as the root cause 195 // it should also use the connect timeout instead of the read timeout 196 System.out.println("Running connect timeout test with 10ms connect timeout, 3000ms read timeout & SSL"); 197 Hashtable sslenv = createEnv(); 198 sslenv.put("com.sun.jndi.ldap.connect.timeout", "10"); 199 sslenv.put("com.sun.jndi.ldap.read.timeout", "3000"); 200 sslenv.put(Context.SECURITY_PROTOCOL, "ssl"); 201 boolean testFailed = 202 (new DeadServerTimeoutSSLTest(sslenv).call()) ? false : true; 203 204 if (testFailed) { 205 throw new AssertionError("some tests failed"); 206 } 207 208 } 209 210 } 211