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    }