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