1 /*
   2  * Copyright (c) 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 import javax.naming.Context;
  25 import javax.naming.NamingException;
  26 import javax.naming.directory.Attributes;
  27 import java.io.Closeable;
  28 import java.io.PrintStream;
  29 import java.net.DatagramSocket;
  30 import java.nio.file.Files;
  31 import java.nio.file.Path;
  32 import java.nio.file.Paths;
  33 import java.util.Hashtable;
  34 
  35 public class DNSTestUtils {
  36     public static final String TEST_DNS_SERVER_THREAD = "test.dns.server.thread";
  37     public static final String TEST_DNS_ROOT_URL = "test.dns.root.url";
  38     public static final int HOSTS_LOOKUP_MAX_DEPTH = 3;
  39 
  40     protected static boolean debug = true;
  41 
  42     /*
  43      * Check that attrs contains the mandatory attributes and the right
  44      * objectclass attribute
  45      */
  46     public static boolean checkSchema(Attributes attrs, String[] mandatory,
  47             String[] optional) {
  48         // Check mandatory attributes
  49         for (String mandatoryAttr : mandatory) {
  50             if (attrs.get(mandatoryAttr) == null) {
  51                 debug("missing mandatory attribute: " + mandatoryAttr);
  52                 return false;
  53             }
  54         }
  55 
  56         // Check optional attributes
  57         int optMissing = 0;
  58         for (String optionalAttr : optional) {
  59             if (attrs.get(optionalAttr) == null) {
  60                 debug("warning: missing optional attribute: " + optionalAttr);
  61                 ++optMissing;
  62             }
  63         }
  64 
  65         if (attrs.size() > (mandatory.length + (optional.length
  66                 - optMissing))) {
  67             debug("too many attributes: " + attrs);
  68             return false;
  69         }
  70 
  71         return true;
  72     }
  73 
  74     /*
  75      * Process command line arguments and init env
  76      */
  77     public static Hashtable<Object, Object> initEnv(boolean localServer,
  78             String testname, String[] args) {
  79 
  80         Hashtable<Object, Object> env = new Hashtable<>();
  81 
  82         // set some default parameters if no additional specified
  83         env.put("DNS_DOMAIN", "domain1.com.");
  84         env.put("FOREIGN_DOMAIN", "Central.Sun.COM.");
  85         env.put("FOREIGN_LEAF", "sunweb");
  86 
  87         // set defaults for some JNDI properties
  88         env.put(Context.INITIAL_CONTEXT_FACTORY,
  89                 "com.sun.jndi.dns.DnsContextFactory");
  90 
  91         boolean traceEnable = false;
  92         boolean loopPlayback = false;
  93         for (int i = 0; i < args.length; i++) {
  94             if ((args[i].equals("-D")) && (args.length > i + 1)) {
  95                 extractProperty(args[++i], env);
  96             } else if (args[i].startsWith("-D")) {
  97                 extractProperty(args[i].substring(2), env);
  98             } else if (args[i].equalsIgnoreCase("-trace")) {
  99                 traceEnable = true;
 100             } else if (args[i].equalsIgnoreCase("-loop")) {
 101                 loopPlayback = true;
 102             }
 103         }
 104 
 105         debug = Boolean.valueOf(System.getProperty("debug", "true"));
 106 
 107         if (env.get("DNS_SERVER") != null) {
 108             String port = (String) env.get("DNS_PORT");
 109             String portSuffix = (port == null) ? "" : ":" + port;
 110             String url = "dns://" + env.get("DNS_SERVER") + portSuffix;
 111             env.put(Context.PROVIDER_URL, url);
 112             env.put(Context.PROVIDER_URL, url + "/" + env.get("DNS_DOMAIN"));
 113         }
 114 
 115         Thread inst = null;
 116         if (traceEnable) {
 117             inst = createDNSTracer(testname, env);
 118         } else {
 119             if (localServer) {
 120                 inst = createDNSServer(testname, loopPlayback);
 121             } else {
 122                 // for tests which run against remote server
 123                 // or no server required
 124                 debug("Skip local DNS Server creation ");
 125             }
 126         }
 127 
 128         if (inst != null) {
 129             inst.start();
 130             env.put(TEST_DNS_SERVER_THREAD, inst);
 131             String url = "dns://localhost:" + ((Server) inst).getPort();
 132 
 133             env.put(TEST_DNS_ROOT_URL, url);
 134             env.put(Context.PROVIDER_URL, url + "/" + env.get("DNS_DOMAIN"));
 135         }
 136 
 137         return env;
 138     }
 139 
 140     /*
 141      * Clean-up the directory context.
 142      */
 143     public static void cleanup(Context ctx) {
 144         if (ctx != null) {
 145             try {
 146                 ctx.close();
 147             } catch (NamingException e) {
 148                 // ignore
 149             }
 150         }
 151     }
 152 
 153     /*
 154      * Clean up given closable resource
 155      */
 156     public static void cleanupClosableRes(Closeable res) {
 157         if (res != null) {
 158             try {
 159                 res.close();
 160             } catch (Exception e) {
 161                 // ignore
 162             }
 163         }
 164     }
 165 
 166     private static void extractProperty(String propString,
 167             Hashtable<Object, Object> env) {
 168         int index;
 169 
 170         if ((index = propString.indexOf('=')) > 0) {
 171             env.put(propString.substring(0, index),
 172                     propString.substring(index + 1));
 173         } else {
 174             throw new RuntimeException(
 175                     "Failed to extract test args property from " + propString);
 176         }
 177     }
 178 
 179     public static DNSTracer createDNSTracer(String testname,
 180             Hashtable<Object, Object> env) {
 181         try {
 182             PrintStream outStream = new PrintStream(getCaptureFile(testname));
 183             return new DNSTracer(outStream, (String) env.get("DNS_SERVER"),
 184                     Integer.parseInt((String) env.get("DNS_PORT")));
 185         } catch (Exception e) {
 186             throw new RuntimeException(
 187                     "Error: failed to create DNSTracer : " + e.getMessage(), e);
 188         }
 189     }
 190 
 191     public static DNSServer createDNSServer(String testname, boolean loop) {
 192         String path = getCaptureFile(testname);
 193         if (Files.exists(Paths.get(path))) {
 194             try {
 195                 return new DNSServer(path, loop);
 196             } catch (Exception e) {
 197                 throw new RuntimeException(
 198                         "Error: failed to create DNSServer : " + e.getMessage(),
 199                         e);
 200             }
 201         } else {
 202             throw new RuntimeException(
 203                     "Error: failed to create DNSServer, not found dns "
 204                             + "cache file " + path);
 205         }
 206     }
 207 
 208     public static String getCaptureFile(String testname) {
 209         return Paths.get(System.getProperty("test.src"))
 210                 .resolve(testname + ".dns").toString();
 211     }
 212 
 213     public static void enableHostsFile(String hostsFile) {
 214         System.out.println("Enable jdk.net.hosts.file = " + hostsFile);
 215         System.setProperty("jdk.net.hosts.file", hostsFile);
 216     }
 217 
 218     public static void enableHostsFile(int depth) {
 219         Path path = Paths.get(System.getProperty("test.src", "."))
 220                 .toAbsolutePath();
 221         for (int i = depth; i >= 0; i--) {
 222             Path filePath = path.resolve("hosts");
 223             if (Files.exists(filePath) && !Files.isDirectory(filePath)) {
 224                 enableHostsFile(filePath.toString());
 225                 break;
 226             }
 227 
 228             path = path.getParent();
 229             if (path == null) {
 230                 break;
 231             }
 232         }
 233     }
 234 
 235     public static void debug(Object object) {
 236         if (debug) {
 237             System.out.println(object);
 238         }
 239     }
 240 
 241     public static void verifySchema(Attributes attrs, String[] mandatory,
 242             String[] optional) {
 243         debug(attrs);
 244         if (!checkSchema(attrs, mandatory, optional)) {
 245             throw new RuntimeException("Check schema failed.");
 246         }
 247     }
 248 
 249     public static String getRootUrl(Hashtable<Object, Object> env) {
 250         return (String) env.get(TEST_DNS_ROOT_URL);
 251     }
 252 
 253     /*
 254      * Assemble a fully-qualified domain name from the base component and the
 255      * domain name.
 256      */
 257     public static String buildFqdn(String base, Hashtable<Object, Object> env,
 258             boolean primary) {
 259         String domain = (String) (primary ?
 260                 env.get("DNS_DOMAIN") :
 261                 env.get("FOREIGN_DOMAIN"));
 262 
 263         return base + "." + domain;
 264     }
 265 }