1 import java.net.Socket;
   2 import java.net.ServerSocket;
   3 import java.io.*;
   4 import javax.naming.*;
   5 import javax.naming.directory.*;
   6 import java.security.Permission;
   7 import java.util.Hashtable;
   8 import java.util.concurrent.*;
   9 
  10 /**
  11  * @test
  12  * @compile TestDnsProvider.java
  13  * @run main/othervm LdapDnsProviderTest
  14  * @modules java.naming/com.sun.jndi.ldap
  15  * @bug 8160768
  16  * @summary ctx provider tests for ldap
  17  */
  18 
  19 class DNSSecurityManager extends SecurityManager {
  20 
  21     private boolean perm = false;
  22 
  23     public void allowSetFactory() {
  24         perm = true;
  25     }
  26 
  27     @Override
  28     public void checkPermission(Permission p) {
  29         if (p.getName().equals("setFactory") && !perm) {
  30             throw new SecurityException(p.getName());
  31         }
  32     }
  33 }
  34 
  35 class ProviderTest implements Callable {
  36 
  37     private final String expected;
  38     private final ScheduledExecutorService killSwitchPool;
  39     private final LdapTestServer server;
  40     private final Hashtable env = new Hashtable(11);
  41     private final int HANGING_TEST_TIMEOUT = 1000;
  42 
  43     public ProviderTest(LdapTestServer server,
  44                         ScheduledExecutorService killSwitchPool,
  45                         String expected)
  46             throws IOException
  47     {
  48         this.server = server;
  49         this.killSwitchPool = killSwitchPool;
  50         this.expected = expected;
  51         env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
  52     }
  53 
  54     public ProviderTest(LdapTestServer server,
  55                         ScheduledExecutorService killSwitchPool,
  56                         String expected,
  57                         String dnsProvider)
  58             throws IOException
  59     {
  60         this(server, killSwitchPool, expected);
  61         env.put("com.sun.jndi.ldap.dnsProvider", dnsProvider);
  62     }
  63 
  64     boolean shutItDown(InitialContext ctx) {
  65         try {
  66             if (ctx != null) ctx.close();
  67             return true;
  68         } catch (NamingException ex) {
  69             return false;
  70         }
  71     }
  72 
  73     public Boolean call() {
  74         boolean passed;
  75         InitialContext ctx = null;
  76         ScheduledFuture killer = null;
  77         // String providerUrl = "ldap://localhost:" + server.getLocalPort();
  78         String providerUrl = "ldap:///dc=example,dc=com";
  79 
  80         try {
  81             while(!server.accepting()) {
  82                 Thread.sleep(200); // allow the server to start up
  83             }
  84             Thread.sleep(200); // to be sure
  85 
  86             // if this is a hanging test, scheduled a thread to
  87             // interrupt after a certain time
  88             if (killSwitchPool != null) {
  89                 final Thread current = Thread.currentThread();
  90                 killer = killSwitchPool.schedule(
  91                         (Callable<Void>) () -> {
  92                             current.interrupt();
  93                             return null;
  94                         }, HANGING_TEST_TIMEOUT, TimeUnit.MILLISECONDS);
  95             }
  96 
  97             env.put(Context.PROVIDER_URL, providerUrl);
  98 
  99             try {
 100                 ctx = new InitialDirContext(env);
 101                 SearchControls scl = new SearchControls();
 102                 scl.setSearchScope(SearchControls.SUBTREE_SCOPE);
 103                 ((InitialDirContext)ctx).search(
 104                         "ou=People,o=Test", "(objectClass=*)", scl);
 105                 throw new RuntimeException("Search should not complete");
 106             } catch (NamingException e) {
 107                 System.err.println(e);
 108                 passed = e.toString().indexOf(expected) > -1;
 109             } finally {
 110                 if (killer != null && !killer.isDone()) {
 111                     killer.cancel(true);
 112                 }
 113                 shutItDown(ctx);
 114                 server.close();
 115             }
 116             return passed;
 117         } catch (IOException|InterruptedException e) {
 118             throw new RuntimeException(e);
 119         }
 120     }
 121 }
 122 
 123 
 124 class LdapTestServer extends Thread {
 125     ServerSocket serverSock;
 126     boolean accepting = false;
 127     private byte[] bindResponse = {
 128         0x30, 0x0C, 0x02, 0x01, 0x01, 0x61, 0x07, 0x0A,
 129         0x01, 0x00, 0x04, 0x00, 0x04, 0x00
 130     };
 131 
 132     public LdapTestServer() throws IOException {
 133         this.serverSock = new ServerSocket(0);
 134         start();
 135     }
 136 
 137     public int getLocalPort() {
 138         return serverSock.getLocalPort();
 139     }
 140 
 141     public boolean accepting() {
 142         return accepting;
 143     }
 144 
 145     public void close() throws IOException {
 146         serverSock.close();
 147     }
 148 
 149     public void run() {
 150         try {
 151             accepting = true;
 152             Socket socket = serverSock.accept();
 153             InputStream in = socket.getInputStream();
 154             OutputStream out = socket.getOutputStream();
 155 
 156             // Read the LDAP BindRequest
 157             while (in.read() != -1) {
 158                 in.skip(in.available());
 159                 break;
 160             }
 161 
 162             // Write an LDAP BindResponse
 163             out.write(bindResponse);
 164             out.flush();
 165         } catch (IOException e) {
 166             // ignore
 167         }
 168     }
 169 }
 170 
 171 public class LdapDnsProviderTest {
 172 
 173     private static final ExecutorService testPool =
 174         Executors.newFixedThreadPool(1);
 175     private static final ScheduledExecutorService killSwitchPool =
 176         Executors.newScheduledThreadPool(1);
 177     private static DNSSecurityManager sm = new DNSSecurityManager();
 178 
 179     public static void main(String[] args) throws Exception {
 180         LdapTestServer server = new LdapTestServer();
 181         try {
 182             // TestDnsProvider, no SecurityManager
 183             runTest(server, "yupyupyup:389", TestDnsProvider.class.getName());
 184             // DefaultDnsProvider, no SecurityManager
 185             runTest(server, "localhost:389", "com.sun.jndi.ldap.DefaultLdapDnsProvider");
 186             // no (default) dnsProvider, no SecurityManager
 187             runTest(server, "localhost:389", "");
 188             // no dnsProvider context property, no SecurityManager
 189             runTest(server, "localhost:389");
 190 
 191             System.setSecurityManager(sm);
 192 
 193             // TestDnsProvider with SecurityManager and no permissions
 194             runTest(server,
 195                     "setFactory",
 196                     TestDnsProvider.class.getName());
 197 
 198             sm.allowSetFactory();
 199 
 200             // TestDnsProvider with SecurityManager and correct permissions
 201             runTest(server,
 202                     "yupyupyup:389",
 203                     TestDnsProvider.class.getName());
 204         } finally {
 205             LdapDnsProviderTest.killSwitchPool.shutdown();
 206             LdapDnsProviderTest.testPool.shutdown();
 207         }
 208     }
 209 
 210     private static boolean runTest(LdapTestServer server,
 211                                    String expected)
 212             throws IOException, InterruptedException, ExecutionException
 213     {
 214         Future test = testPool.submit(
 215                 new ProviderTest(
 216                         server, killSwitchPool, expected));
 217 
 218         while (!test.isDone()) {
 219             if ((Boolean) test.get()) {
 220                 return true;
 221             }
 222         }
 223         throw new AssertionError("FAILED: " + expected);
 224     }
 225 
 226     private static boolean runTest(LdapTestServer server,
 227                                    String expected,
 228                                    String dnsProvider)
 229             throws IOException, InterruptedException, ExecutionException
 230     {
 231         Future test = testPool.submit(
 232                 new ProviderTest(
 233                         server, killSwitchPool, expected, dnsProvider));
 234 
 235         while (!test.isDone()) {
 236             if ((Boolean) test.get()) {
 237                 return true;
 238             }
 239         }
 240         throw new AssertionError("FAILED: " + expected + ", " + dnsProvider);
 241     }
 242 
 243 }
 244