From 91207aeed2bfe6c6bb91adab103be6e2e8945b67 Mon Sep 17 00:00:00 2001 From: David Blacka Date: Sat, 12 Feb 2011 21:25:26 +0000 Subject: [PATCH] Refactor the command line classes with a new base class and upgrade commons-cli to version 1.2. git-svn-id: https://svn.verisignlabs.com/jdnssec/tools/trunk@245 4cbd57fe-54e5-0310-bd9a-f30fe5ea5e6e --- ChangeLog | 7 + lib/commons-cli-1.1.jar | Bin 36174 -> 0 bytes lib/commons-cli-1.2.jar | Bin 0 -> 41123 bytes licenses/commons-cli-LICENSE.txt | 262 ++++++++++---- src/com/verisignlabs/dnssec/cl/CLBase.java | 325 ++++++++++++++++++ src/com/verisignlabs/dnssec/cl/DSTool.java | 160 +-------- src/com/verisignlabs/dnssec/cl/KeyGen.java | 182 +--------- .../verisignlabs/dnssec/cl/KeyInfoTool.java | 187 +--------- .../verisignlabs/dnssec/cl/SignKeyset.java | 197 +---------- src/com/verisignlabs/dnssec/cl/SignRRset.java | 197 +---------- src/com/verisignlabs/dnssec/cl/SignZone.java | 276 +++------------ .../verisignlabs/dnssec/cl/VerifyZone.java | 210 ++--------- .../verisignlabs/dnssec/cl/ZoneFormat.java | 213 ++---------- 13 files changed, 705 insertions(+), 1511 deletions(-) delete mode 100644 lib/commons-cli-1.1.jar create mode 100644 lib/commons-cli-1.2.jar create mode 100644 src/com/verisignlabs/dnssec/cl/CLBase.java diff --git a/ChangeLog b/ChangeLog index e678cca..4caaba7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2011-02-12 David Blacka + + * Update commons-cli to version 1.2. + * Refactor all of the command line classes. A new command line + base class has been created to eliminate much of the duplicated + code. + 2011-02-09 Blacka * Enable reading and writing from stdin/stdout for most tools. To diff --git a/lib/commons-cli-1.1.jar b/lib/commons-cli-1.1.jar deleted file mode 100644 index e633afbe6842aa92b1a8f0ff3f5b8c0e3283961b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36174 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 edFKBT8{e^?v~S{1Y(% diff --git a/lib/commons-cli-1.2.jar b/lib/commons-cli-1.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..ce4b9fffe40c41669797cd806ac989471b2acd84 GIT binary patch literal 41123 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%0D2u^7bcx|ezvBT@`6 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 CNzF?D literal 0 HcmV?d00001 diff --git a/licenses/commons-cli-LICENSE.txt b/licenses/commons-cli-LICENSE.txt index 58c4c55..57bc88a 100644 --- a/licenses/commons-cli-LICENSE.txt +++ b/licenses/commons-cli-LICENSE.txt @@ -1,60 +1,202 @@ -/* - * $Header: /home/radcvs/dnssec_pilot/sectools/licenses/commons-cli-LICENSE.txt,v 1.1 2003/04/02 22:40:49 davidb Exp $ - * $Revision: 1.1 $ - * $Date: 2003/04/02 22:40:49 $ - * - * ==================================================================== - * - * The Apache Software License, Version 1.1 - * - * Copyright (c) 1999-2001 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowlegement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowlegement may appear in the software itself, - * if and wherever such third-party acknowlegements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Group. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - * - */ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/src/com/verisignlabs/dnssec/cl/CLBase.java b/src/com/verisignlabs/dnssec/cl/CLBase.java new file mode 100644 index 0000000..e8b3ed4 --- /dev/null +++ b/src/com/verisignlabs/dnssec/cl/CLBase.java @@ -0,0 +1,325 @@ +package com.verisignlabs.dnssec.cl; + +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import org.apache.commons.cli.AlreadySelectedException; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.PosixParser; +import org.apache.commons.cli.UnrecognizedOptionException; + +import com.verisignlabs.dnssec.security.DnsKeyAlgorithm; + +/** + * This is a base class for jdnssec command line tools. Each command line tool + * should inherit from this class, create a subclass of CLIStateBase (overriding + * setupOptions and processOptions), and implement the execute() method. + * Subclasses also have their own main() methods, which should just create the + * subclass variant of the CLIState and call run(). + */ +public abstract class CLBase +{ + protected static Logger log; + + /** + * This is a very simple log formatter that simply outputs the log level and + * log string. + */ + public static class BareLogFormatter extends Formatter + { + @Override + public String format(LogRecord arg0) + { + StringBuilder out = new StringBuilder(); + String lvl = arg0.getLevel().getName(); + + out.append(lvl); + out.append(": "); + out.append(arg0.getMessage()); + out.append("\n"); + + return out.toString(); + } + } + + /** + * This is a base class for command line parsing state. Subclasses should + * override setupOptions and processOptions. + */ + public static class CLIStateBase + { + protected Options opts; + protected String usageStr; + + /** + * The base constructor. This will setup the command line options. + * + * @param usage + * The command line usage string (e.g., + * "jdnssec-foo [..options..] zonefile") + */ + public CLIStateBase(String usage) + { + usageStr = usage; + setup(); + } + + /** This is the base set of command line options provided to all subclasses. */ + private void setup() + { + // Set up the standard set of options that all jdnssec command line tools will implement. + opts = new Options(); + + // boolean options + opts.addOption("h", "help", false, "Print this message."); + opts.addOption("m", "multiline", false, + "Output DNS records using 'multiline' format"); + + OptionBuilder.hasOptionalArg(); + OptionBuilder.withLongOpt("verbose"); + OptionBuilder.withArgName("level"); + OptionBuilder.withDescription("verbosity level -- 0 is silence, 3 is info, " + + "5 is debug information, 6 is trace information. default is level 2 (warning)"); + opts.addOption(OptionBuilder.create('v')); + + OptionBuilder.hasArg(); + OptionBuilder.withArgName("alias:original:mnemonic"); + OptionBuilder.withLongOpt("alg-alias"); + OptionBuilder.withDescription("Define an alias for an algorithm"); + opts.addOption(OptionBuilder.create('A')); + + setupOptions(opts); + } + + /** + * This is an overridable method for subclasses to add their own command + * line options. + * + * @param opts + * the options object to add (via OptionBuilder, typically) new + * options to. + */ + protected void setupOptions(Options opts) + { + // Subclasses generally override this. + } + + /** + * This is the main method for parsing the command line arguments. + * Subclasses generally override processOptions() rather than this method. + * This method create the parsing objects and processes the standard + * options. + * + * @param args + * The command line arguments. + * @throws ParseException + */ + public void parseCommandLine(String args[]) throws ParseException + { + CommandLineParser cli_parser = new PosixParser(); + CommandLine cli = cli_parser.parse(opts, args); + + if (cli.hasOption('h')) usage(); + + Logger rootLogger = Logger.getLogger(""); + int value = parseInt(cli.getOptionValue('v'), -1); + + switch (value) + { + case 0: + rootLogger.setLevel(Level.OFF); + break; + case 1: + rootLogger.setLevel(Level.SEVERE); + break; + case 2: + default: + rootLogger.setLevel(Level.WARNING); + break; + case 3: + rootLogger.setLevel(Level.INFO); + break; + case 4: + rootLogger.setLevel(Level.CONFIG); + case 5: + rootLogger.setLevel(Level.FINE); + break; + case 6: + rootLogger.setLevel(Level.ALL); + break; + } + + // I hate java.util.logging, btw. + for (Handler h : rootLogger.getHandlers()) + { + h.setLevel(rootLogger.getLevel()); + h.setFormatter(new BareLogFormatter()); + } + + if (cli.hasOption('m')) + { + org.xbill.DNS.Options.set("multiline"); + } + + String[] optstrs = null; + if ((optstrs = cli.getOptionValues('A')) != null) + { + for (int i = 0; i < optstrs.length; i++) + { + addArgAlias(optstrs[i]); + } + } + + processOptions(cli); + } + + /** + * Process additional tool-specific options. Subclasses generally override + * this. + * + * @param cli + * The {@link CommandLine} object containing the parsed command + * line state. + */ + protected void processOptions(CommandLine cli) throws ParseException + { + // Subclasses generally override this. + } + + /** Print out the usage and help statements, then quit. */ + public void usage() + { + HelpFormatter f = new HelpFormatter(); + + PrintWriter out = new PrintWriter(System.err); + + // print our own usage statement: + f.printHelp(out, 75, usageStr, null, opts, HelpFormatter.DEFAULT_LEFT_PAD, + HelpFormatter.DEFAULT_DESC_PAD, null); + + out.flush(); + System.exit(64); + + } + + protected void addArgAlias(String s) + { + if (s == null) return; + + DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance(); + + String[] v = s.split(":"); + if (v.length < 2) return; + + int alias = parseInt(v[0], -1); + if (alias <= 0) return; + int orig = parseInt(v[1], -1); + if (orig <= 0) return; + String mn = null; + if (v.length > 2) mn = v[2]; + + algs.addAlias(alias, mn, orig); + } + } + + public static int parseInt(String s, int def) + { + try + { + int v = Integer.parseInt(s); + return v; + } + catch (NumberFormatException e) + { + return def; + } + } + + /** + * Calculate a date/time from a command line time/offset duration string. + * + * @param start + * the start time to calculate offsets from. + * @param duration + * the time/offset string to parse. + * @return the calculated time. + */ + public static Date convertDuration(Date start, String duration) throws ParseException + { + if (start == null) start = new Date(); + if (duration.startsWith("now")) + { + start = new Date(); + if (duration.indexOf("+") < 0) return start; + + duration = duration.substring(3); + } + + if (duration.startsWith("+")) + { + long offset = (long) parseInt(duration.substring(1), 0) * 1000; + return new Date(start.getTime() + offset); + } + + SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss"); + dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT")); + try + { + return dateFormatter.parse(duration); + } + catch (java.text.ParseException e) + { + throw new ParseException(e.getMessage()); + } + } + + public abstract void execute() throws Exception; + + public void run(CLIStateBase state, String[] args) + { + try + { + state.parseCommandLine(args); + } + catch (UnrecognizedOptionException e) + { + System.err.println("error: unknown option encountered: " + e.getMessage()); + state.usage(); + } + catch (AlreadySelectedException e) + { + System.err.println("error: mutually exclusive options have " + + "been selected:\n " + e.getMessage()); + state.usage(); + } + catch (Exception e) + { + System.err.println("error: unknown command line parsing exception:"); + e.printStackTrace(); + state.usage(); + } + + log = Logger.getLogger(this.getClass().toString()); + + try + { + execute(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/src/com/verisignlabs/dnssec/cl/DSTool.java b/src/com/verisignlabs/dnssec/cl/DSTool.java index f9b3ee5..444af8e 100644 --- a/src/com/verisignlabs/dnssec/cl/DSTool.java +++ b/src/com/verisignlabs/dnssec/cl/DSTool.java @@ -1,6 +1,4 @@ -// $Id: KeyGen.java 1954 2005-08-14 17:05:50Z davidb $ -// -// Copyright (C) 2001-2003 VeriSign, Inc. +// Copyright (C) 2001-2003, 2011 VeriSign, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -21,9 +19,6 @@ package com.verisignlabs.dnssec.cl; import java.io.FileWriter; import java.io.PrintWriter; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; import org.apache.commons.cli.*; import org.xbill.DNS.DLVRecord; @@ -36,29 +31,26 @@ import com.verisignlabs.dnssec.security.*; /** * This class forms the command line implementation of a DNSSEC DS/DLV generator * - * @author David Blacka (original) - * @author $Author: davidb $ - * @version $Revision: 1954 $ + * @author David Blacka */ -public class DSTool +public class DSTool extends CLBase { - private static Logger log; + private CLIState state; /** * This is a small inner class used to hold all of the command line option * state. */ - private static class CLIState + protected static class CLIState extends CLIStateBase { - private Options opts; - public boolean createDLV = false; - public String outputfile = null; - public String keyname = null; - public int digest_id = DSRecord.SHA1_DIGEST_ID; + public boolean createDLV = false; + public String outputfile = null; + public String keyname = null; + public int digest_id = DSRecord.SHA1_DIGEST_ID; public CLIState() { - setupCLI(); + super("jdnssec-dstool [..options..] keyfile"); } /** @@ -66,25 +58,12 @@ public class DSTool * * @return a set of command line options. */ - private void setupCLI() + protected void setupOptions(Options opts) { - opts = new Options(); - - // boolean options - opts.addOption("h", "help", false, "Print this message."); - OptionBuilder.withLongOpt("dlv"); OptionBuilder.withDescription("Generate a DLV record instead."); opts.addOption(OptionBuilder.create()); - OptionBuilder.hasOptionalArg(); - OptionBuilder.withLongOpt("verbose"); - OptionBuilder.withArgName("level"); - OptionBuilder.withDescription("verbosity level -- 0 is silence, 5 is debug information, 6 is trace information.\n" - + "default is level 5."); - // Argument options - opts.addOption(OptionBuilder.create('v')); - OptionBuilder.hasArg(); OptionBuilder.withLongOpt("digest"); OptionBuilder.withArgName("id"); @@ -92,49 +71,9 @@ public class DSTool opts.addOption(OptionBuilder.create('d')); } - public void parseCommandLine(String[] args) + protected void processOptions(CommandLine cli) throws org.apache.commons.cli.ParseException { - CommandLineParser cli_parser = new PosixParser(); - CommandLine cli = cli_parser.parse(opts, args); - - if (cli.hasOption('h')) usage(); - - Logger rootLogger = Logger.getLogger(""); - - int value = parseInt(cli.getOptionValue('v'), -1); - switch (value) - { - case 0: - rootLogger.setLevel(Level.OFF); - break; - case 1: - rootLogger.setLevel(Level.SEVERE); - break; - case 2: - default: - rootLogger.setLevel(Level.WARNING); - break; - case 3: - rootLogger.setLevel(Level.INFO); - break; - case 4: - rootLogger.setLevel(Level.CONFIG); - case 5: - rootLogger.setLevel(Level.FINE); - break; - case 6: - rootLogger.setLevel(Level.ALL); - break; - } - - // I hate java.util.logging, btw. - for (Handler h : rootLogger.getHandlers()) - { - h.setLevel(rootLogger.getLevel()); - h.setFormatter(new BareLogFormatter()); - } - outputfile = cli.getOptionValue('f'); createDLV = cli.hasOption("dlv"); String optstr = cli.getOptionValue('d'); @@ -151,47 +90,10 @@ public class DSTool keyname = cl_args[0]; } - /** Print out the usage and help statements, then quit. */ - private void usage() - { - HelpFormatter f = new HelpFormatter(); - - PrintWriter out = new PrintWriter(System.err); - - // print our own usage statement: - f.printHelp(out, 75, "jdnssec-dstool [..options..] keyfile", null, opts, - HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null); - - out.flush(); - System.exit(64); - } } - /** - * This is just a convenience method for parsing integers from strings. - * - * @param s - * the string to parse. - * @param def - * the default value, if the string doesn't parse. - * @return the parsed integer, or the default. - */ - private static int parseInt(String s, int def) + public void execute() throws Exception { - try - { - int v = Integer.parseInt(s); - return v; - } - catch (NumberFormatException e) - { - return def; - } - } - - public static void execute(CLIState state) throws Exception - { - DnsKeyPair key = BINDKeyUtils.loadKey(state.keyname, null); DNSKEYRecord dnskey = key.getDNSKEYRecord(); @@ -226,39 +128,9 @@ public class DSTool public static void main(String[] args) { - CLIState state = new CLIState(); + DSTool tool = new DSTool(); + tool.state = new CLIState(); - try - { - state.parseCommandLine(args); - } - catch (UnrecognizedOptionException e) - { - System.err.println("error: unknown option encountered: " + e.getMessage()); - state.usage(); - } - catch (AlreadySelectedException e) - { - System.err.println("error: mutually exclusive options have been selected:\n " - + e.getMessage()); - state.usage(); - } - catch (Exception e) - { - System.err.println("error: unknown command line parsing exception:"); - e.printStackTrace(); - state.usage(); - } - - log = Logger.getLogger(DSTool.class.toString()); - - try - { - execute(state); - } - catch (Exception e) - { - e.printStackTrace(); - } + tool.run(tool.state, args); } } diff --git a/src/com/verisignlabs/dnssec/cl/KeyGen.java b/src/com/verisignlabs/dnssec/cl/KeyGen.java index d21c1ba..aa888b1 100644 --- a/src/com/verisignlabs/dnssec/cl/KeyGen.java +++ b/src/com/verisignlabs/dnssec/cl/KeyGen.java @@ -1,6 +1,4 @@ -// $Id$ -// -// Copyright (C) 2001-2003 VeriSign, Inc. +// Copyright (C) 2001-2003, 2011 VeriSign, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -20,10 +18,6 @@ package com.verisignlabs.dnssec.cl; import java.io.File; -import java.io.PrintWriter; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; import org.apache.commons.cli.*; import org.xbill.DNS.DClass; @@ -35,21 +29,18 @@ import com.verisignlabs.dnssec.security.*; /** * This class forms the command line implementation of a DNSSEC key generator * - * @author David Blacka (original) - * @author $Author$ - * @version $Revision$ + * @author David Blacka */ -public class KeyGen +public class KeyGen extends CLBase { - private static Logger log; + private CLIState state; /** * This is a small inner class used to hold all of the command line option * state. */ - private static class CLIState + protected static class CLIState extends CLIStateBase { - private Options opts; public int algorithm = 8; public int keylength = 1024; public boolean useLargeE = true; @@ -62,20 +53,15 @@ public class KeyGen public CLIState() { - setupCLI(); + super("jdnssec-keygen [..options..] name"); } /** * Set up the command line options. - * - * @return a set of command line options. */ - private void setupCLI() + protected void setupOptions(Options opts) { - opts = new Options(); - // boolean options - opts.addOption("h", "help", false, "Print this message."); opts.addOption("k", "kskflag", false, "Key is a key-signing-key (sets the SEP flag)."); opts.addOption("e", "large-exponent", false, "Use large RSA exponent (default)"); @@ -88,13 +74,6 @@ public class KeyGen OptionBuilder.withDescription("ZONE | OTHER (default ZONE)"); opts.addOption(OptionBuilder.create('n')); - OptionBuilder.hasOptionalArg(); - OptionBuilder.withLongOpt("verbose"); - OptionBuilder.withArgName("level"); - OptionBuilder.withDescription("verbosity level -- 0 is silence, " - + "5 is debug information, 6 is trace information.\n" + "default is level 5."); - opts.addOption(OptionBuilder.create('v')); - OptionBuilder.hasArg(); OptionBuilder.withArgName("algorithm"); OptionBuilder.withDescription("RSA | RSASHA1 | RSAMD5 | DH | DSA " @@ -119,61 +98,16 @@ public class KeyGen OptionBuilder.withArgName("dir"); OptionBuilder.withDescription("place generated key files in this " + "directory"); opts.addOption(OptionBuilder.create('d')); - - OptionBuilder.hasArg(); - OptionBuilder.withLongOpt("alg-alias"); - OptionBuilder.withArgName("alias:original:mnemonic"); - OptionBuilder.withDescription("define an alias for an algorithm"); opts.addOption(OptionBuilder.create('A')); } - public void parseCommandLine(String[] args) + protected void processOptions(CommandLine cli) throws org.apache.commons.cli.ParseException { - CommandLineParser cli_parser = new PosixParser(); - CommandLine cli = cli_parser.parse(opts, args); - String optstr = null; - - if (cli.hasOption('h')) usage(); - - Logger rootLogger = Logger.getLogger(""); - - int value = parseInt(cli.getOptionValue('v'), -1); - switch (value) - { - case 0: - rootLogger.setLevel(Level.OFF); - break; - case 1: - rootLogger.setLevel(Level.SEVERE); - break; - case 2: - default: - rootLogger.setLevel(Level.WARNING); - break; - case 3: - rootLogger.setLevel(Level.INFO); - break; - case 4: - rootLogger.setLevel(Level.CONFIG); - case 5: - rootLogger.setLevel(Level.FINE); - break; - case 6: - rootLogger.setLevel(Level.ALL); - break; - } - - // I hate java.util.logging, btw. - for (Handler h : rootLogger.getHandlers()) - { - h.setLevel(rootLogger.getLevel()); - h.setFormatter(new BareLogFormatter()); - } - + String[] optstrs = null; + if (cli.hasOption('k')) kskFlag = true; - if (cli.hasOption('e')) useLargeE = true; outputfile = cli.getOptionValue('f'); @@ -191,7 +125,6 @@ public class KeyGen } } - String[] optstrs; if ((optstrs = cli.getOptionValues('A')) != null) { for (int i = 0; i < optstrs.length; i++) @@ -225,63 +158,8 @@ public class KeyGen owner = cl_args[0]; } - - private void addArgAlias(String s) - { - if (s == null) return; - - DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance(); - - String[] v = s.split(":"); - if (v.length < 2) return; - - int alias = parseInt(v[0], -1); - if (alias <= 0) return; - int orig = parseInt(v[1], -1); - if (orig <= 0) return; - String mn = null; - if (v.length > 2) mn = v[2]; - - algs.addAlias(alias, mn, orig); - } - - /** Print out the usage and help statements, then quit. */ - private void usage() - { - HelpFormatter f = new HelpFormatter(); - - PrintWriter out = new PrintWriter(System.err); - - // print our own usage statement: - f.printHelp(out, 75, "jdnssec-keygen [..options..] name", null, opts, - HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null); - - out.flush(); - System.exit(64); - } } - /** - * This is just a convenience method for parsing integers from strings. - * - * @param s - * the string to parse. - * @param def - * the default value, if the string doesn't parse. - * @return the parsed integer, or the default. - */ - private static int parseInt(String s, int def) - { - try - { - int v = Integer.parseInt(s); - return v; - } - catch (NumberFormatException e) - { - return def; - } - } private static int parseAlg(String s) { @@ -293,7 +171,7 @@ public class KeyGen return algs.stringToAlgorithm(s); } - public static void execute(CLIState state) throws Exception + public void execute() throws Exception { JCEDnsSecSigner signer = new JCEDnsSecSigner(); @@ -331,39 +209,9 @@ public class KeyGen public static void main(String[] args) { - CLIState state = new CLIState(); - - try - { - state.parseCommandLine(args); - } - catch (UnrecognizedOptionException e) - { - System.err.println("error: unknown option encountered: " + e.getMessage()); - state.usage(); - } - catch (AlreadySelectedException e) - { - System.err.println("error: mutually exclusive options have " - + "been selected:\n " + e.getMessage()); - state.usage(); - } - catch (Exception e) - { - System.err.println("error: unknown command line parsing exception:"); - e.printStackTrace(); - state.usage(); - } - - log = Logger.getLogger(KeyGen.class.toString()); - - try - { - execute(state); - } - catch (Exception e) - { - e.printStackTrace(); - } + KeyGen tool = new KeyGen(); + tool.state = new CLIState(); + + tool.run(tool.state, args); } } diff --git a/src/com/verisignlabs/dnssec/cl/KeyInfoTool.java b/src/com/verisignlabs/dnssec/cl/KeyInfoTool.java index 08b989e..3eead2a 100644 --- a/src/com/verisignlabs/dnssec/cl/KeyInfoTool.java +++ b/src/com/verisignlabs/dnssec/cl/KeyInfoTool.java @@ -1,6 +1,4 @@ -// $Id: KeyGen.java 1954 2005-08-14 17:05:50Z davidb $ -// -// Copyright (C) 2001-2003 VeriSign, Inc. +// Copyright (C) 2001-2003, 2011 VeriSign, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -19,12 +17,8 @@ package com.verisignlabs.dnssec.cl; -import java.io.PrintWriter; import java.security.interfaces.DSAPublicKey; import java.security.interfaces.RSAPublicKey; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; import org.apache.commons.cli.*; import org.xbill.DNS.DNSKEYRecord; @@ -34,105 +28,35 @@ import com.verisignlabs.dnssec.security.*; /** * This class forms the command line implementation of a key introspection tool. * - * @author David Blacka (original) - * @author $Author: davidb $ - * @version $Revision: 1954 $ + * @author David Blacka */ -public class KeyInfoTool +public class KeyInfoTool extends CLBase { + private CLIState state; /** * This is a small inner class used to hold all of the command line option * state. */ - private static class CLIState + protected static class CLIState extends CLIStateBase { - private Options opts; public String[] keynames = null; public CLIState() { - setupCLI(); + super("jdnssec-keyinfo [..options..] keyfile"); } /** * Set up the command line options. - * - * @return a set of command line options. */ - private void setupCLI() + protected void setupOptions(Options opts) { - opts = new Options(); - - // boolean options - opts.addOption("h", "help", false, "Print this message."); - - OptionBuilder.hasOptionalArg(); - OptionBuilder.withLongOpt("verbose"); - OptionBuilder.withArgName("level"); - OptionBuilder.withDescription("verbosity level -- 0 is silence, " - + "5 is debug information, 6 is trace information.\n" + "default is level 5."); - // Argument options - opts.addOption(OptionBuilder.create('v')); - - OptionBuilder.hasArg(); - OptionBuilder.withLongOpt("alg-alias"); - OptionBuilder.withArgName("alias:original:mnemonic"); - OptionBuilder.withDescription("define an alias for an algorithm"); - opts.addOption(OptionBuilder.create('A')); + // no special options at the moment. } - public void parseCommandLine(String[] args) - throws org.apache.commons.cli.ParseException + protected void processOptions(CommandLine cli) throws ParseException { - CommandLineParser cli_parser = new PosixParser(); - CommandLine cli = cli_parser.parse(opts, args); - - if (cli.hasOption('h')) usage(); - - Logger rootLogger = Logger.getLogger(""); - - int value = parseInt(cli.getOptionValue('v'), -1); - switch (value) - { - case 0: - rootLogger.setLevel(Level.OFF); - break; - case 1: - rootLogger.setLevel(Level.SEVERE); - break; - case 2: - default: - rootLogger.setLevel(Level.WARNING); - break; - case 3: - rootLogger.setLevel(Level.INFO); - break; - case 4: - rootLogger.setLevel(Level.CONFIG); - case 5: - rootLogger.setLevel(Level.FINE); - break; - case 6: - rootLogger.setLevel(Level.ALL); - break; - } - - // I hate java.util.logging, btw. - for (Handler h : rootLogger.getHandlers()) - { - h.setLevel(rootLogger.getLevel()); - h.setFormatter(new BareLogFormatter()); - } - - String[] optstrs; - if ((optstrs = cli.getOptionValues('A')) != null) - { - for (int i = 0; i < optstrs.length; i++) - { - addArgAlias(optstrs[i]); - } - } keynames = cli.getArgs(); if (keynames.length < 1) @@ -141,67 +65,10 @@ public class KeyInfoTool usage(); } } - - /** Print out the usage and help statements, then quit. */ - private void usage() - { - HelpFormatter f = new HelpFormatter(); - - PrintWriter out = new PrintWriter(System.err); - - // print our own usage statement: - f.printHelp(out, 75, "jdnssec-keyinfo [..options..] keyfile", null, opts, - HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null); - - out.flush(); - System.exit(64); - } } - /** - * This is just a convenience method for parsing integers from strings. - * - * @param s - * the string to parse. - * @param def - * the default value, if the string doesn't parse. - * @return the parsed integer, or the default. - */ - private static int parseInt(String s, int def) + public void execute() throws Exception { - try - { - int v = Integer.parseInt(s); - return v; - } - catch (NumberFormatException e) - { - return def; - } - } - - private static void addArgAlias(String s) - { - if (s == null) return; - - DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance(); - - String[] v = s.split(":"); - if (v.length < 2) return; - - int alias = parseInt(v[0], -1); - if (alias <= 0) return; - int orig = parseInt(v[1], -1); - if (orig <= 0) return; - String mn = null; - if (v.length > 2) mn = v[2]; - - algs.addAlias(alias, mn, orig); - } - - public static void execute(CLIState state) throws Exception - { - for (int i = 0; i < state.keynames.length; ++i) { String keyname = state.keynames[i]; @@ -246,37 +113,9 @@ public class KeyInfoTool public static void main(String[] args) { - CLIState state = new CLIState(); + KeyInfoTool tool = new KeyInfoTool(); + tool.state = new CLIState(); - try - { - state.parseCommandLine(args); - } - catch (UnrecognizedOptionException e) - { - System.err.println("error: unknown option encountered: " + e.getMessage()); - state.usage(); - } - catch (AlreadySelectedException e) - { - System.err.println("error: mutually exclusive options have " - + "been selected:\n " + e.getMessage()); - state.usage(); - } - catch (Exception e) - { - System.err.println("error: unknown command line parsing exception:"); - e.printStackTrace(); - state.usage(); - } - - try - { - execute(state); - } - catch (Exception e) - { - e.printStackTrace(); - } + tool.run(tool.state, args); } } diff --git a/src/com/verisignlabs/dnssec/cl/SignKeyset.java b/src/com/verisignlabs/dnssec/cl/SignKeyset.java index 978a169..ec7d201 100644 --- a/src/com/verisignlabs/dnssec/cl/SignKeyset.java +++ b/src/com/verisignlabs/dnssec/cl/SignKeyset.java @@ -1,6 +1,4 @@ -// $Id: SignZone.java 2235 2009-02-07 20:37:29Z davidb $ -// -// Copyright (C) 2001-2003, 2009 VeriSign, Inc. +// Copyright (C) 2001-2003, 2011 VeriSign, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -22,26 +20,15 @@ package com.verisignlabs.dnssec.cl; import java.io.File; import java.io.FileFilter; import java.io.IOException; -import java.io.PrintWriter; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; -import java.util.TimeZone; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apache.commons.cli.AlreadySelectedException; + import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; -import org.apache.commons.cli.PosixParser; -import org.apache.commons.cli.UnrecognizedOptionException; import org.xbill.DNS.DNSSEC; import org.xbill.DNS.Name; import org.xbill.DNS.RRset; @@ -55,21 +42,18 @@ import com.verisignlabs.dnssec.security.*; * Instead of being able to sign an entire zone, it will just sign a given * DNSKEY RRset. * - * @author David Blacka (original) - * @author $Author: davidb $ - * @version $Revision: 2235 $ + * @author David Blacka */ -public class SignKeyset +public class SignKeyset extends CLBase { - private static Logger log; + private CLIState state; /** * This is an inner class used to hold all of the command line option state. */ - private static class CLIState + protected static class CLIState extends CLIStateBase { - private Options opts; - private File keyDirectory = null; + public File keyDirectory = null; public String[] keyFiles = null; public Date start = null; public Date expire = null; @@ -79,29 +63,18 @@ public class SignKeyset public CLIState() { - setupCLI(); + super("jdnssec-signkeyset [..options..] dnskeyset_file [key_file ...]"); } /** * Set up the command line options. - * - * @return a set of command line options. */ - private void setupCLI() + protected void setupOptions(Options opts) { - opts = new Options(); - // boolean options - opts.addOption("h", "help", false, "Print this message."); opts.addOption("a", "verify", false, "verify generated signatures>"); - OptionBuilder.hasOptionalArg(); - OptionBuilder.withLongOpt("verbose"); - OptionBuilder.withArgName("level"); - OptionBuilder.withDescription("verbosity level."); // Argument options - opts.addOption(OptionBuilder.create('v')); - OptionBuilder.hasArg(); OptionBuilder.withArgName("dir"); OptionBuilder.withLongOpt("key-directory"); @@ -126,49 +99,9 @@ public class SignKeyset opts.addOption(OptionBuilder.create('f')); } - public void parseCommandLine(String[] args) - throws org.apache.commons.cli.ParseException, ParseException, IOException + protected void processOptions(CommandLine cli) throws org.apache.commons.cli.ParseException { - CommandLineParser cli_parser = new PosixParser(); - CommandLine cli = cli_parser.parse(opts, args); - String optstr = null; - if (cli.hasOption('h')) usage(); - - Logger rootLogger = Logger.getLogger(""); - - int value = parseInt(cli.getOptionValue('v'), -1); - switch (value) - { - case 0: - rootLogger.setLevel(Level.OFF); - break; - case 1: - rootLogger.setLevel(Level.SEVERE); - break; - case 2: - default: - rootLogger.setLevel(Level.WARNING); - break; - case 3: - rootLogger.setLevel(Level.INFO); - break; - case 4: - rootLogger.setLevel(Level.CONFIG); - case 5: - rootLogger.setLevel(Level.FINE); - break; - case 6: - rootLogger.setLevel(Level.ALL); - break; - } - - // I hate java.util.logging, btw. - for (Handler h : rootLogger.getHandlers()) - { - h.setLevel(rootLogger.getLevel()); - h.setFormatter(new BareLogFormatter()); - } if (cli.hasOption('a')) verifySigs = true; @@ -218,46 +151,6 @@ public class SignKeyset System.arraycopy(files, 1, keyFiles, 0, files.length - 1); } } - - /** Print out the usage and help statements, then quit. */ - private void usage() - { - HelpFormatter f = new HelpFormatter(); - - PrintWriter out = new PrintWriter(System.err); - - // print our own usage statement: - f.printHelp(out, 75, "jdnssec-signkeyset [..options..] " - + "dnskeyset_file [key_file ...]", null, opts, - HelpFormatter.DEFAULT_LEFT_PAD, - HelpFormatter.DEFAULT_DESC_PAD, - "\ntime/offset = YYYYMMDDHHmmss|+offset|\"now\"+offset\n"); - - out.flush(); - System.exit(64); - } - } - - /** - * This is just a convenience method for parsing integers from strings. - * - * @param s - * the string to parse. - * @param def - * the default value, if the string doesn't parse. - * @return the parsed integer, or the default. - */ - private static int parseInt(String s, int def) - { - try - { - int v = Integer.parseInt(s); - return v; - } - catch (NumberFormatException e) - { - return def; - } } /** @@ -378,38 +271,7 @@ public class SignKeyset return null; } - /** - * Calculate a date/time from a command line time/offset duration string. - * - * @param start - * the start time to calculate offsets from. - * @param duration - * the time/offset string to parse. - * @return the calculated time. - */ - private static Date convertDuration(Date start, String duration) throws ParseException - { - if (start == null) start = new Date(); - if (duration.startsWith("now")) - { - start = new Date(); - if (duration.indexOf("+") < 0) return start; - - duration = duration.substring(3); - } - - if (duration.startsWith("+")) - { - long offset = (long) parseInt(duration.substring(1), 0) * 1000; - return new Date(start.getTime() + offset); - } - - SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss"); - dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT")); - return dateFormatter.parse(duration); - } - - public static void execute(CLIState state) throws Exception + public void execute() throws Exception { // Read in the zone List records = ZoneUtils.readZoneFile(state.inputfile, null); @@ -524,38 +386,9 @@ public class SignKeyset public static void main(String[] args) { - CLIState state = new CLIState(); - try - { - state.parseCommandLine(args); - } - catch (UnrecognizedOptionException e) - { - System.err.println("error: unknown option encountered: " + e.getMessage()); - state.usage(); - } - catch (AlreadySelectedException e) - { - System.err.println("error: mutually exclusive options have " - + "been selected:\n " + e.getMessage()); - state.usage(); - } - catch (Exception e) - { - System.err.println("error: unknown command line parsing exception:"); - e.printStackTrace(); - state.usage(); - } - - log = Logger.getLogger(SignKeyset.class.toString()); - - try - { - execute(state); - } - catch (Exception e) - { - e.printStackTrace(); - } + SignKeyset tool = new SignKeyset(); + tool.state = new CLIState(); + + tool.run(tool.state, args); } } diff --git a/src/com/verisignlabs/dnssec/cl/SignRRset.java b/src/com/verisignlabs/dnssec/cl/SignRRset.java index a4fe6bc..ca80344 100644 --- a/src/com/verisignlabs/dnssec/cl/SignRRset.java +++ b/src/com/verisignlabs/dnssec/cl/SignRRset.java @@ -1,6 +1,4 @@ -// $Id: SignZone.java 2235 2009-02-07 20:37:29Z davidb $ -// -// Copyright (C) 2001-2003, 2009 VeriSign, Inc. +// Copyright (C) 2001-2003, 2011 VeriSign, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -21,26 +19,15 @@ package com.verisignlabs.dnssec.cl; import java.io.File; import java.io.IOException; -import java.io.PrintWriter; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; -import java.util.TimeZone; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apache.commons.cli.AlreadySelectedException; import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; -import org.apache.commons.cli.PosixParser; -import org.apache.commons.cli.UnrecognizedOptionException; + import org.xbill.DNS.DNSSEC; import org.xbill.DNS.Name; import org.xbill.DNS.RRset; @@ -56,20 +43,17 @@ import com.verisignlabs.dnssec.security.*; * consideration of whether or not the RRset *should* be signed in the context * of a zone. * - * @author David Blacka (original) - * @author $Author: davidb $ - * @version $Revision: 2235 $ + * @author David Blacka */ -public class SignRRset +public class SignRRset extends CLBase { - private static Logger log; + private CLIState state; /** * This is an inner class used to hold all of the command line option state. */ - private static class CLIState + protected static class CLIState extends CLIStateBase { - private Options opts; private File keyDirectory = null; public String[] keyFiles = null; public Date start = null; @@ -80,29 +64,16 @@ public class SignRRset public CLIState() { - setupCLI(); + super("jdnssec-signrrset [..options..] rrset_file key_file [key_file ...]"); } /** * Set up the command line options. - * - * @return a set of command line options. */ - private void setupCLI() + protected void setupOptions(Options opts) { - opts = new Options(); - // boolean options - opts.addOption("h", "help", false, "Print this message."); opts.addOption("a", "verify", false, "verify generated signatures>"); - opts.addOption("m", "multiline", false, "Use a multiline format"); - - OptionBuilder.hasOptionalArg(); - OptionBuilder.withLongOpt("verbose"); - OptionBuilder.withArgName("level"); - OptionBuilder.withDescription("verbosity level."); - // Argument options - opts.addOption(OptionBuilder.create('v')); OptionBuilder.hasArg(); OptionBuilder.withArgName("dir"); @@ -128,52 +99,11 @@ public class SignRRset opts.addOption(OptionBuilder.create('f')); } - public void parseCommandLine(String[] args) - throws org.apache.commons.cli.ParseException, ParseException, IOException + protected void processOptions(CommandLine cli) throws org.apache.commons.cli.ParseException { - CommandLineParser cli_parser = new PosixParser(); - CommandLine cli = cli_parser.parse(opts, args); - String optstr = null; - if (cli.hasOption('h')) usage(); - - Logger rootLogger = Logger.getLogger(""); - - int value = parseInt(cli.getOptionValue('v'), -1); - switch (value) - { - case 0: - rootLogger.setLevel(Level.OFF); - break; - case 1: - rootLogger.setLevel(Level.SEVERE); - break; - case 2: - default: - rootLogger.setLevel(Level.WARNING); - break; - case 3: - rootLogger.setLevel(Level.INFO); - break; - case 4: - rootLogger.setLevel(Level.CONFIG); - case 5: - rootLogger.setLevel(Level.FINE); - break; - case 6: - rootLogger.setLevel(Level.ALL); - break; - } - - // I hate java.util.logging, btw. - for (Handler h : rootLogger.getHandlers()) - { - h.setLevel(rootLogger.getLevel()); - h.setFormatter(new BareLogFormatter()); - } if (cli.hasOption('a')) verifySigs = true; - if (cli.hasOption('m')) org.xbill.DNS.Options.set("multiline"); if ((optstr = cli.getOptionValue('D')) != null) { @@ -221,45 +151,6 @@ public class SignRRset System.arraycopy(files, 1, keyFiles, 0, files.length - 1); } } - - /** Print out the usage and help statements, then quit. */ - private void usage() - { - HelpFormatter f = new HelpFormatter(); - - PrintWriter out = new PrintWriter(System.err); - - // print our own usage statement: - f.printHelp(out, 75, "jdnssec-signrrset [..options..] " - + "rrset_file key_file [key_file ...]", null, opts, - HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, - "\ntime/offset = YYYYMMDDHHmmss|+offset|\"now\"+offset\n"); - - out.flush(); - System.exit(64); - } - } - - /** - * This is just a convenience method for parsing integers from strings. - * - * @param s - * the string to parse. - * @param def - * the default value, if the string doesn't parse. - * @return the parsed integer, or the default. - */ - private static int parseInt(String s, int def) - { - try - { - int v = Integer.parseInt(s); - return v; - } - catch (NumberFormatException e) - { - return def; - } } /** @@ -339,38 +230,7 @@ public class SignRRset return keys; } - /** - * Calculate a date/time from a command line time/offset duration string. - * - * @param start - * the start time to calculate offsets from. - * @param duration - * the time/offset string to parse. - * @return the calculated time. - */ - private static Date convertDuration(Date start, String duration) throws ParseException - { - if (start == null) start = new Date(); - if (duration.startsWith("now")) - { - start = new Date(); - if (duration.indexOf("+") < 0) return start; - - duration = duration.substring(3); - } - - if (duration.startsWith("+")) - { - long offset = (long) parseInt(duration.substring(1), 0) * 1000; - return new Date(start.getTime() + offset); - } - - SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss"); - dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT")); - return dateFormatter.parse(duration); - } - - public static void execute(CLIState state) throws Exception + public void execute() throws Exception { // Read in the zone List records = ZoneUtils.readZoneFile(state.inputfile, null); @@ -495,38 +355,9 @@ public class SignRRset public static void main(String[] args) { - CLIState state = new CLIState(); - try - { - state.parseCommandLine(args); - } - catch (UnrecognizedOptionException e) - { - System.err.println("error: unknown option encountered: " + e.getMessage()); - state.usage(); - } - catch (AlreadySelectedException e) - { - System.err.println("error: mutually exclusive options have " - + "been selected:\n " + e.getMessage()); - state.usage(); - } - catch (Exception e) - { - System.err.println("error: unknown command line parsing exception:"); - e.printStackTrace(); - state.usage(); - } - - log = Logger.getLogger(SignRRset.class.toString()); - - try - { - execute(state); - } - catch (Exception e) - { - e.printStackTrace(); - } + SignRRset tool = new SignRRset(); + tool.state = new CLIState(); + + tool.run(tool.state, args); } } diff --git a/src/com/verisignlabs/dnssec/cl/SignZone.java b/src/com/verisignlabs/dnssec/cl/SignZone.java index ae1cfc9..45b971b 100644 --- a/src/com/verisignlabs/dnssec/cl/SignZone.java +++ b/src/com/verisignlabs/dnssec/cl/SignZone.java @@ -1,6 +1,4 @@ -// $Id$ -// -// Copyright (C) 2001-2003, 2009 VeriSign, Inc. +// Copyright (C) 2001-2003, 2011 VeriSign, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -24,27 +22,17 @@ import java.io.File; import java.io.FileFilter; import java.io.FileReader; import java.io.IOException; -import java.io.PrintWriter; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Random; -import java.util.TimeZone; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apache.commons.cli.AlreadySelectedException; import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; -import org.apache.commons.cli.PosixParser; -import org.apache.commons.cli.UnrecognizedOptionException; +import org.apache.commons.cli.ParseException; + import org.xbill.DNS.DNSKEYRecord; import org.xbill.DNS.DNSSEC; import org.xbill.DNS.DSRecord; @@ -55,26 +43,28 @@ import org.xbill.DNS.TextParseException; import org.xbill.DNS.Type; import org.xbill.DNS.utils.base16; -import com.verisignlabs.dnssec.security.*; +import com.verisignlabs.dnssec.security.BINDKeyUtils; +import com.verisignlabs.dnssec.security.DnsKeyPair; +import com.verisignlabs.dnssec.security.DnsSecVerifier; +import com.verisignlabs.dnssec.security.JCEDnsSecSigner; +import com.verisignlabs.dnssec.security.SignUtils; +import com.verisignlabs.dnssec.security.ZoneUtils; /** * This class forms the command line implementation of a DNSSEC zone signer. * - * @author David Blacka (original) - * @author $Author$ - * @version $Revision$ + * @author David Blacka */ -public class SignZone +public class SignZone extends CLBase { - private static Logger log; + private CLIState state; /** * This is an inner class used to hold all of the command line option state. */ - private static class CLIState + private static class CLIState extends CLIStateBase { - private Options opts; - private File keyDirectory = null; + public File keyDirectory = null; public File keysetDirectory = null; public String[] kskFiles = null; public String[] keyFiles = null; @@ -95,34 +85,18 @@ public class SignZone public CLIState() { - setupCLI(); + super("jdnssec-signzone [..options..] zone_file [key_file ...]"); } - /** - * Set up the command line options. - * - * @return a set of command line options. - */ - private void setupCLI() + protected void setupOptions(Options opts) { - opts = new Options(); - // boolean options - opts.addOption("h", "help", false, "Print this message."); opts.addOption("a", "verify", false, "verify generated signatures>"); opts.addOption("F", "fully-sign-keyset", false, "sign the zone apex keyset with all available keys."); opts.addOption("V", "verbose-signing", false, "Display verbose signing activity."); - opts.addOption("m", "multiline", false, "Use a multiline format"); // Argument options - OptionBuilder.hasOptionalArg(); - OptionBuilder.withLongOpt("verbose"); - OptionBuilder.withArgName("level"); - OptionBuilder.withDescription("verbosity level -- 0 is silence, 3 is info, " - + "5 is debug information, 6 is trace information. default is level 2 (warning)"); - opts.addOption(OptionBuilder.create('v')); - OptionBuilder.hasArg(); OptionBuilder.withArgName("dir"); OptionBuilder.withLongOpt("keyset-directory"); @@ -193,12 +167,6 @@ public class SignZone OptionBuilder.withDescription("use this value for the NSEC3PARAM RR ttl"); opts.addOption(OptionBuilder.create()); - OptionBuilder.hasArg(); - OptionBuilder.withArgName("alias:original:mnemonic"); - OptionBuilder.withLongOpt("alg-alias"); - OptionBuilder.withDescription("Define an alias for an algorithm (may repeat)."); - opts.addOption(OptionBuilder.create('A')); - OptionBuilder.hasArg(); OptionBuilder.withArgName("id"); OptionBuilder.withLongOpt("ds-digest"); @@ -206,57 +174,15 @@ public class SignZone opts.addOption(OptionBuilder.create()); } - public void parseCommandLine(String[] args) - throws org.apache.commons.cli.ParseException, ParseException, IOException + protected void processOptions(CommandLine cli) throws ParseException { - CommandLineParser cli_parser = new PosixParser(); - CommandLine cli = cli_parser.parse(opts, args); - - String optstr = null; - String[] optstrs = null; - - if (cli.hasOption('h')) usage(); - - Logger rootLogger = Logger.getLogger(""); - - int value = parseInt(cli.getOptionValue('v'), -1); - switch (value) - { - case 0: - rootLogger.setLevel(Level.OFF); - break; - case 1: - rootLogger.setLevel(Level.SEVERE); - break; - case 2: - default: - rootLogger.setLevel(Level.WARNING); - break; - case 3: - rootLogger.setLevel(Level.INFO); - break; - case 4: - rootLogger.setLevel(Level.CONFIG); - case 5: - rootLogger.setLevel(Level.FINE); - break; - case 6: - rootLogger.setLevel(Level.ALL); - break; - } - - // I hate java.util.logging, btw. - for (Handler h : rootLogger.getHandlers()) - { - h.setLevel(rootLogger.getLevel()); - h.setFormatter(new BareLogFormatter()); - } + String optstr; + String[] optstrs; if (cli.hasOption('a')) verifySigs = true; if (cli.hasOption('3')) useNsec3 = true; if (cli.hasOption('O')) useOptOut = true; if (cli.hasOption('V')) verboseSigning = true; - if (cli.hasOption('m')) org.xbill.DNS.Options.set("multiline"); if (useOptOut && !useNsec3) { @@ -264,14 +190,6 @@ public class SignZone useOptOut = false; } - if ((optstrs = cli.getOptionValues('A')) != null) - { - for (int i = 0; i < optstrs.length; i++) - { - addArgAlias(optstrs[i]); - } - } - if (cli.hasOption('F')) fullySignKeyset = true; if ((optstr = cli.getOptionValue('d')) != null) @@ -297,7 +215,7 @@ public class SignZone if ((optstr = cli.getOptionValue('s')) != null) { - start = convertDuration(null, optstr); + start = CLBase.convertDuration(null, optstr); } else { @@ -307,11 +225,11 @@ public class SignZone if ((optstr = cli.getOptionValue('e')) != null) { - expire = convertDuration(start, optstr); + expire = CLBase.convertDuration(start, optstr); } else { - expire = convertDuration(start, "+2592000"); // 30 days + expire = CLBase.convertDuration(start, "+2592000"); // 30 days } outputfile = cli.getOptionValue('f'); @@ -321,7 +239,14 @@ public class SignZone if ((optstr = cli.getOptionValue('I')) != null) { File includeNamesFile = new File(optstr); - includeNames = getNameList(includeNamesFile); + try + { + includeNames = getNameList(includeNamesFile); + } + catch (IOException e) + { + throw new ParseException(e.getMessage()); + } } if ((optstr = cli.getOptionValue('S')) != null) @@ -385,64 +310,6 @@ public class SignZone System.arraycopy(files, 1, keyFiles, 0, files.length - 1); } } - - private void addArgAlias(String s) - { - if (s == null) return; - - DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance(); - - String[] v = s.split(":"); - if (v.length < 2) return; - - int alias = parseInt(v[0], -1); - if (alias <= 0) return; - int orig = parseInt(v[1], -1); - if (orig <= 0) return; - String mn = null; - if (v.length > 2) mn = v[2]; - - algs.addAlias(alias, mn, orig); - } - - /** Print out the usage and help statements, then quit. */ - private void usage() - { - HelpFormatter f = new HelpFormatter(); - - PrintWriter out = new PrintWriter(System.err); - - // print our own usage statement: - f.printHelp(out, 75, - "jdnssec-signzone [..options..] " + "zone_file [key_file ...]", null, - opts, HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, - "\ntime/offset = YYYYMMDDHHmmss|+offset|\"now\"+offset\n"); - - out.flush(); - System.exit(64); - } - } - - /** - * This is just a convenience method for parsing integers from strings. - * - * @param s - * the string to parse. - * @param def - * the default value, if the string doesn't parse. - * @return the parsed integer, or the default. - */ - private static int parseInt(String s, int def) - { - try - { - int v = Integer.parseInt(s); - return v; - } - catch (NumberFormatException e) - { - return def; - } } /** @@ -497,7 +364,8 @@ public class SignZone * a string array containing the base names or paths of the keys to * be loaded. * @param start_index - * the starting index of keyfiles string array to use. This allows us + * the starting index of keyfiles string array to use. This allows + * us * to use the straight command line argument array. * @param inDirectory * the directory to look in (may be null). @@ -602,12 +470,15 @@ public class SignZone * Load keysets (which contain delegation point security info). * * @param inDirectory - * the directory to look for the keyset files (may be null, in which + * the directory to look for the keyset files (may be null, in + * which * case it defaults to looking in the current working directory). * @param zonename - * the name of the zone we are signing, so we can ignore keysets that + * the name of the zone we are signing, so we can ignore keysets + * that * do not belong in the zone. - * @return a list of {@link org.xbill.DNS.Record}s found in the keyset files. + * @return a list of {@link org.xbill.DNS.Record}s found in the keyset + * files. */ private static List getKeysets(File inDirectory, Name zonename) throws IOException { @@ -677,44 +548,14 @@ public class SignZone return res; } - /** - * Calculate a date/time from a command line time/offset duration string. - * - * @param start - * the start time to calculate offsets from. - * @param duration - * the time/offset string to parse. - * @return the calculated time. - */ - private static Date convertDuration(Date start, String duration) throws ParseException - { - if (start == null) start = new Date(); - if (duration.startsWith("now")) - { - start = new Date(); - if (duration.indexOf("+") < 0) return start; - - duration = duration.substring(3); - } - - if (duration.startsWith("+")) - { - long offset = (long) parseInt(duration.substring(1), 0) * 1000; - return new Date(start.getTime() + offset); - } - - SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss"); - dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT")); - return dateFormatter.parse(duration); - } - /** * Determine if the given keypairs can be used to sign the zone. * * @param zonename * the zone origin. * @param keypairs - * a list of {@link DnsKeyPair} objects that will be used to sign the + * a list of {@link DnsKeyPair} objects that will be used to sign + * the * zone. * @return true if the keypairs valid. */ @@ -735,7 +576,7 @@ public class SignZone return true; } - public static void execute(CLIState state) throws Exception + public void execute() throws Exception { // Read in the zone List records = ZoneUtils.readZoneFile(state.zonefile, null); @@ -904,38 +745,9 @@ public class SignZone public static void main(String[] args) { - CLIState state = new CLIState(); - try - { - state.parseCommandLine(args); - } - catch (UnrecognizedOptionException e) - { - System.err.println("error: unknown option encountered: " + e.getMessage()); - state.usage(); - } - catch (AlreadySelectedException e) - { - System.err.println("error: mutually exclusive options have " - + "been selected:\n " + e.getMessage()); - state.usage(); - } - catch (Exception e) - { - System.err.println("error: unknown command line parsing exception:"); - e.printStackTrace(); - state.usage(); - } + SignZone tool = new SignZone(); + tool.state = new CLIState(); - log = Logger.getLogger(SignZone.class.toString()); - - try - { - execute(state); - } - catch (Exception e) - { - e.printStackTrace(); - } + tool.run(tool.state, args); } } diff --git a/src/com/verisignlabs/dnssec/cl/VerifyZone.java b/src/com/verisignlabs/dnssec/cl/VerifyZone.java index 223643e..35a958e 100644 --- a/src/com/verisignlabs/dnssec/cl/VerifyZone.java +++ b/src/com/verisignlabs/dnssec/cl/VerifyZone.java @@ -1,6 +1,4 @@ -// $Id$ -// -// Copyright (C) 2001-2003 VeriSign, Inc. +// Copyright (C) 2011 VeriSign, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -19,41 +17,31 @@ package com.verisignlabs.dnssec.cl; -import java.io.PrintWriter; import java.util.List; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apache.commons.cli.AlreadySelectedException; import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; -import org.apache.commons.cli.PosixParser; -import org.apache.commons.cli.UnrecognizedOptionException; -import com.verisignlabs.dnssec.security.*; +import com.verisignlabs.dnssec.security.ZoneUtils; +import com.verisignlabs.dnssec.security.ZoneVerifier; /** * This class forms the command line implementation of a DNSSEC zone validator. * - * @author David Blacka (original) - * @author $Author$ - * @version $Revision$ + * @author David Blacka */ -public class VerifyZone +public class VerifyZone extends CLBase { - private static Logger log; - + + private CLIState state; + /** * This is a small inner class used to hold all of the command line option * state. */ - private static class CLIState + protected static class CLIState extends CLIStateBase { - private Options opts; public String zonefile = null; public String[] keyfiles = null; public int startfudge = 0; @@ -62,35 +50,11 @@ public class VerifyZone public CLIState() { - setupCLI(); + super("jdnssec-verifyzone [..options..] zonefile"); } - /** - * Set up the command line options. - * - * @return a set of command line options. - */ - private void setupCLI() + protected void setupOptions(Options opts) { - opts = new Options(); - - // boolean options - opts.addOption("h", "help", false, "Print this message."); - opts.addOption("m", "multiline", false, "log DNS records using 'multiline' format"); - - OptionBuilder.hasOptionalArg(); - OptionBuilder.withLongOpt("verbose"); - OptionBuilder.withArgName("level"); - OptionBuilder.withDescription("verbosity level -- 0 is silence, 3 is info, " - + "5 is debug information, 6 is trace information. default is level 2 (warning)"); - opts.addOption(OptionBuilder.create('v')); - - OptionBuilder.hasArg(); - OptionBuilder.withArgName("alias:original:mnemonic"); - OptionBuilder.withLongOpt("alg-alias"); - OptionBuilder.withDescription("Define an alias for an algorithm"); - opts.addOption(OptionBuilder.create('A')); - OptionBuilder.hasOptionalArg(); OptionBuilder.withLongOpt("sig-start-fudge"); OptionBuilder.withArgName("seconds"); @@ -107,55 +71,9 @@ public class VerifyZone OptionBuilder.withDescription("Ignore RRSIG inception and expiration time errors."); opts.addOption(OptionBuilder.create()); } - - public void parseCommandLine(String[] args) - throws org.apache.commons.cli.ParseException + + protected void processOptions(CommandLine cli) { - CommandLineParser cli_parser = new PosixParser(); - CommandLine cli = cli_parser.parse(opts, args); - - if (cli.hasOption('h')) usage(); - - Logger rootLogger = Logger.getLogger(""); - int value = parseInt(cli.getOptionValue('v'), -1); - - switch (value) - { - case 0: - rootLogger.setLevel(Level.OFF); - break; - case 1: - rootLogger.setLevel(Level.SEVERE); - break; - case 2: - default: - rootLogger.setLevel(Level.WARNING); - break; - case 3: - rootLogger.setLevel(Level.INFO); - break; - case 4: - rootLogger.setLevel(Level.CONFIG); - case 5: - rootLogger.setLevel(Level.FINE); - break; - case 6: - rootLogger.setLevel(Level.ALL); - break; - } - - // I hate java.util.logging, btw. - for (Handler h : rootLogger.getHandlers()) - { - h.setLevel(rootLogger.getLevel()); - h.setFormatter(new BareLogFormatter()); - } - - if (cli.hasOption('m')) - { - org.xbill.DNS.Options.set("multiline"); - } - if (cli.hasOption("ignore-time")) { ignoreTime = true; @@ -197,69 +115,11 @@ public class VerifyZone System.arraycopy(cl_args, 1, keyfiles, 0, keyfiles.length); } } - - private void addArgAlias(String s) - { - if (s == null) return; - - DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance(); - - String[] v = s.split(":"); - if (v.length < 2) return; - - int alias = parseInt(v[0], -1); - if (alias <= 0) return; - int orig = parseInt(v[1], -1); - if (orig <= 0) return; - String mn = null; - if (v.length > 2) mn = v[2]; - - algs.addAlias(alias, mn, orig); - } - - /** Print out the usage and help statements, then quit. */ - public void usage() - { - HelpFormatter f = new HelpFormatter(); - - PrintWriter out = new PrintWriter(System.err); - - // print our own usage statement: - f.printHelp(out, 75, "jdnssec-verifyzone [..options..] zonefile " - + "[keyfile [keyfile...]]", null, opts, - HelpFormatter.DEFAULT_LEFT_PAD, - HelpFormatter.DEFAULT_DESC_PAD, null); - - out.flush(); - System.exit(64); - - } - - /** - * This is just a convenience method for parsing integers from strings. - * - * @param s - * the string to parse. - * @param def - * the default value, if the string doesn't parse. - * @return the parsed integer, or the default. - */ - private static int parseInt(String s, int def) - { - try - { - int v = Integer.parseInt(s); - return v; - } - catch (NumberFormatException e) - { - return def; - } - } - } - public static void execute(CLIState state) throws Exception + + + public void execute() throws Exception { ZoneVerifier zoneverifier = new ZoneVerifier(); zoneverifier.getVerifier().setStartFudge(state.startfudge); @@ -286,39 +146,9 @@ public class VerifyZone public static void main(String[] args) { - CLIState state = new CLIState(); - - try - { - state.parseCommandLine(args); - } - catch (UnrecognizedOptionException e) - { - System.err.println("error: unknown option encountered: " + e.getMessage()); - state.usage(); - } - catch (AlreadySelectedException e) - { - System.err.println("error: mutually exclusive options have " - + "been selected:\n " + e.getMessage()); - state.usage(); - } - catch (Exception e) - { - System.err.println("error: unknown command line parsing exception:"); - e.printStackTrace(); - state.usage(); - } - - log = Logger.getLogger(VerifyZone.class.toString()); - - try - { - execute(state); - } - catch (Exception e) - { - e.printStackTrace(); - } + VerifyZone tool = new VerifyZone(); + tool.state = new CLIState(); + + tool.run(tool.state, args); } } diff --git a/src/com/verisignlabs/dnssec/cl/ZoneFormat.java b/src/com/verisignlabs/dnssec/cl/ZoneFormat.java index aea0040..594171c 100644 --- a/src/com/verisignlabs/dnssec/cl/ZoneFormat.java +++ b/src/com/verisignlabs/dnssec/cl/ZoneFormat.java @@ -1,31 +1,19 @@ -/* - * $Id$ - * - * Copyright (c) 2005 VeriSign. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. 2. Redistributions in - * binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. 3. The name of the author may not - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ +// Copyright (C) 2011 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA package com.verisignlabs.dnssec.cl; @@ -38,11 +26,10 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.cli.*; +import org.apache.commons.cli.Options; import org.xbill.DNS.*; -import org.xbill.DNS.Options; import org.xbill.DNS.utils.base32; -import com.verisignlabs.dnssec.security.BareLogFormatter; import com.verisignlabs.dnssec.security.RecordComparator; /** @@ -53,72 +40,34 @@ import com.verisignlabs.dnssec.security.RecordComparator; * @author $Author: davidb $ * @version $Revision: 2218 $ */ -public class ZoneFormat +public class ZoneFormat extends CLBase { - // private static Logger log; + private CLIState state; /** * This is a small inner class used to hold all of the command line option * state. */ - private static class CLIState + protected static class CLIState extends CLIStateBase { - private org.apache.commons.cli.Options opts; - public String file; - public boolean assignNSEC3; + public String file; + public boolean assignNSEC3; public CLIState() { - setupCLI(); + super("jdnssec-zoneformat [..options..] zonefile"); } - public void parseCommandLine(String[] args) - throws org.apache.commons.cli.ParseException + protected void setupOptions(Options opts) { - CommandLineParser cli_parser = new PosixParser(); - CommandLine cli = cli_parser.parse(opts, args); + opts.addOption("N", "nsec3", false, + "attempt to determine the original ownernames for NSEC3 RRs."); + } - // String optstr = null; - - if (cli.hasOption('h')) usage(); - if (cli.hasOption('m')) Options.set("multiline"); + protected void processOptions(CommandLine cli) throws ParseException + { if (cli.hasOption('N')) assignNSEC3 = true; - Logger rootLogger = Logger.getLogger(""); - - int value = parseInt(cli.getOptionValue('v'), -1); - switch (value) - { - case 0: - rootLogger.setLevel(Level.OFF); - break; - case 1: - rootLogger.setLevel(Level.SEVERE); - break; - case 2: - default: - rootLogger.setLevel(Level.WARNING); - break; - case 3: - rootLogger.setLevel(Level.INFO); - break; - case 4: - rootLogger.setLevel(Level.CONFIG); - case 5: - rootLogger.setLevel(Level.FINE); - break; - case 6: - rootLogger.setLevel(Level.ALL); - break; - } - - // I hate java.util.logging, btw. - for (Handler h : rootLogger.getHandlers()) - { - h.setLevel(rootLogger.getLevel()); - h.setFormatter(new BareLogFormatter()); - } - String[] cl_args = cli.getArgs(); if (cl_args.length < 1) @@ -129,69 +78,6 @@ public class ZoneFormat file = cl_args[0]; } - - /** - * Set up the command line options. - * - * @return a set of command line options. - */ - private void setupCLI() - { - opts = new org.apache.commons.cli.Options(); - - // boolean options - opts.addOption("h", "help", false, "Print this message."); - opts.addOption("m", "multiline", false, "Use a multiline format"); - opts.addOption("N", "nsec3", false, - "attempt to determine the original ownernames for NSEC3 RRs."); - - // Argument options - OptionBuilder.hasOptionalArg(); - OptionBuilder.withLongOpt("verbose"); - OptionBuilder.withArgName("level"); - OptionBuilder.withDescription("verbosity level -- 0 is silence, " - + "5 is debug information, 6 is trace information.\n" + "default is level 5."); - opts.addOption(OptionBuilder.create('v')); - } - - /** Print out the usage and help statements, then quit. */ - public void usage() - { - HelpFormatter f = new HelpFormatter(); - - PrintWriter out = new PrintWriter(System.err); - - // print our own usage statement: - f.printHelp(out, 75, "jdnssec-zoneformat [..options..] zonefile", null, opts, - HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null); - - out.flush(); - System.exit(64); - - } - - /** - * This is just a convenience method for parsing integers from strings. - * - * @param s - * the string to parse. - * @param def - * the default value, if the string doesn't parse. - * @return the parsed integer, or the default. - */ - private static int parseInt(String s, int def) - { - try - { - int v = Integer.parseInt(s); - return v; - } - catch (NumberFormatException e) - { - return def; - } - } - } private static List readZoneFile(String filename) throws IOException @@ -283,8 +169,7 @@ public class ZoneFormat } } - private static void execute(CLIState state) throws IOException, - NoSuchAlgorithmException + public void execute() throws IOException, NoSuchAlgorithmException { List z = readZoneFile(state.file); if (state.assignNSEC3) determineNSEC3Owners(z); @@ -293,40 +178,10 @@ public class ZoneFormat public static void main(String[] args) { - CLIState state = new CLIState(); - - try - { - state.parseCommandLine(args); - } - catch (UnrecognizedOptionException e) - { - System.err.println("error: unknown option encountered: " + e.getMessage()); - state.usage(); - } - catch (AlreadySelectedException e) - { - System.err.println("error: mutually exclusive options have " - + "been selected:\n " + e.getMessage()); - state.usage(); - } - catch (Exception e) - { - System.err.println("error: unknown command line parsing exception:"); - e.printStackTrace(); - state.usage(); - } - - // log = Logger.getLogger(VerifyZone.class.toString()); - - try - { - execute(state); - } - catch (Exception e) - { - e.printStackTrace(); - } + ZoneFormat tool = new ZoneFormat(); + tool.state = new CLIState(); + + tool.run(tool.state, args); } }