Cli improvements (#17)
* add config file processing, refactor CLIBase some * fix algorithm aliases with key generation * Refactor to remove CLIState et al, move CLI common statics to new Utils * only use usage() for help, otherwise fail() * add a universal command line client, build a one-jar to use it. * bump the version * update ChangeLog, README, README.TODO, minor fixes * undo overzealous find/replace. sigh. * fix use_large_exponent logic in KeyGen * more fixes, minor improvements
This commit is contained in:
parent
2876649a4e
commit
1727d7c7d8
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
build
|
||||
bin/main
|
||||
dist/
|
||||
.classpath
|
||||
.project
|
||||
.gradle
|
||||
|
15
ChangeLog
15
ChangeLog
@ -1,3 +1,18 @@
|
||||
2024-04-07 David Blacka <david@blacka.com>
|
||||
|
||||
* Released version 0.20
|
||||
* Removed support for Gradle builds since the gradle config was
|
||||
out-of-date
|
||||
* Requires Java 15 or later for EdDSA support
|
||||
* Supports DNSSEC algorithm 16 using the SunEC provider
|
||||
* Supports a java properties-formatted config file. See
|
||||
jdnssec-tools.properties.example for an example
|
||||
* Updated to dnsjava 3.5.3
|
||||
* Updated to commons-cli 1.6.0
|
||||
* Added a "one-jar" distribution method, and a "univeral" CLI to
|
||||
use with it.
|
||||
* Formatting and linter suggestions
|
||||
|
||||
2024-03-25 David Blacka <davidb@verisign.com>
|
||||
|
||||
* Released version 0.19
|
||||
|
@ -11,5 +11,3 @@ This bit of code has been around since approximately 2005, and has been in "mini
|
||||
* allowing for an external sort once the data is shown to be larger than X, and/or
|
||||
* allowing for a memory-constrained internal sort that uses disk, and/or,
|
||||
* figuring out how to let the JVM use *a lot* of memory.
|
||||
* Add support for algorithm 16, perhaps refactor algorithm 15 support using bouncycastle.
|
||||
* Note that our current dnsjava version, 3.5.1 has some support, although it isn't clear if it has sign/verify support.
|
||||
|
32
README.md
32
README.md
@ -8,9 +8,13 @@ This is a collection of DNSSEC tools written in Java. They are intended to be a
|
||||
|
||||
These tools depend upon DNSjava (<https://github.com/dnsjava/dnsjava>), the Jakarta Commons CLI and Logging libraries (<https://commons.apache.org/proper/commons-cli>), slf4j (<https://www.slf4j.org>), and Sun's Java Cryptography extensions. A copy of each of these libraries is included in the distribution.
|
||||
|
||||
See the "licenses" directory for the licensing information of this package and the other packages that are distributed with it.
|
||||
See the "[licenses](https://github.com/dblacka/jdnssec-tools/tree/master/licenses)" directory for the licensing information of this package and the other packages that are distributed with it.
|
||||
|
||||
Getting started:
|
||||
## Getting Started
|
||||
|
||||
### Using the binary distribution
|
||||
|
||||
The binary distributions can be downloaded from the [releases](https://github.com/dblacka/jdnssec-tools/releases) page. To use it;
|
||||
|
||||
1. Unpack the binary distribution:
|
||||
|
||||
@ -21,9 +25,11 @@ Getting started:
|
||||
cd java-dnssec-tools-x.x.x
|
||||
./bin/jdnssec-signzone -h
|
||||
|
||||
Building from source:
|
||||
### Building from source
|
||||
|
||||
1. Unpack the source distribution, preferably into the same directory that the binary distribution was unpacked.
|
||||
There is a source distribution also downloadable from the [releases](https://github.com/dblacka/jdnssec-tools/releases) page, but this should work with a clone of this repo.
|
||||
|
||||
1. (If downloaded) Unpack the source distribution, preferably into the same directory that the binary distribution was unpacked.
|
||||
|
||||
tar zxvf java-dnssec-tools-x.x.x-src.tar.gz
|
||||
|
||||
@ -32,17 +38,19 @@ Building from source:
|
||||
|
||||
ant
|
||||
|
||||
4. You can build the distribution tarballs with 'ant dist'. You can run the tools directly from the build area (without building the jdnssec-tools.jar file) by using the ./bin/\_jdnssec_* wrappers.
|
||||
|
||||
5. Alternatively, build the project using gradle:
|
||||
|
||||
gradlew clean
|
||||
gradlew assemble -i
|
||||
|
||||
The resulting jar file gets generated in build/libs.
|
||||
4. You can build the distribution tarballs with 'ant dist', although the main `ant` build command will have built the primary jar file.
|
||||
|
||||
The source for this project is available in git on github: <https://github.com/dblacka/jdnssec-tools>
|
||||
|
||||
### Using the one-jar distribution
|
||||
|
||||
As of version 0.20, there is a one-jar (aka an executable jar) as part of the distribution. It can also be downloaded from the [releases](https://github.com/dblacka/jdnssec-tools/releases) page.
|
||||
|
||||
1. Fetch the one-jar distribution.
|
||||
2. Invoke with `java -jar jdnssec-tools-x.x.x.jar`
|
||||
|
||||
java -jar jdnssec-tools-x.x.x.jar signzone -h
|
||||
|
||||
---
|
||||
|
||||
Questions or comments may be directed to the author (<mailto:davidb@verisign.com>), or by creating issues in the [github issue tracker](https://github.com/dblacka/jdnssec-tools/issues).
|
||||
|
@ -1,19 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.DSTool "$@"
|
@ -1,19 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.KeyGen "$@"
|
@ -1,19 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.KeyInfoTool "$@"
|
@ -1,19 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.SignKeyset "$@"
|
@ -1,19 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.SignZone "$@"
|
@ -1,19 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.VerifyZone "$@"
|
@ -1,19 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.ZoneFormat "$@"
|
22
bin/jdnssec-tools
Executable file
22
bin/jdnssec-tools
Executable file
@ -0,0 +1,22 @@
|
||||
#! /bin/sh
|
||||
|
||||
thisdir=$(dirname $0)
|
||||
basedir=$(cd $thisdir/.. || exit; pwd)
|
||||
|
||||
ulimit_max=$(ulimit -H -n)
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
for i in "$basedir"/lib/*.jar "$basedir"/lib/*.zip "$basedir"/build/libs/*.jar; do
|
||||
if ! [ -f $i ]; then continue; fi
|
||||
if [ -z "$CLASSPATH" ]; then
|
||||
CLASSPATH=$i
|
||||
else
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
fi
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.CLI "$@"
|
87
build.xml
87
build.xml
@ -10,18 +10,20 @@
|
||||
|
||||
-->
|
||||
|
||||
<project default="compile" basedir=".">
|
||||
<project default="build" basedir=".">
|
||||
|
||||
<property file="build.properties" />
|
||||
<property file="VERSION" />
|
||||
|
||||
<property name="sectools-distname" value="jdnssec-tools-${version}" />
|
||||
|
||||
<property name="build.dir" value="build" />
|
||||
<property name="build.dest" value="${build.dir}/classes" />
|
||||
<property name="build.lib.dest" value="${build.dir}/libs" />
|
||||
<property name="build.src" value="src/main/java" />
|
||||
|
||||
<property name="dist.dir" value="dist"/>
|
||||
<property name="dist.name" value="jdnssec-tools-${version}" />
|
||||
|
||||
<property name="packages" value="com.verisignlabs.dnssec.*" />
|
||||
<property name="doc.dir" value="docs" />
|
||||
<property name="javadoc.dest" value="${doc.dir}/javadoc" />
|
||||
@ -33,6 +35,7 @@
|
||||
<pathelement location="${build.dest}" />
|
||||
<fileset dir="${lib.dir}" includes="*.jar,*.zip" />
|
||||
</path>
|
||||
|
||||
<property name="project.classpath" refid="project.classpath" />
|
||||
|
||||
<target name="prepare-src">
|
||||
@ -40,7 +43,7 @@
|
||||
<mkdir dir="${build.lib.dest}" />
|
||||
</target>
|
||||
|
||||
<target name="sectools" depends="prepare-src" >
|
||||
<target name="compile" depends="prepare-src" >
|
||||
<javac srcdir="${build.src}"
|
||||
destdir="${build.dest}"
|
||||
classpathref="project.classpath"
|
||||
@ -51,14 +54,29 @@
|
||||
release="${build.java_version}" />
|
||||
</target>
|
||||
|
||||
<target name="sectools-jar" depends="usage,sectools">
|
||||
<target name="build-jar" depends="usage, compile">
|
||||
<jar jarfile="${build.lib.dest}/jdnssec-tools.jar"
|
||||
basedir="${build.dest}"
|
||||
includes="com/verisignlabs/dnssec/" />
|
||||
</target>
|
||||
|
||||
<target name="compile"
|
||||
depends="usage,sectools-jar">
|
||||
<target name="build"
|
||||
depends="usage,build-jar">
|
||||
</target>
|
||||
|
||||
<target name="build-onejar" depends="compile">
|
||||
<jar destfile="${dist.dir}/${dist.name}.jar">
|
||||
<zipfileset dir="${build.dest}" includes="**/*.class" />
|
||||
|
||||
<zipfileset src="${lib.dir}/dnsjava-3.5.3.jar" />
|
||||
<zipfileset src="${lib.dir}/commons-cli-1.6.0.jar" />
|
||||
<zipfileset src="${lib.dir}/slf4j-api-1.7.36.jar" />
|
||||
<zipfileset src="${lib.dir}/slf4j-simple-1.7.36.jar" />
|
||||
<manifest>
|
||||
<attribute name="Main-Class"
|
||||
value="com.verisignlabs.dnssec.cl.CLI" />
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="javadoc" depends="usage">
|
||||
@ -75,16 +93,21 @@
|
||||
</javadoc>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="clean" depends="usage">
|
||||
<delete dir="${build.dest}" />
|
||||
<delete dir="${build.lib.dest}" />
|
||||
<delete dir="${dist.dir}" />
|
||||
</target>
|
||||
|
||||
<target name="sectools-dist-prepare" depends="usage, compile, javadoc">
|
||||
<mkdir dir="${sectools-distname}" />
|
||||
<target name="dist-clean" depends="usage">
|
||||
<delete dir="${dist.name}" />
|
||||
</target>
|
||||
|
||||
<copy todir="${sectools-distname}">
|
||||
<target name="dist-prepare" depends="usage, build, javadoc">
|
||||
<mkdir dir="${dist.dir}" />
|
||||
<mkdir dir="${dist.name}" />
|
||||
|
||||
<copy todir="${dist.name}">
|
||||
<fileset dir=".">
|
||||
<include name="bin/jdnssec-*" />
|
||||
<include name="lib/*.jar" />
|
||||
@ -99,37 +122,33 @@
|
||||
</fileset>
|
||||
</copy>
|
||||
|
||||
<copy todir="${sectools-distname}/lib">
|
||||
<copy todir="${dist.name}/lib">
|
||||
<fileset dir="${build.lib.dest}">
|
||||
<include name="*.jar" />
|
||||
</fileset>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="sectools-dist-clean">
|
||||
<delete dir="${sectools-distname}" />
|
||||
</target>
|
||||
|
||||
<patternset id="exec.files">
|
||||
<include name="${sectools-distname}/bin/jdnssec-*" />
|
||||
<include name="${dist.name}/bin/jdnssec-*" />
|
||||
</patternset>
|
||||
|
||||
<patternset id="src.files">
|
||||
<include name="${sectools-distname}/src/" />
|
||||
<include name="${sectools-distname}/build.xml" />
|
||||
<include name="${sectools-distname}/build.properties" />
|
||||
<include name="${dist.name}/src/" />
|
||||
<include name="${dist.name}/build.xml" />
|
||||
<include name="${dist.name}/build.properties" />
|
||||
</patternset>
|
||||
|
||||
<patternset id="bin.files">
|
||||
<include name="${sectools-distname}/doc/" />
|
||||
<include name="${sectools-distname}/lib/" />
|
||||
<include name="${sectools-distname}/licenses/" />
|
||||
<include name="${sectools-distname}/VERSION" />
|
||||
<include name="${sectools-distname}/README" />
|
||||
<include name="${dist.name}/doc/" />
|
||||
<include name="${dist.name}/lib/" />
|
||||
<include name="${dist.name}/licenses/" />
|
||||
<include name="${dist.name}/VERSION" />
|
||||
<include name="${dist.name}/README" />
|
||||
</patternset>
|
||||
|
||||
<target name="sectools-bin-dist" depends="sectools-dist-prepare">
|
||||
<tar destfile="${sectools-distname}.tar.gz" compression="gzip">
|
||||
<target name="bin-dist" depends="dist-prepare">
|
||||
<tar destfile="${dist.dir}/${dist.name}.tar.gz" compression="gzip">
|
||||
<tarfileset mode="755" dir=".">
|
||||
<patternset refid="exec.files" />
|
||||
</tarfileset>
|
||||
@ -139,21 +158,16 @@
|
||||
</tar>
|
||||
</target>
|
||||
|
||||
<target name="sectools-src-dist" depends="sectools-dist-prepare">
|
||||
<tar destfile="${sectools-distname}-src.tar.gz"
|
||||
compression="gzip">
|
||||
<target name="src-dist" depends="dist-prepare">
|
||||
<tar destfile="${dist.dir}/${dist.name}-src.tar.gz" compression="gzip">
|
||||
<tarfileset dir=".">
|
||||
<patternset refid="src.files" />
|
||||
</tarfileset>
|
||||
</tar>
|
||||
</target>
|
||||
|
||||
<target name="sectools-dist"
|
||||
depends="sectools-bin-dist,sectools-src-dist, sectools-dist-clean">
|
||||
</target>
|
||||
|
||||
|
||||
<target name="dist" depends="sectools-dist">
|
||||
<target name="dist"
|
||||
depends="bin-dist, src-dist, build-onejar, dist-clean">
|
||||
</target>
|
||||
|
||||
<target name="usage">
|
||||
@ -161,10 +175,11 @@
|
||||
<echo message="jdnssec-tools v. ${version} Build System" />
|
||||
<echo message="--------------------------------" />
|
||||
<echo message="Available Targets:" />
|
||||
<echo message=" compile (default) - compiles the source code, creates jar" />
|
||||
<echo message=" build (default) - compiles the source code, creates main jar" />
|
||||
<echo message=" javadoc - create javadoc from source" />
|
||||
<echo message=" clean - delete class files" />
|
||||
<echo message=" dist - package it up" />
|
||||
<echo message=" onejar - build the executable jar" />
|
||||
<echo message=" usage - this help message" />
|
||||
<echo message=" " />
|
||||
</target>
|
||||
|
93
jdnssec-tools.properties.example
Normal file
93
jdnssec-tools.properties.example
Normal file
@ -0,0 +1,93 @@
|
||||
# An example properties file for jdnssec-tools
|
||||
# Properties may be be scoped by the tool name, which is the name minus "jdnssec-"
|
||||
# If unscoped, the same named property will be used by multiple tools
|
||||
|
||||
# Common properties
|
||||
|
||||
# log_level = warning
|
||||
# verbose = true # same as log_level = fine (true) or log_level = warning (false)
|
||||
# multiline = false
|
||||
|
||||
# algorithm aliasing is <scope>.alias.<new-mnemonic> = <orig-alg-id>:<alias-alg-id>
|
||||
# alias.NEWALG = 8:100
|
||||
|
||||
# jdnssec-dstool properties
|
||||
|
||||
## These are all equivalent. Unscoped properties might apply to other tools
|
||||
# dstool.digest_algorithm = 4
|
||||
# digest_algorithm = 4 # applies to jdnssec-signzone, too
|
||||
# dstool.digest_id = 4
|
||||
|
||||
# jdnssec-keygen properties
|
||||
|
||||
# keygen.use_large_exponent = true
|
||||
# keygen.key_directory = .
|
||||
# key_directory = /path/to/dnskey_files # applies to jdnssec-sign*
|
||||
# keygen.algorithm = ED448
|
||||
# keygen.keylength = 2048
|
||||
# keygen.keylen = 2048 # same thing
|
||||
# keygen.ttl = 3600
|
||||
|
||||
# jdnssec-keyinfotool
|
||||
|
||||
# no additional keys
|
||||
|
||||
# jdnssec-signkeyset
|
||||
|
||||
# signkeyset.verify = false
|
||||
# signkeyset.key_directory = .
|
||||
# signkeyset.start = -300
|
||||
# signkeyset.inception = 1712424863
|
||||
# signkeyset.expire = +604800
|
||||
|
||||
# jdnssec-signrrset
|
||||
|
||||
# signrrset.verify_signatures = false
|
||||
# signrrset.verify = false # same thing
|
||||
# signrrset.key_directory = .
|
||||
# signrrset.start = now
|
||||
# signrrset.inception = now # same thing
|
||||
# signrrset.expire = now+3600
|
||||
|
||||
# jdnssec-signzone
|
||||
|
||||
# signzone.verify_signatures = false
|
||||
# signzone.verify = false # same thing
|
||||
# signzone.use_nsec3 = false
|
||||
# signzone.nsec3 = false # same thing
|
||||
# signzone.use_opt_out = false
|
||||
# signzone.opt_out = false # same thing
|
||||
# signzone.verbose_signing = false
|
||||
# signzone.fully_sign_keyset = false
|
||||
# signzone.fully_sign = false # same thing
|
||||
# signzone.key_directory = .
|
||||
# signzone.keydir = . # same thing
|
||||
# signzone.start = now
|
||||
# signzone.inception = now
|
||||
# signzone.expire = now+3600
|
||||
# signzone.nsec3_salt = DEADBEEF
|
||||
# signzone.salt = DEADBEEF # same thing
|
||||
# signzone.nsec3_random_salt_length = 6
|
||||
# signzone.nsec3_salt_length = 6 # same thing
|
||||
# signzone.random_salt_length = 6 # same thing
|
||||
# signzone.nsec3_iterations = 0
|
||||
# signzone.iterations = 0 # same thing
|
||||
# signzone.digest_algorithm = 4
|
||||
# signzone.digest_id = 4 # same thing
|
||||
# signzone.nsec3param_ttl = 86400
|
||||
# signzone.include_names_file = /path/to/include-names
|
||||
# signzone.include_names = /path/to/include-names # same thing
|
||||
|
||||
# jdnssec-verifyzone
|
||||
|
||||
# verifyzone.ignore_time = false
|
||||
# verifyzone.ignore_duplicate_rrs = false
|
||||
# verifyzone.ignore_duplicates = false # same thing
|
||||
# verifyzone.start_fudge = 0
|
||||
# verifyzone.expire_fudge = 0
|
||||
# verifyzone.current_time = now
|
||||
|
||||
# jdnssec-zoneformat
|
||||
|
||||
# zoneformat.assign_nsec3_owners = false
|
||||
# zoneformat.assign_owners = false # same thing
|
@ -17,11 +17,11 @@
|
||||
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
@ -48,8 +48,19 @@ import com.verisignlabs.dnssec.security.DnsKeyAlgorithm;
|
||||
* subclass variant of the CLIState and call run().
|
||||
*/
|
||||
public abstract class CLBase {
|
||||
protected static Logger staticLog = Logger.getLogger(CLBase.class.getName());
|
||||
protected Logger log;
|
||||
protected Logger log = Logger.getLogger(this.getClass().toString());
|
||||
protected Options opts;
|
||||
protected String name;
|
||||
protected String usageStr;
|
||||
protected Properties props;
|
||||
protected CommandLine cli;
|
||||
|
||||
protected CLBase(String name, String usageStr) {
|
||||
this.name = name;
|
||||
this.usageStr = usageStr;
|
||||
|
||||
setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a very simple log formatter that simply outputs the log level and
|
||||
@ -70,253 +81,319 @@ public abstract class CLBase {
|
||||
}
|
||||
}
|
||||
|
||||
/** This is the base set of command line options provided to all subclasses. */
|
||||
private void setupCommonOptions() {
|
||||
// Set up the standard set of options that all jdnssec command line tools will
|
||||
// implement.
|
||||
|
||||
// boolean options
|
||||
opts.addOption("h", "help", false, "Print this message.");
|
||||
opts.addOption("m", "multiline", false,
|
||||
"Output DNS records using 'multiline' format");
|
||||
|
||||
opts.addOption(Option.builder("l").longOpt("log-level").argName("level").hasArg()
|
||||
.desc("set the logging level with either java.util.logging levels, or 0-6").build());
|
||||
opts.addOption(Option.builder("v").longOpt("verbose").desc(
|
||||
"set as verbose (log-level = fine)").build());
|
||||
|
||||
opts.addOption(Option.builder("c").longOpt("config").argName("file").hasArg()
|
||||
.desc("configuration file (format: java properties)").build());
|
||||
|
||||
opts.addOption(Option.builder("A").hasArg().argName("alias:original:mnemonic").longOpt("alg-alias")
|
||||
.desc("Define an alias for an algorithm").build());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a base class for command line parsing state. Subclasses should
|
||||
* override setupOptions and processOptions.
|
||||
* This is an overridable method for subclasses to add their own command line
|
||||
* options.
|
||||
*/
|
||||
public static class CLIStateBase {
|
||||
protected Options opts;
|
||||
protected String usageStr;
|
||||
protected abstract void setupOptions();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
/**
|
||||
* Initialize the command line options
|
||||
*/
|
||||
public void setup() {
|
||||
opts = new Options();
|
||||
setupCommonOptions();
|
||||
setupOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main method for parsing the command line arguments. Subclasses
|
||||
* generally override processOptions() rather than this method. This method
|
||||
* creates the parsing objects and processes the common options.
|
||||
*
|
||||
* @param args The command line arguments.
|
||||
*/
|
||||
public void parseCommandLine(String[] args) {
|
||||
String[] logLevelOptionKeys = { "log_level", "log-level" };
|
||||
String[] multilineOptionKeys = { "multiline" };
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
|
||||
try {
|
||||
cli = parser.parse(opts, args);
|
||||
} catch (UnrecognizedOptionException e) {
|
||||
fail("unknown option encountered: " + e.getMessage());
|
||||
} catch (AlreadySelectedException e) {
|
||||
fail("mutually exclusive options have been selected:\n " + e.getMessage());
|
||||
} catch (ParseException e) {
|
||||
fail("unable to parse command line: " + e);
|
||||
}
|
||||
|
||||
/** 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");
|
||||
|
||||
opts.addOption(Option.builder("v").longOpt("verbose").argName("level").hasArg().desc(
|
||||
"verbosity level -- 0: silence, 1: error, 2: warning, 3: info, 4/5: fine, 6: finest; default: 2 (warning)")
|
||||
.build());
|
||||
|
||||
opts.addOption(Option.builder("A").hasArg().argName("alias:original:mnemonic").longOpt("alg-alias")
|
||||
.desc("Define an alias for an algorithm").build());
|
||||
|
||||
setupOptions(opts);
|
||||
if (cli.hasOption('h')) {
|
||||
usage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
String loadedConfig = loadConfig(cli.getOptionValue('c'));
|
||||
|
||||
Logger rootLogger = Logger.getLogger("");
|
||||
|
||||
// we set log level with both --log-level and -v/--verbose.
|
||||
String logLevel = cliOption("log-level", logLevelOptionKeys, null);
|
||||
if (logLevel == null) {
|
||||
logLevel = cli.hasOption("v") ? "fine" : "warning";
|
||||
}
|
||||
setLogLevel(rootLogger, logLevel);
|
||||
|
||||
for (Handler h : rootLogger.getHandlers()) {
|
||||
h.setLevel(rootLogger.getLevel());
|
||||
h.setFormatter(new BareLogFormatter());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 parser = new DefaultParser();
|
||||
CommandLine cli = parser.parse(opts, args);
|
||||
if (loadedConfig != null) {
|
||||
log.info("Loaded config file: " + loadedConfig);
|
||||
}
|
||||
|
||||
if (cli.hasOption('h')) {
|
||||
usage();
|
||||
if (cliBooleanOption("m", multilineOptionKeys, false)) {
|
||||
org.xbill.DNS.Options.set("multiline");
|
||||
}
|
||||
|
||||
processAliasOptions();
|
||||
|
||||
processOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process additional tool-specific options. Subclasses generally override
|
||||
* this.
|
||||
*/
|
||||
protected abstract void processOptions();
|
||||
|
||||
/**
|
||||
* Load a configuration (java properties) file for jdnssec-tools. Returns
|
||||
* the path of the loaded file.
|
||||
*
|
||||
* @param configFile a given path to a config file. This will be considered
|
||||
* first.
|
||||
* @return The path of the file that was actually loaded, or null if no config
|
||||
* file was loaded.
|
||||
*/
|
||||
protected String loadConfig(String configFile) {
|
||||
// Do not load config files twice
|
||||
if (props != null) {
|
||||
return null;
|
||||
}
|
||||
props = new Properties();
|
||||
String[] configFiles = { configFile, "jdnssec-tools.properties", ".jdnssec-tools.properties",
|
||||
System.getProperty("user.home") + "/.jdnssec-tools.properties" };
|
||||
|
||||
File f = null;
|
||||
|
||||
for (String fname : configFiles) {
|
||||
if (fname == null) {
|
||||
continue;
|
||||
}
|
||||
f = new File(fname);
|
||||
if (!f.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger rootLogger = Logger.getLogger("");
|
||||
int value = parseInt(cli.getOptionValue('v'), -1);
|
||||
try (FileInputStream stream = new FileInputStream(f)) {
|
||||
props.load(stream);
|
||||
break; // load the first config file found in our list
|
||||
} catch (IOException e) {
|
||||
log.warning("Could not read config file " + f.getName() + ": " + e);
|
||||
}
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
if (f != null) {
|
||||
return f.getPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void fail(String errorMessage) {
|
||||
log.severe(errorMessage);
|
||||
System.exit(64);
|
||||
}
|
||||
/** 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, 120, usageStr, null, opts, HelpFormatter.DEFAULT_LEFT_PAD,
|
||||
HelpFormatter.DEFAULT_DESC_PAD, null);
|
||||
|
||||
out.flush();
|
||||
System.exit(0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the logging level based on a string value
|
||||
*
|
||||
* @param logger The logger to set -- usually the rootLogger
|
||||
* @param levelStr A level string that is either an integer from 0 to 6, or a
|
||||
* java.util.logging log level string (severe, warning, info,
|
||||
* fine, finer,
|
||||
* finest).
|
||||
*/
|
||||
private void setLogLevel(Logger logger, String levelStr) {
|
||||
Level level;
|
||||
int internalLogLevel = Utils.parseInt(levelStr, -1);
|
||||
if (internalLogLevel != -1) {
|
||||
switch (internalLogLevel) {
|
||||
case 0:
|
||||
rootLogger.setLevel(Level.OFF);
|
||||
level = Level.OFF;
|
||||
break;
|
||||
case 1:
|
||||
rootLogger.setLevel(Level.SEVERE);
|
||||
level = Level.SEVERE;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
rootLogger.setLevel(Level.WARNING);
|
||||
level = Level.WARNING;
|
||||
break;
|
||||
case 3:
|
||||
rootLogger.setLevel(Level.INFO);
|
||||
level = Level.INFO;
|
||||
break;
|
||||
case 4:
|
||||
rootLogger.setLevel(Level.CONFIG);
|
||||
level = Level.FINE;
|
||||
break;
|
||||
case 5:
|
||||
rootLogger.setLevel(Level.FINE);
|
||||
break;
|
||||
case 6:
|
||||
rootLogger.setLevel(Level.ALL);
|
||||
break;
|
||||
level = Level.ALL;
|
||||
}
|
||||
|
||||
// I hate java.util.logging, btw.
|
||||
for (Handler h : rootLogger.getHandlers()) {
|
||||
h.setLevel(rootLogger.getLevel());
|
||||
h.setFormatter(new BareLogFormatter());
|
||||
} else {
|
||||
try {
|
||||
level = Level.parse(levelStr.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println("Verbosity level '" + levelStr + "' not recognized");
|
||||
level = Level.WARNING;
|
||||
}
|
||||
}
|
||||
logger.setLevel(level);
|
||||
}
|
||||
|
||||
if (cli.hasOption('m')) {
|
||||
org.xbill.DNS.Options.set("multiline");
|
||||
}
|
||||
/**
|
||||
* Process both property file based alias definitions and command line alias
|
||||
* definitions
|
||||
*/
|
||||
protected void processAliasOptions() {
|
||||
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
||||
// First parse any command line options
|
||||
// those look like '-A <alias-num>:<orig-num>:<mnemonic>', e.g., '-A
|
||||
// 21:13:ECDSAP256-NSEC6'
|
||||
String[] optstrs = null;
|
||||
if ((optstrs = cli.getOptionValues('A')) != null) {
|
||||
for (String value : optstrs) {
|
||||
String[] valueComponents = value.split(":");
|
||||
int aliasAlg = Utils.parseInt(valueComponents[0], -1);
|
||||
int origAlg = Utils.parseInt(valueComponents[1], -1);
|
||||
String mnemonic = valueComponents[2];
|
||||
|
||||
String[] optstrs = null;
|
||||
if ((optstrs = cli.getOptionValues('A')) != null) {
|
||||
for (int i = 0; i < optstrs.length; i++) {
|
||||
addArgAlias(optstrs[i]);
|
||||
if (mnemonic != null && origAlg >= 0 && aliasAlg >= 0) {
|
||||
algs.addAlias(aliasAlg, mnemonic, origAlg);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
// Next see if we have any alias options in properties
|
||||
// Those look like 'signzone.alias.<alias-mnemonic> =
|
||||
// <orig-alg-num>:<alias-alg-num>'
|
||||
for (String key : props.stringPropertyNames()) {
|
||||
if (key.startsWith(name + ".alias.") || key.startsWith("alias.")) {
|
||||
String[] keyComponents = key.split("\\.");
|
||||
String mnemonic = keyComponents[keyComponents.length - 1];
|
||||
String[] valueComponents = props.getProperty(key).split(":");
|
||||
int origAlg = Utils.parseInt(valueComponents[0], -1);
|
||||
int aliasAlg = Utils.parseInt(valueComponents[1], -1);
|
||||
|
||||
/** 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 {
|
||||
return Integer.parseInt(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public static long parseLong(String s, long def) {
|
||||
try {
|
||||
return Long.parseLong(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return def;
|
||||
if (mnemonic != null && origAlg >= 0 && aliasAlg >= 0) {
|
||||
algs.addAlias(aliasAlg, mnemonic, origAlg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a date/time from a command line time/offset duration string.
|
||||
* Given a parsed command line, option, and list of possible config
|
||||
* properties, and a default value, determine value for the option
|
||||
*
|
||||
* @param start
|
||||
* the start time to calculate offsets from.
|
||||
* @param duration
|
||||
* the time/offset string to parse.
|
||||
* @return the calculated time.
|
||||
* @param option The option name
|
||||
* @param properties A list of configuration parameters that we would like
|
||||
* to use for this option, from most preferred to least.
|
||||
* @param defaultValue A default value to return if either the option or
|
||||
* config value cannot be parsed, or neither are present.
|
||||
* @return The found value, or the default value.
|
||||
*/
|
||||
public static Instant convertDuration(Instant start, String duration) throws ParseException {
|
||||
if (start == null) {
|
||||
start = Instant.now();
|
||||
protected String cliOption(String option, String[] properties, String defaultValue) {
|
||||
if (cli.hasOption(option)) {
|
||||
return cli.getOptionValue(option);
|
||||
}
|
||||
|
||||
if (duration.startsWith("now")) {
|
||||
start = Instant.now();
|
||||
if (duration.indexOf("+") < 0)
|
||||
return start;
|
||||
|
||||
duration = duration.substring(3);
|
||||
for (String property : properties) {
|
||||
// first look up the scoped version of the property
|
||||
String value = props.getProperty(name + "." + property);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
value = props.getProperty(property);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (duration.startsWith("+")) {
|
||||
long offset = parseLong(duration.substring(1), 0);
|
||||
return start.plusSeconds(offset);
|
||||
}
|
||||
/**
|
||||
* Given a parsed command line, option, and list of possible config
|
||||
* properties, determine the value for the option, converting the value to
|
||||
* long.
|
||||
*/
|
||||
protected long cliLongOption(String option, String[] properties, long defaultValue) {
|
||||
String value = cliOption(option, properties, Long.toString(defaultValue));
|
||||
return Utils.parseLong(value, defaultValue);
|
||||
}
|
||||
|
||||
// This is a heuristic to distinguish UNIX epoch times from the zone file
|
||||
// format standard (which is length == 14)
|
||||
if (duration.length() <= 10) {
|
||||
long epoch = parseLong(duration, 0);
|
||||
return Instant.ofEpochSecond(epoch);
|
||||
}
|
||||
/**
|
||||
* Given a parsed command line, option, and list of possible config
|
||||
* properties, determine the value for the option, converting the value to
|
||||
* int.
|
||||
*/
|
||||
protected int cliIntOption(String option, String[] properties, int defaultValue) {
|
||||
String value = cliOption(option, properties, Integer.toString(defaultValue));
|
||||
return Utils.parseInt(value, defaultValue);
|
||||
}
|
||||
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
try {
|
||||
Date parsedDate = dateFormatter.parse(duration);
|
||||
return parsedDate.toInstant();
|
||||
} catch (java.text.ParseException e) {
|
||||
throw new ParseException(e.getMessage());
|
||||
/**
|
||||
* Given a parsed command line, option, and list of possible config
|
||||
* properties, determine the value for the option, converting the value to
|
||||
* a boolean.
|
||||
*/
|
||||
protected boolean cliBooleanOption(String option, String[] properties, boolean defaultValue) {
|
||||
if (cli.hasOption(option)) {
|
||||
return true;
|
||||
}
|
||||
String value = cliOption(option, properties, Boolean.toString(defaultValue));
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
public void run(String[] args) {
|
||||
|
||||
parseCommandLine(args);
|
||||
log = Logger.getLogger(this.getClass().toString());
|
||||
|
||||
try {
|
||||
|
84
src/main/java/com/verisignlabs/dnssec/cl/CLI.java
Normal file
84
src/main/java/com/verisignlabs/dnssec/cl/CLI.java
Normal file
@ -0,0 +1,84 @@
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
public class CLI {
|
||||
private SubCommandType subCommand = null;
|
||||
private String commandSetStr = null;
|
||||
|
||||
enum SubCommandType {
|
||||
DSTOOL, KEYGEN, KEYINFO, SIGNKEYSET, SIGNRRSET, SIGNZONE, VERIFYZONE, ZONEFORMAT;
|
||||
}
|
||||
|
||||
public CLI(String name, String usageStr) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (SubCommandType type : SubCommandType.class.getEnumConstants()) {
|
||||
sb.append(type.toString().toLowerCase());
|
||||
sb.append(" ");
|
||||
}
|
||||
commandSetStr = sb.toString().trim();
|
||||
}
|
||||
|
||||
private void fail(String errorMessage){
|
||||
System.err.println("ERROR: " + errorMessage);
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
public void run(String[] args) {
|
||||
String[] subCommandArgs = null;
|
||||
if (args.length < 1) {
|
||||
fail("missing command: must be one of: " + commandSetStr);
|
||||
}
|
||||
|
||||
String command = args[0];
|
||||
if (command.equals("-h")) {
|
||||
System.err.println("usage: jdnssec-tools <command> <command args..>");
|
||||
System.err.println(" <command> is one of: " + commandSetStr);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
try {
|
||||
subCommand = SubCommandType.valueOf(command.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
fail("unrecognized command '" + command + "': must be one of: " + commandSetStr);
|
||||
}
|
||||
subCommandArgs = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, subCommandArgs, 0, args.length - 1);
|
||||
|
||||
CLBase cmd = null;
|
||||
|
||||
switch(subCommand) {
|
||||
case DSTOOL:
|
||||
cmd = new DSTool("dstool", "jdnssec-tools dstool [..options..] keyfile [keyfile..]");
|
||||
break;
|
||||
case KEYGEN:
|
||||
cmd = new KeyGen("keygen", "jdnssec-tools keygen [..options..] zonename");
|
||||
break;
|
||||
case KEYINFO:
|
||||
cmd = new KeyInfoTool("keyinfotool", "jdnssec-tools keyinfo [..options..] keyfile");
|
||||
break;
|
||||
case SIGNKEYSET:
|
||||
cmd = new SignKeyset("signkeyset", "jdnssec-tools signkeyset [..options..] dnskeyset_file [key_file ...]");
|
||||
break;
|
||||
case SIGNRRSET:
|
||||
cmd = new SignRRset("signrrset", "jdnssec-tools signrrset [..options..] rrset_file key_file [key_file ...]");
|
||||
break;
|
||||
case SIGNZONE:
|
||||
cmd = new SignZone("signzone", "jdnssec-tools signzone [..options..] zone_file [key_file ...]");
|
||||
break;
|
||||
case VERIFYZONE:
|
||||
cmd = new VerifyZone("verifyzone", "jdnssec-tools verifyzone [..options..] zonefile");
|
||||
break;
|
||||
case ZONEFORMAT:
|
||||
cmd = new ZoneFormat("zoneformat", "jdnssec-tools zoneformat [..options..] zonefile");
|
||||
break;
|
||||
default:
|
||||
fail("commmand " + command + " has not been implemented.");
|
||||
break;
|
||||
}
|
||||
cmd.run(subCommandArgs);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CLI cli = new CLI("cli", "jdnssec-tools <command> [..args..]");
|
||||
cli.run(args);
|
||||
}
|
||||
}
|
@ -18,11 +18,10 @@
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.xbill.DNS.CDSRecord;
|
||||
import org.xbill.DNS.DLVRecord;
|
||||
import org.xbill.DNS.DNSKEYRecord;
|
||||
@ -40,7 +39,15 @@ import com.verisignlabs.dnssec.security.SignUtils;
|
||||
* @author David Blacka
|
||||
*/
|
||||
public class DSTool extends CLBase {
|
||||
private CLIState state;
|
||||
private dsType createType = dsType.DS;
|
||||
private String outputfile = null;
|
||||
private String[] keynames = null;
|
||||
private int digestId = DNSSEC.Digest.SHA256;
|
||||
private long dsTTL = -1;
|
||||
|
||||
public DSTool(String name, String usageStr) {
|
||||
super(name, usageStr);
|
||||
}
|
||||
|
||||
/** There are several records that are based on DS. */
|
||||
protected enum dsType {
|
||||
@ -52,80 +59,77 @@ public class DSTool extends CLBase {
|
||||
* state.
|
||||
*/
|
||||
|
||||
protected static class CLIState extends CLIStateBase {
|
||||
public dsType createType = dsType.DS;
|
||||
public String outputfile = null;
|
||||
public String keyname = null;
|
||||
public int digestId = DNSSEC.Digest.SHA256;
|
||||
|
||||
public CLIState() {
|
||||
super("jdnssec-dstool [..options..] keyfile");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*
|
||||
* @return a set of command line options.
|
||||
*/
|
||||
@Override
|
||||
protected void setupOptions(Options opts) {
|
||||
opts.addOption(Option.builder("D").longOpt("dlv").desc("Generate a DLV record instead.").build());
|
||||
opts.addOption(Option.builder("C").longOpt("cds").desc("Generate a CDS record instead").build());
|
||||
opts.addOption(
|
||||
Option.builder("d").hasArg().argName("id").longOpt("digest").desc("The digest algorithm to use").build());
|
||||
opts.addOption(Option.builder("f").hasArg().argName("file").longOpt("output").desc("output to file").build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOptions(CommandLine cli)
|
||||
throws org.apache.commons.cli.ParseException {
|
||||
outputfile = cli.getOptionValue('f');
|
||||
if (cli.hasOption("dlv")) {
|
||||
createType = dsType.DLV;
|
||||
} else if (cli.hasOption("cds")) {
|
||||
createType = dsType.CDS;
|
||||
}
|
||||
String optstr = cli.getOptionValue('d');
|
||||
if (optstr != null)
|
||||
digestId = DNSSEC.Digest.value(optstr);
|
||||
|
||||
String[] args = cli.getArgs();
|
||||
|
||||
if (args.length < 1) {
|
||||
System.err.println("error: missing key file ");
|
||||
usage();
|
||||
}
|
||||
|
||||
keyname = args[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*
|
||||
* @return a set of command line options.
|
||||
*/
|
||||
protected void setupOptions() {
|
||||
opts.addOption(Option.builder("D").longOpt("dlv").desc("Generate a DLV record instead.").build());
|
||||
opts.addOption(Option.builder("C").longOpt("cds").desc("Generate a CDS record instead").build());
|
||||
opts.addOption(
|
||||
Option.builder("d").hasArg().argName("id").longOpt("digest").desc("The digest algorithm to use").build());
|
||||
opts.addOption(Option.builder("f").hasArg().argName("file").longOpt("output").desc("output to file").build());
|
||||
opts.addOption(Option.builder("T").longOpt("ttl").hasArg().desc("TTL to use for generated DS/CDS record").build());
|
||||
}
|
||||
|
||||
public void execute() throws Exception {
|
||||
DnsKeyPair key = BINDKeyUtils.loadKey(state.keyname, null);
|
||||
protected void processOptions() {
|
||||
String[] digestAlgOptionKeys = { "digest_algorithm", "digest_id" };
|
||||
String[] dsTTLOptionKeys = { "ds_ttl", "ttl" };
|
||||
|
||||
outputfile = cli.getOptionValue('f');
|
||||
if (cli.hasOption("dlv")) {
|
||||
createType = dsType.DLV;
|
||||
} else if (cli.hasOption("cds")) {
|
||||
createType = dsType.CDS;
|
||||
}
|
||||
String digestValue = cliOption("d", digestAlgOptionKeys, Integer.toString(digestId));
|
||||
digestId = DNSSEC.Digest.value(digestValue);
|
||||
|
||||
dsTTL = cliLongOption("ttl", dsTTLOptionKeys, dsTTL);
|
||||
|
||||
String[] args = cli.getArgs();
|
||||
|
||||
if (args.length < 1) {
|
||||
fail("missing key file");
|
||||
}
|
||||
|
||||
keynames = args;
|
||||
}
|
||||
|
||||
public void createDS(String keyname) throws IOException {
|
||||
DnsKeyPair key = BINDKeyUtils.loadKey(keyname, null);
|
||||
DNSKEYRecord dnskey = key.getDNSKEYRecord();
|
||||
|
||||
if ((dnskey.getFlags() & DNSKEYRecord.Flags.SEP_KEY) == 0) {
|
||||
log.warning("DNSKEY is not an SEP-flagged key.");
|
||||
log.warning("DNSKEY " + keyname + " is not an SEP-flagged key.");
|
||||
}
|
||||
|
||||
DSRecord ds = SignUtils.calculateDSRecord(dnskey, state.digestId, dnskey.getTTL());
|
||||
Record res = ds;
|
||||
long ttl = dsTTL < 0 ? dnskey.getTTL() : dsTTL;
|
||||
DSRecord ds = SignUtils.calculateDSRecord(dnskey, digestId, ttl);
|
||||
Record res;
|
||||
|
||||
if (state.createType == dsType.DLV) {
|
||||
log.fine("creating DLV.");
|
||||
DLVRecord dlv = new DLVRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(),
|
||||
ds.getDigestID(), ds.getDigest());
|
||||
res = dlv;
|
||||
} else if (state.createType == dsType.CDS) {
|
||||
log.fine("creating CDS.");
|
||||
CDSRecord cds = new CDSRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(),
|
||||
ds.getDClass(), ds.getDigest());
|
||||
res = cds;
|
||||
switch (createType) {
|
||||