/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package staticContent.framework; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.SecureRandom; import java.util.Vector; import org.apache.commons.cli.BasicParser; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.MissingOptionException; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import com.github.encdns.LibSodiumWrapper; import staticContent.framework.config.Settings; import staticContent.framework.controller.Controller; import staticContent.framework.controller.Layer1NetworkClientController; import staticContent.framework.controller.Layer2RecodingSchemeClientController; import staticContent.framework.controller.Layer3OutputStrategyClientController; import staticContent.framework.controller.Layer4TransportClientController; import staticContent.framework.controller.Layer5ApplicationClientController; import staticContent.framework.launcher.CommandLineParameters; import staticContent.framework.util.Util; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer2recodingScheme.encDNS_v0_001.EncDNSHelper; /** * This is the EncDNS client proxy (also known as local proxy). It should be * executed on the client computer or at least be connected to it via a * trustworthy connection. The client proxy will encrypt standard DNS requests * sent to it by a stub resolver and pass the encrypted request on to the local * recursive nameserver. It will also decrypt the encrypted response received * from the local recursive nameserver and pass the decrypted standard DNS * response on to the stub resolver. * * This implementation currently supports UDP only. * * Note: don't use anonnode.java as we dont need the info service etc (encDns * is a single node system) */ public class EncDnsClient { private Settings settings; public static CommandLineParameters commandLineParameters; // references to controllers public Layer1NetworkClientController networkLayerClient; public Layer2RecodingSchemeClientController recodingLayerClient; public Layer3OutputStrategyClientController outputStrategyLayerClient; public Layer4TransportClientController transportLayerClient; public Layer5ApplicationClientController applicationLayerClient; private Vector<Controller> components = new Vector<Controller>(); // instantiated controllers /** maximum message size */ public static final int MAX_MSG_SIZE = 65535; /** query timeout */ public static final int TIMEOUT = 5000; /** maximum number of threads */ public static int threads = 100; public static int bindPort; public static InetAddress bindAddr; public static int verbosity; public static boolean encryption; public static boolean isLoadgen; public static byte[] remoteNS; public static InetAddress localNS; public static int localNSPort; public static String rrnskeyPath; private static EncDnsClient encDnsClient; public static LibSodiumWrapper libSodium; public static SecureRandom rand = new SecureRandom(); public static byte[] pk; //private byte[] _sk; private byte[] _rPK; public static byte[] k; public static byte[] magicString; private static Options _opt; /** * Creates a new instance of EncDNSServerProxy.new file * @param zoneurl EncDNS zone name (The IP this proxy is listening on must be listed as an authoritative nameserver IP for this zone in the parent zone) * @param nsaddr remote recursive nameserver's address * @param port remote recursive nameserver's port * @param cachesize size of cache for intermediate shared secrets * @param pkPath path to public key file * @param skPath path to secret key file * @param noEncryption If true, disables encryption. If false, encryption is enabled. * @param verbosity verbosity level */ public EncDnsClient() { //System.err.println("warning: listening port for encDns client is not 53 (but 22322); debug..."); if (EncDnsClient.encDnsClient != null) throw new RuntimeException("this is a sigletone"); EncDnsClient.encDnsClient = this; if (encryption) { EncDnsClient.libSodium = new LibSodiumWrapper(); // Generate new local key pair. We do not need to save this persistently // as the public key will always be sent along with a query and using // the same key for multiple sessions would make these linkable. EncDnsClient.pk = new byte[EncDnsClient.libSodium.PKBYTES]; byte[] sk = new byte[EncDnsClient.libSodium.SKBYTES]; EncDnsClient.libSodium.jni_generateKeys(EncDnsClient.pk, sk); // Load remote recursive nameserver's PK from file _rPK = EncDNSHelper.readByteArrayFromFile(rrnskeyPath); if(_rPK == null) { System.err.println("ERROR: Could not load remote recursive " + "nameserver's public key. Exiting."); return; } else if (Util.toHex(_rPK).equals("CF3CB86BEA08A9D7D7B09443418DD9CB6DB047395BB662241CA038FF997BC174")) { System.err.println("WARNING: you are using a publicly known test key; never use this key to transmit sensitive data!"); } long ktime = System.nanoTime(); EncDnsClient.k = EncDnsClient.libSodium.cryptoBoxBeforenm(_rPK, sk); if(EncDnsClient.verbosity >= 1) System.out.println("key generation time (ns) = " + (System.nanoTime()-ktime)); } EncDnsClient.magicString = new byte[]{0x20, 0x45, 0x5e}; if (isLoadgen) { if (commandLineParameters == null) { settings = CommandLineParameters.loadPluginSettings("./inputOutput/anonNode/encDnsLoadGen.txt"); } else { settings = CommandLineParameters.loadPluginSettings("./inputOutput/anonNode/encDnsLoadGen.txt", commandLineParameters.overwriteParameters); // settigns in Paths.PATH_TO_PATH_CONFIG_FILE have higher priority than normal plug-in settings -> overwrite (again) settings.addProperties("./inputOutput/anonNode/encDnsLoadGen.txt"); if (commandLineParameters.overwriteParameters != null) // overwriteParameters have higher priority than all other settings -> overwrite (again) Settings.overwriteSettings(settings.getPropertiesObject(), commandLineParameters.overwriteParameters, true); } } else { if (commandLineParameters == null) { settings = CommandLineParameters.loadPluginSettings("./inputOutput/anonNode/encDns.txt"); } else { settings = CommandLineParameters.loadPluginSettings("./inputOutput/anonNode/encDns.txt", commandLineParameters.overwriteParameters); // settigns in Paths.PATH_TO_PATH_CONFIG_FILE have higher priority than normal plug-in settings -> overwrite (again) settings.addProperties("./inputOutput/anonNode/encDns.txt"); if (commandLineParameters.overwriteParameters != null) // overwriteParameters have higher priority than all other settings -> overwrite (again) Settings.overwriteSettings(settings.getPropertiesObject(), commandLineParameters.overwriteParameters, true); } } generateComponents(); setReferencesBetweenComponents(); loadImplementations(); // loads the plug-ins callConstructors(); initializeComponents(); beginMixing(); } public static EncDnsClient getInstance() { if (EncDnsClient.encDnsClient == null) throw new RuntimeException("please use main method in class EncDnsClient.java to start an EncDNS-Client"); return encDnsClient; } private void generateComponents() { // NOTE: the order of the items is important (do not change it) //this.outputStrategyLayerClient = new Layer3OutputStrategyClientController(null, settings, null, null, null); //this.outputStrategyLayerClient.loadClientPluginInstance(); //this.components.add(outputStrategyLayerClient); this.recodingLayerClient = new Layer2RecodingSchemeClientController(null, settings, null, null, null); this.recodingLayerClient.loadClientPluginInstance(); this.components.add(recodingLayerClient); this.networkLayerClient = new Layer1NetworkClientController(null, settings, null, null, null); this.networkLayerClient.loadClientPluginInstance(); this.components.add(networkLayerClient); //this.transportLayerClient = new Layer4TransportClientController(null, settings, null, null, null); //this.transportLayerClient.loadClientPluginInstance(); //this.components.add(transportLayerClient); this.applicationLayerClient = new Layer5ApplicationClientController(null, settings, null, null, null); this.components.add(applicationLayerClient); } private void setReferencesBetweenComponents() { for (Controller c:components) c.setComponentReferences(null, networkLayerClient, null, recodingLayerClient, null, outputStrategyLayerClient, null, transportLayerClient, null, applicationLayerClient, null, null, null, null); } public void registerComponent(Controller controllerToRegister) { components.add(controllerToRegister); } private void callConstructors() { for (Controller c:components) if (c.implementation != null) c.implementation.constructor(); } private void loadImplementations() { for (Controller c:components) c.instantiateSubclass(); } private void initializeComponents() { for (Controller c:components) c.initialize(); } private void beginMixing() { for (Controller c:components) c.begin(); } /** * @param args the command line arguments (see -h for documentation) */ public static void main(String[] args) { try { _opt = new Options(); _opt.addOption("h", "help", false, "prints this message"); _opt.addOption("d", "disable-encryption", false, "disable encryption"); _opt.addOption("s", "libsodiumwrap", true, "path to libsodiumWrap library (default: in ./lib/)"); _opt.addOption("rk", "rrns-key", true, "path to remote recursive nameserver's public key (default: ./lib/encdns.pk)"); _opt.addOption("v", "verbose", false, "enable verbose output"); _opt.addOption("l", "loadgen", false, "start this client as load generator"); _opt.addOption("mt", "threads", true, "number of threads (default: 100)"); _opt.addOption("ba", "bindAddress", true, "IP address the EncDNS clinet will listen for DNS requests (default: 127.0.0.1)"); _opt.addOption("bp", "bindPort", true, "Port the EncDNS clinet will listen for DNS requests (default: 53)"); Option laOpt = new Option("la", "localns", true, "local nameserver's IP address or hostname (REQUIRED)"); laOpt.setRequired(true); _opt.addOption(laOpt); _opt.addOption("lp", "localnsport", true, "local nameserver's port (default: 53)"); Option raOpt = new Option("ra", "remotens", true, "remote nameserver's zone name (REQUIRED)"); raOpt.setRequired(true); _opt.addOption(raOpt); Options helpOpts = new Options(); helpOpts.addOption("h", "help", false, "prints this message"); CommandLineParser parser = new BasicParser(); // We need to check for the help parameter first, as parsing all // simultaneously will throw an exception if a required option // is missing. CommandLine cmd = parser.parse(helpOpts, args, true); if (cmd.hasOption("help")) { HelpFormatter hf = new HelpFormatter(); hf.printHelp("EncDNSClientProxy", _opt); return; } // Now we can parse all options cmd = parser.parse(_opt, args); // get parameter values passed by CLI String libStr = cmd.getOptionValue("libsodiumwrap"); String localnsStr = cmd.getOptionValue("localns"); String localnsportStr = cmd.getOptionValue("localnsport"); String remotensStr = cmd.getOptionValue("remotens"); String rrnskeyStr = cmd.getOptionValue("rrns-key"); String threadsStr = cmd.getOptionValue("threads"); String baStr = cmd.getOptionValue("bindAddress"); String bpStr = cmd.getOptionValue("bindPort"); // set standard values if corresponding CLI parameters are not set if(libStr == null) { if(EncDNSHelper.osIsWindows()) { libStr = System.getProperty("user.dir") + "\\lib\\libsodiumWrap.dll"; } else { // assume a Unix-like OS libStr = System.getProperty("user.dir") + "/lib/libsodiumWrap.so"; } } if(localnsStr == null) localnsStr = "127.0.0.1"; int localnsport = 53; if(localnsportStr != null) { localnsport = Integer.parseInt(localnsportStr); } if(baStr == null) baStr = "127.0.0.1"; int bPort = 53; if (bpStr != null) { bPort = Integer.parseInt(bpStr); } boolean encryption = true; if(cmd.hasOption("disable-encryption")) { encryption = false; } boolean isLoadGen = false; if(cmd.hasOption("loadgen")) { isLoadGen = true; } int verbosity; if(cmd.hasOption("verbose")) { verbosity = 1; } else { verbosity = 0; } if(rrnskeyStr == null) { if(EncDNSHelper.osIsWindows()) { rrnskeyStr = System.getProperty("user.dir") + "\\lib\\encdns.pk"; } else { // assume Unix-like OS rrnskeyStr = System.getProperty("user.dir") + "/lib/encdns.pk"; } } int threads = 100; if(threadsStr != null) { threads = Integer.parseInt(threadsStr); } // load the required C wrapper library if (encryption) System.load(libStr); // resolve the local recursive nameserver's address or IP InetAddress localns = InetAddress.getByName(localnsStr); InetAddress bpaddr = InetAddress.getByName(baStr); EncDnsClient.bindAddr = bpaddr; EncDnsClient.bindPort = bPort; EncDnsClient.remoteNS = EncDNSHelper.parseZoneNameString(remotensStr); EncDnsClient.localNS = localns; EncDnsClient.localNSPort = localnsport; EncDnsClient.rrnskeyPath = rrnskeyStr; EncDnsClient.encryption = encryption; EncDnsClient.isLoadgen = isLoadGen; EncDnsClient.verbosity = verbosity; EncDnsClient.threads = threads; new EncDnsClient(); } catch (MissingOptionException e) { // print and error and the help if a required option is missing System.out.println(e.getMessage()); HelpFormatter hf = new HelpFormatter(); hf.printHelp("EncDNSClientProxy", _opt); } catch (ParseException e) { System.err.println(e); } catch (UnknownHostException e) { // print an error if the local recursive nameserver's address cannot // be resolved System.err.println("local recursive nameserver's address could not be resolved"); } catch (UnsatisfiedLinkError e) { System.err.println("Could not access libsodium; was \"export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/user/gmix/lib/\" executed? is libsodiumWrap.so compatible to your system? error message: " +e.getMessage()); } } }