001 /****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one *
003 * or more contributor license agreements. See the NOTICE file *
004 * distributed with this work for additional information *
005 * regarding copyright ownership. The ASF licenses this file *
006 * to you under the Apache License, Version 2.0 (the *
007 * "License"); you may not use this file except in compliance *
008 * with the License. You may obtain a copy of the License at *
009 * *
010 * http://www.apache.org/licenses/LICENSE-2.0 *
011 * *
012 * Unless required by applicable law or agreed to in writing, *
013 * software distributed under the License is distributed on an *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015 * KIND, either express or implied. See the License for the *
016 * specific language governing permissions and limitations *
017 * under the License. *
018 ****************************************************************/
019
020 package org.apache.james.jspf.tester;
021
022 import org.apache.commons.cli.CommandLine;
023 import org.apache.commons.cli.CommandLineParser;
024 import org.apache.commons.cli.HelpFormatter;
025 import org.apache.commons.cli.OptionBuilder;
026 import org.apache.commons.cli.Options;
027 import org.apache.commons.cli.ParseException;
028 import org.apache.commons.cli.PosixParser;
029 import org.jvyaml.Constructor;
030 import org.jvyaml.DefaultYAMLFactory;
031 import org.jvyaml.YAMLFactory;
032 import org.xbill.DNS.TextParseException;
033
034 import java.io.BufferedReader;
035 import java.io.FileInputStream;
036 import java.io.IOException;
037 import java.io.InputStream;
038 import java.io.InputStreamReader;
039 import java.io.Reader;
040 import java.util.HashMap;
041 import java.util.Iterator;
042 import java.util.Locale;
043 import java.util.Set;
044
045 /**
046 * Run a fake dnsserver listening both TCP and UDP ports.
047 *
048 * Mandatory parameters are -f (yaml zone definition) and -t (test name).
049 * if testname is "ALL" then all of the zones in the file are merged in a single
050 * zone and loaded.
051 *
052 * e.g: DNSTestingServerLauncher -f rfc4408-tests.yml -t ALL
053 *
054 * by default listen to port 53 of every interface, but ip and port can be updated.
055 */
056 public class DNSTestingServerLauncher {
057
058 private static final char CHAR_TESTNAME = 't';
059
060 private static final char CHAR_FILE = 'f';
061
062 private static final char CHAR_PORT = 'p';
063
064 private static final char CHAR_IP = 'i';
065
066 private final static String CMD_IP = "ip";
067
068 private final static String CMD_PORT = "port";
069
070 private final static String CMD_FILE = "file";
071
072 private final static String CMD_TESTNAME = "test";
073
074 /**
075 * @param args
076 */
077 @SuppressWarnings("unchecked")
078 public static void main(String[] args) {
079 String ip = null;
080 String port = null;
081 String file = null;
082 String test = null;
083
084 Options options = generateOptions();
085 CommandLineParser parser = new PosixParser();
086
087 try {
088 CommandLine line = parser.parse(options, args);
089
090 ip = line.getOptionValue(CHAR_IP);
091 port = line.getOptionValue(CHAR_PORT);
092 file = line.getOptionValue(CHAR_FILE);
093 test = line.getOptionValue(CHAR_TESTNAME);
094
095 if (ip == null) ip = "0.0.0.0";
096 if (port == null) port = "53";
097
098 if (file != null && test != null) {
099
100 InputStream is = new FileInputStream(file);
101
102 if (is != null) {
103 Reader br = new BufferedReader(new InputStreamReader(is));
104 YAMLFactory fact = new DefaultYAMLFactory();
105
106 Constructor ctor = fact.createConstructor(fact.createComposer(fact.createParser(fact.createScanner(br)),fact.createResolver()));
107 boolean found = false;
108 HashMap zonedata = new HashMap();
109 HashMap testMap = null;
110 while(ctor.checkData() && !found) {
111 Object o = ctor.getData();
112 if (o instanceof HashMap) {
113 testMap = (HashMap) o;
114 if (test.equals(testMap.get("description")) || "ALL".equalsIgnoreCase(test)) {
115 found = true;
116 loadZoneData(testMap, zonedata);
117 }
118 }
119 }
120 if (found) {
121 DNSTestingServer testingServer = new DNSTestingServer(ip, port);
122 testingServer.setData(zonedata);
123
124 System.out.println("Listening on "+ip+":"+port);
125
126 while (true) {
127 try {
128 Thread.sleep(1000);
129 } catch (InterruptedException e) {
130 // TODO Auto-generated catch block
131 }
132 }
133
134 } else {
135 throw new RuntimeException("Unable to find a <"+test+"> section in the passed file.");
136 }
137 } else {
138 throw new RuntimeException("Unable to load the file: "+file);
139 }
140
141
142 } else {
143 System.out.println("Missing required parameter.");
144 usage();
145 }
146 } catch (ParseException e) {
147 usage();
148 } catch (RuntimeException e) {
149 System.out.println("Error: "+e.getMessage());
150 e.printStackTrace();
151 usage();
152 } catch (TextParseException e) {
153 System.out.println("Parsing Error: "+e.getMessage());
154 e.printStackTrace();
155 usage();
156 } catch (IOException e) {
157 System.out.println("IO Error: "+e.getMessage());
158 e.printStackTrace();
159 usage();
160 }
161
162 }
163
164 @SuppressWarnings("unchecked")
165 private static void loadZoneData(HashMap testMap, HashMap zonedata) {
166 HashMap loadedZoneData = (HashMap) testMap.get("zonedata");
167 Set keys = loadedZoneData.keySet();
168 for (Iterator i = keys.iterator(); i.hasNext(); ) {
169 String hostname = (String) i.next();
170 String lowercase = hostname.toLowerCase(Locale.US);
171 if (zonedata.containsKey(lowercase)) {
172 System.err.println("Replace zone entry for "+lowercase+" to "+loadedZoneData.get(hostname));
173 }
174 zonedata.put(lowercase, loadedZoneData.get(hostname));
175 }
176 }
177
178 /**
179 * Print out the usage
180 */
181 private static void usage() {
182 HelpFormatter hf = new HelpFormatter();
183 hf.printHelp("DNSTestingServerLauncher", generateOptions(), true);
184 System.exit(255);
185 }
186
187 /**
188 * Return the generated Options
189 *
190 * @return options
191 */
192 private static Options generateOptions() {
193 Options options = new Options();
194
195 OptionBuilder.withLongOpt(CMD_IP);
196 OptionBuilder.withValueSeparator('=');
197 OptionBuilder.hasArg();
198 OptionBuilder.withArgName("ip");
199 OptionBuilder.withDescription("Listening IP (default: 0.0.0.0 for every IP)");
200 options.addOption(OptionBuilder.create(CHAR_IP));
201
202 OptionBuilder.withLongOpt(CMD_PORT);
203 OptionBuilder.withValueSeparator('=');
204 OptionBuilder.hasArg();
205 OptionBuilder.withArgName("port");
206 OptionBuilder.withDescription("Listening port (default: 53)");
207 options.addOption(OptionBuilder.create(CHAR_PORT));
208
209 OptionBuilder.withLongOpt(CMD_FILE);
210 OptionBuilder.withValueSeparator('=');
211 OptionBuilder.withDescription("YML file name");
212 OptionBuilder.withArgName("file");
213 OptionBuilder.isRequired();
214 OptionBuilder.hasArg();
215 options.addOption(OptionBuilder.create(CHAR_FILE));
216
217 OptionBuilder.withLongOpt(CMD_TESTNAME);
218 OptionBuilder.withValueSeparator('=');
219 OptionBuilder.hasArg();
220 OptionBuilder.withDescription("Test name");
221 OptionBuilder.withArgName("test");
222 OptionBuilder.isRequired();
223 options.addOption(OptionBuilder.create(CHAR_TESTNAME));
224
225
226 return options;
227 }
228
229 }