Zonemaster-CLI-v7.2.0/ 000755 001754 001754 00000000000 14761573760 014754 5 ustar 00matsd matsd 000000 000000 Zonemaster-CLI-v7.2.0/inc/ 000755 001754 001754 00000000000 14761573760 015525 5 ustar 00matsd matsd 000000 000000 Zonemaster-CLI-v7.2.0/t/ 000755 001754 001754 00000000000 14761573760 015217 5 ustar 00matsd matsd 000000 000000 Zonemaster-CLI-v7.2.0/share/ 000755 001754 001754 00000000000 14761573760 016056 5 ustar 00matsd matsd 000000 000000 Zonemaster-CLI-v7.2.0/CONTRIBUTING.md 000644 001754 001754 00000002720 14724074026 017174 0 ustar 00matsd matsd 000000 000000 # Contributing to Zonemaster::CLI
Contribution to this repository is welcome. Contribution can be either an issue
report or a code or a documentation update. Also see the information in the
[main README][Zonemaster/Zonemaster README] in the main Zonemaster respository.
## Issue
First search for a similar issue in the [issues list]. If a relevant issue is
found, add your information as a comment. If no relevant issue is found, create
[a new issue][create issue]. Give as many details as you have and describe, if
possible, how the issue can be reproduced.
## Pull request
If you would like to contribute an update, first please look for issues and open
[pull requests] that are about the same thing. If nothing relevant is found or
you have a different solution, create [a new pull request][create pull request].
Creating a pull request assumes that you have your proposal in a fork repository.
When you create a pull request, please always start with the `develop` branch
and create the pull request against the same branch.
[issues list]: https://github.com/zonemaster/zonemaster-cli/issues
[create issue]: https://github.com/zonemaster/zonemaster-cli/issues/new
[pull requests]: https://github.com/zonemaster/zonemaster-cli/pulls
[create pull request]: https://github.com/zonemaster/zonemaster-cli/compare
[Zonemaster/Zonemaster README]: https://github.com/zonemaster/zonemaster#readme
Zonemaster-CLI-v7.2.0/LICENSE 000644 001754 001754 00000003561 14724074026 015754 0 ustar 00matsd matsd 000000 000000 ### Code license
Copyright (c) The Swedish Internet Foundation ()
Copyright (c) AFNIC ()
All rights reserved.
Copyright belongs to external contributor where applicable.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
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.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR 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.
### Documentation license
Copyright (c) The Swedish Internet Foundation ()
Copyright (c) AFNIC ()
All rights reserved.
Copyright belongs to external contributor where applicable.
Creative Commons Attribution 4.0 International License
You should have received a copy of the license along with this
work. If not, see .
Zonemaster-CLI-v7.2.0/Changes 000644 001754 001754 00000026335 14761573655 016263 0 ustar 00matsd matsd 000000 000000 Release history for Zonemaster component Zonemaster-CLI
v7.2.0 2025-03-04 (part of Zonemaster v2024.2.1 release)
[Release information]
- Translations have not been fully updated in this release. They will
be updated in an upcoming extra release.
[Features]
- Adds translation to Slovenian language (#384)
[Fixes]
- Updates translations (#418, #425, #415, #422, #414)
- Minor code cleanup (#420)
- Fixes to make early messages not to be lost (#416)
v7.1.0 2024-12-09 (part of Zonemaster v2024.2 release)
[Release information]
- Translations have not been fully updated in this release. They will
be updated in an upcoming extra release.
[Features]
- Corrects display when running 'zonemaster-cli' with '--ns' or '--ds'
(#382)
- Allows explicitly setting '--stop-level' to empty string (#395)
[Fixes]
- Provides a useful link in man page (#401)
- Write “AS” in uppercase in Dockerfile (#400)
- Corrects gettext code (#398)
- Organizes "zonemaster-cli --help" text (#389)
- Make "zonemaster-cli" usage documentation consistent and also remove
dependency on the Moose library (#371)
- Fixes the handling of invalid --hints argument (#386)
- Moves the "zonemaster-info" utility from this repository (#385)
- Clean-up exit statuses (#381)
v7.0.0 2024-07-01 (part of Zonemaster v2024.1 release)
[Release information]
- Translations have not been fully updated in this release. They will
be updated in an upcoming release.
[Breaking change]
- Removes deprecated '--sourceaddr' option. Breaks custom 'cli.args'
using this option. Use '--sourceaddr4' and '--sourceaddr6'
instead. (#370)
[Fixes]
- Fixes command line argument handling of domain names (#365)
- Speeds up Docker build (#374)
v6.1.0 2024-03-18 (public release version)
[Features]
- Extends "--test" option to allow passing of test case only (#333)
- Updates "--list_tests" option (#354)
- Adds input name normalization (#357)
[Fixes]
- Fixes the "--raw" output (#360)
v6.0.3 2023-09-08 (public fix version)
[Fixes]
- Fixes a spelling error (text) in the zonemaster-cli script (#351).
- This version contains no real code changes, but it requires a higher
(fixed) version of Zonemaster-Engine.
v6.0.2 2023-08-07 (public fix version)
[Fixes]
- This version contains no real changes. It has been created to
require a higher (fixed) version of Zonemaster-Engine.
v6.0.1 2023-07-24 (public fix version)
[Fixes]
- Updates the translation to Norwegian (#342)
v6.0.0 2023-06-21 (public release version)
[Breaking changes]
- Adds display of Zonemaster::LDNS and libldns versions
when --version has been selected (#306)
[Features]
- Updates various report options to zonemaster-cli script
(#318, #337, #309, #308)
- Adds options "--sourceaddr4" and "--sourceaddr6" to
zonemaster-cli script (#317)
[Fixes]
- Updates translations (#338, #328, #329, #331, #327, #322)
- Updates documentation in zonemaster-cli script (#336)
- Fixes table layout in zonemaster-cli output (#335)
- Removes documentation moved to the zonemaster/zonemaster
repository (#332)
- Removes some untranslatable characters from msgids (#307)
v5.0.2 2023-03-01 (public fix version)
[Fixes]
- Updates translations (#303)
v5.0.1 2023-01-31 (public fix version)
[Fixes]
- Updates translations (#298, #297)
v5.0.0 2022-12-19 (public release version)
[Breaking changes]
- Makes zonemaster-cli fail if there are multiple domain
names in entry (#287, #286)
[Features]
- Adds --hints option (#284, #293, #292)
[Fixes]
- Updates installation instruction (#291, #289)
- Corrects the license statement in CLI.pm (#288)
- Sets lowercase fragments to refer to internal headings in
markdown documents (#282)
- Cleans-up unused import (#283)
- Adds Mac note on USING.md when it comes to Docker (#281)
v4.0.1 2022-07-08 (public fix version)
[Fixes]
- Adds missing installation instructions for CentOS Linux 7
(#275)
- Updates translation to Danish (#271)
v4.0.0 2022-06-09 (public release version)
[Breaking changes]
- Updates how trailing dots of domain name or name
server name is handled. Trailing double dot is now
always an error. (#253)
[Features]
- Uses pre-built packages for ubuntu (#268)
- Adds support for global configuration file (#260, #252)
[Fixes]
- Updates translation (#263, #267, #266, #265, #269, #264, #261)
- Fixes a bug where a trailing dot on name server name
gives a crash (#253)
- Fixes warning message (#255)
- Uses libidn2 instead of libidn (#254)
- Updates documentation for users (#241, #244)
- Updates installation document (#243)
v3.2.0 2021-12-20 (public fix version)
[Features]
- Adds translation to Spanish language (#219)
[Fixes]
- Clarifies IPv6 limitations in Docker (#238)
- Updates Danish translation (#236, #233)
- Updates French translation (#237, #225)
v3.1.1 2021-12-03 (public release version)
[Features]
- Deb packages are available for Debian (#233)
- Adds support for Docker (#221, #218, #216)
- Replaces CentOS with Rocky Linux (#220)
[Fixes]
- Updates Finnish translation (#230, #224)
- Updates Norwegian translation (#228, #226)
- Updates Swedish translation (#227)
- Updates dependencies (#217)
- Improves DEBUG3 output (#215, #111)
- Improves output when option is incorrect (#214, #160)
- Clean-up removing unused options (#213, #145)
- More user-friendly options (#212, #159)
v3.1.0 2021-05-28 (public release version)
[Features]
- Adds Finnish translation (PO file) (#192, #191)
[Fixes]
- Updates the installation instructions (#193, #191)
- Updates translations (#204, #201, #205, #200,
#207, #202)
v3.0.4 2021-02-10 (public release version)
[Features]
- Updates --raw mode output in debug level (#113)
[Fixes]
- Updates the installation instructions (#188)
- Prevents PO files to be updated unintentionally (#186)
- Fixes how command line options of profile and IP
interact (#185, #183, #182)
- Makes --sourceaddr behavior more reasonable when
selected address is not reasonable (#110, #38)
v3.0.3 (never released)
v3.0.2 (never released)
v3.0.1 2020-11-09 (public release version)
[Fixes]
- Fixed an version specification error in Makefile.PL
(#178)
v3.0.0 2020-11-06 (public release version)
[Breaking changes]
- Added "--show_testcase" and changed format when "--raw"
is selected (#157)
[Features]
- Added Norwegian language support to match added
translation to Norwegian in Zonemaster-Engine (#164, #155,
#161)
[Fixes]
- Updated/corrected translations/PO files (#174, #173)
- Corrected documentation (#172)
- Rename share/Makefile to share/GNUmakefile and
create wrapper for FreeBSD. Make FreeBSD use gmake
for share/GNUmakefile (#169)
- Corrected MANIFEST (#168)
- Updated share/Makefile (#167)
- Clean-up (#166, #158)
- Correcting --nstimes option (#163, #148)
- Updated translation/PO files handling (#151, #132, #147,
#150, #149)
v2.0.4 2020-05-22
[Fixes]
- Bumping the version of Zonemaster::CLI to be able to
upload a new package to CPAN to avoid the confusion
that the version v2.0.3.1 created. There are no
changes compared to v2.0.3.
v2.0.3.1 2020-05-15
[Fixes]
- This release fixes inconsitency between the branches in the
repository. See the release notes for v2.0.3 for the real
changes.
- There is no change in data so this release has not been
published on CPAN.
v2.0.3 2020-04-30
[Features]
- (none)
[Fixes]
- Update installation instructions (#119, #117, #137)
- Translation into Danish (#109, #61)
v2.0.2 2019-05-31 (public fix version)
[Fixes]
- Corrects in Makefile.PL the versions of Zonemaster::Engine
and Zonemaster::LDNS that this version of Zonemaster::CLI
depends on (#105)
v2.0.1 2019-05-22 (public release version)
[Status]
- This a public release fully tested before release. This version
will be available on CPAN.
[Fixes]
- Updated installation instructions for FreeBSD (#101)
- Dropped support for Ubuntu 14.04 (#99)
- Made Travis use the equivalent branch in Zonemaster-Engine (#98)
v2.0.0 2019-01-25 (pre-release version)
- Status
- This is a pre-release version not fully tested on all supported
OS's and Perl versions. This version will not be available on
CPAN.
- API change
- Replaced separate config and policy with a unified profile
(see below)
- Some changes in printout that can affect scripts using
zonemaster-cli (see below)
- Features
- Changes in printout
- Print information on STDERR if --json has been selected #84
- Diagnostics #85
- Only show spinner in human readable output mode #86
- Make --level and --stop_level case-insensitive #87
- Replaced separate config and policy with a unified profile
- Removed config and policy and added profile #70, #93
- Fixes
- All link references on Github now to zonemaster/zonemaster instead
of old dotse/zonemaster #81
- Clarify documentation of --progress option #88
- Travis tests against develop branch of Zonemaster-Engine instead
of fetching from CPAN #92
- Update travis configuration when it comes to Perl versions. #95
v1.1.3 2018-06-25
Fixed:
- Move license from Makefile.PL to main module (#67)
- Initialize gettext according to gettext documentation (#71), which
solves issues with translations in Linux (#46) and FreeBSD (#64)
- Updated the install instructions for debian and centos (#75)
- Update Installation.md for FreeBSD (uses cpan instead of cpanm) (#78)
v1.1.2 2018-01-12
Natural Language support:
- Adding support for Danish translation. (#62)
Fixed:
- Fixes issues with pre-delegation testing ("fake delegation") (#63)
v1.1.1 2017-11-02 Public release
Fixed:
- Update licensing (#58)
- Specify smallest version of Locale::TextDomain i Makefile.PL. (#57)
- Updated installation instruction (#53)
- Changed dependency from Net::LDNS to Zonemaster::LDNS due to
name space change (#51)
- Various updates to packaging (#52)
- Fix Commonmark rendering on Github. Replace NBSP with SPACE. (#49)
- Changed dependency from Zonemaster to Zonemaster::Engine due
to name space change (#43)
v1.1.0 2017-04-04 Public pre-release
- This release will not be published on CPAN since it contains updates that have
not been fully tested. Do not update production systems without verification.
- Update install instructions (#34)
- Fixes packaging issue with version numbering (#35)
- Solved --sourceaddr is not correctly implemented (#36)
- Updates so that "fake delegation" is tested correctly (#42)
1.0.5 2016-10-14
- Correcting version in CLI.pm. Update missing in 1.0.4.
1.0.4 2016-10-14
- Updating README.md and USING.md
- Better way to check ExtUtils::MakeMaker version
- Introduced MANIFEST.SKIP
1.0.3 2015-12-22
- Added JSON streaming
- Changed all instances from .SE to IIS
1.0.2 2015-06-25
- Allow lookup of nameservers for undelegated tests
- Net::LDNS::to_idn() takes Perl characters, not octets with utf-8 data
- Fixed display bug when using --nstimes with --no-ipv6 or --no-ipv4
1.0.1 2015-04-07
- State clearly that a given name is not a domain when it is not a domain
- Send "Net::LDNS not compiled with libidn" to STDERR, not STDOUT
1.0.0 2014-12-11 Release version
1.000000 2014-12-11 Public beta release.
0.001002 2014-11-26
0.001001 2014-11-19
0.001000 2014-11-17
0.03 2014-10-30
0.01 2014-05-25
- initial CPAN release
Zonemaster-CLI-v7.2.0/MANIFEST 000644 001754 001754 00000001666 14761573655 016121 0 ustar 00matsd matsd 000000 000000 Changes
CONTRIBUTING.md
inc/Module/Install.pm
inc/Module/Install/Base.pm
inc/Module/Install/Can.pm
inc/Module/Install/External.pm
inc/Module/Install/Fetch.pm
inc/Module/Install/Makefile.pm
inc/Module/Install/Metadata.pm
inc/Module/Install/Scripts.pm
inc/Module/Install/Share.pm
inc/Module/Install/Win32.pm
inc/Module/Install/WriteAll.pm
lib/Zonemaster/CLI.pm
LICENSE
Makefile.PL
MANIFEST This list of files
META.yml
README.md
script/zonemaster-cli
share/GNUmakefile
share/Makefile
share/locale/da/LC_MESSAGES/Zonemaster-CLI.mo
share/locale/es/LC_MESSAGES/Zonemaster-CLI.mo
share/locale/fi/LC_MESSAGES/Zonemaster-CLI.mo
share/locale/fr/LC_MESSAGES/Zonemaster-CLI.mo
share/locale/nb/LC_MESSAGES/Zonemaster-CLI.mo
share/locale/sl/LC_MESSAGES/Zonemaster-CLI.mo
share/locale/sv/LC_MESSAGES/Zonemaster-CLI.mo
t/00-load.t
t/pod.t
t/usage.fake-data.data
t/usage.fake-root.data
t/usage.hints
t/usage.normal.data
t/usage.profile
t/usage.t
t/usage.wrapper.pl
Zonemaster-CLI-v7.2.0/Makefile.PL 000644 001754 001754 00000003356 14761573655 016740 0 ustar 00matsd matsd 000000 000000 use 5.014002;
use strict;
use warnings FATAL => 'all';
use inc::Module::Install;
use ExtUtils::MakeMaker ();
name 'Zonemaster-CLI';
all_from 'lib/Zonemaster/CLI.pm';
resources(
repository => 'https://github.com/zonemaster/zonemaster-cli',
bugtracker => 'https://github.com/zonemaster/zonemaster-cli/issues',
);
tests_recursive( 't' );
# "2.1.0" could be declared as "2.001" but not as "2.1"
# (see Zonemaster::LDNS below)
requires(
'Readonly' => 0,
'Net::IP::XS' => 0,
'JSON::XS' => 0,
'Locale::TextDomain' => 1.23,
'Try::Tiny' => 0,
'Zonemaster::LDNS' => 4.001000, # v4.1.0
'Zonemaster::Engine' => 7.001000, # v7.1.0
);
test_requires(
'JSON::Validator' => 0,
'Test::Differences' => 0,
);
# Make all platforms include inc/Module/Install/External.pm
requires_external_bin 'find';
if ($^O eq "freebsd") {
requires_external_bin 'gmake';
};
sub MY::postamble {
my $pure_all;
my $sharemakefile = 'share/GNUmakefile';
if ($^O eq "freebsd") {
# Make FreeBSD use gmake for share
$pure_all = "GMAKE ?= \"gmake\"\n"
. "pure_all :: $sharemakefile\n"
. "\tcd share && \$(GMAKE) all\n";
} else {
# Here Linux and GNU Make is assumed
$pure_all = "pure_all :: $sharemakefile\n"
. "\tcd share && \$(MAKE) all\n";
};
my $docker = <<'END_DOCKER';
docker-build:
docker build --tag zonemaster/cli:local --build-arg version=$(VERSION) .
docker-tag-version:
docker tag zonemaster/cli:local zonemaster/cli:$(VERSION)
docker-tag-latest:
docker tag zonemaster/cli:local zonemaster/cli:latest
END_DOCKER
return $pure_all . $docker;
};
install_script 'zonemaster-cli';
install_share;
WriteAll;
Zonemaster-CLI-v7.2.0/README.md 000644 001754 001754 00000004647 14726012174 016232 0 ustar 00matsd matsd 000000 000000 # Zonemaster-CLI
## Purpose
This Git repository is one of the components of the Zonemaster software and
contains the source for the Zonemaster-CLI utility.
For an overview of the Zonemaster software, please see the
[Zonemaster repository].
## Prerequisite
Before you install the Zonemaster-CLI utility, you need the Zonemaster-Engine
test framework installed. Please see the [Zonemaster Engine installation
instructions][Zonemaster-Engine installation].
## Installation
For installation, see the [installation] document.
## Configuration
This repository does not need any specific configuration.
## Docker
Zonemaster-CLI is available on [Docker Hub], and can be conveniently downloaded
and run without any installation. See [USING] Zonemaster-CLI for how to run
Zonemaster-CLI on Docker.
To build your own Docker image, see the [Docker Image Creation] documentation.
## Documentation
Run `zonemaster-cli --help` to get brief descriptions of a selection of the most
important command line options.
For complete reference documentation, see the manual page by running `man
zonemaster-cli`.
Additional end-user documentation is available in the [USING] document.
When developing Zonemaster-CLI, refer to the [development documentation].
## Participation, Contact and Bug reporting
For participation, contact and bug reporting, please see the main
[Zonemaster README].
## License
This is free software under a 2-clause BSD license. The full text of the license can
be found in the [LICENSE](LICENSE) file included in this respository.
[Development documentation]: https://github.com/zonemaster/zonemaster/blob/master/docs/public/development/cli.md
[Docker Image Creation]: https://github.com/zonemaster/zonemaster/blob/master/docs/internal/maintenance/ReleaseProcess-create-docker-image.md
[Docker Hub]: https://hub.docker.com/u/zonemaster
[Installation]: https://github.com/zonemaster/zonemaster/blob/master/docs/public/installation/zonemaster-cli.md
[USING]: https://github.com/zonemaster/zonemaster/blob/master/docs/public/using/cli.md
[Zonemaster-Engine installation]: https://github.com/zonemaster/zonemaster/blob/master/docs/public/installation/zonemaster-engine.md
[Zonemaster README]: https://github.com/zonemaster/zonemaster/blob/master/README.md
[Zonemaster repository]: https://github.com/zonemaster/zonemaster
Zonemaster-CLI-v7.2.0/lib/ 000755 001754 001754 00000000000 14761573760 015522 5 ustar 00matsd matsd 000000 000000 Zonemaster-CLI-v7.2.0/META.yml 000644 001754 001754 00000001610 14761573711 016217 0 ustar 00matsd matsd 000000 000000 ---
abstract: 'run Zonemaster tests from the command line'
author:
- 'Vincent Levigneron '
build_requires:
ExtUtils::MakeMaker: 6.59
JSON::Validator: 0
Test::Differences: 0
configure_requires:
ExtUtils::MakeMaker: 6.59
distribution_type: module
dynamic_config: 1
generated_by: 'Module::Install version 1.21'
license: bsd
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: 1.4
name: Zonemaster-CLI
no_index:
directory:
- inc
- share
- t
requires:
JSON::XS: 0
Locale::TextDomain: 1.23
Net::IP::XS: 0
Readonly: 0
Try::Tiny: 0
Zonemaster::Engine: 7.001
Zonemaster::LDNS: 4.001
perl: 5.14.2
resources:
bugtracker: https://github.com/zonemaster/zonemaster-cli/issues
license: http://opensource.org/licenses/bsd-license.php
repository: https://github.com/zonemaster/zonemaster-cli
version: '7.002000'
Zonemaster-CLI-v7.2.0/script/ 000755 001754 001754 00000000000 14761573760 016260 5 ustar 00matsd matsd 000000 000000 Zonemaster-CLI-v7.2.0/script/zonemaster-cli 000755 001754 001754 00000024464 14726012174 021140 0 ustar 00matsd matsd 000000 000000 #!/usr/bin/env perl
use 5.14.2;
use warnings;
use Zonemaster::CLI;
use File::Spec;
use autodie;
sub read_conf_file {
# Returns list of command line parameters. List can be empty.
my ($conf_file) = @_;
my @lines;
open my $fh, '<', $conf_file;
while (<$fh>) {
chomp;
next if /^\s*$/;
next if /^\s*#/;
push @lines, $_;
};
return @lines;
}
# Load default arguments from file in home directory, if any
# This must be loaded before any global file to make the local
# file take precedence
my $home_dir = ((getpwuid($<))[7]) || $ENV{HOME};
my $home_conf_file = File::Spec->catfile($home_dir, '.zonemaster', 'cli.args');
if (-r $home_conf_file) {
my @lines = read_conf_file ($home_conf_file);
unshift @ARGV, @lines;
}
# Load default arguments from global file, if any
my @global_conf = (
'/etc/zonemaster/cli.args',
'/usr/local/etc/zonemaster/cli.args'
); # Order is significant.
my $global_conf_file;
for my $p (@global_conf) {
if ( -e $p and -r $p ) {
$global_conf_file = $p;
last;
}
}
if ( defined $global_conf_file ) {
my @lines = read_conf_file ($global_conf_file);
unshift @ARGV, @lines;
}
eval {
my $exitstatus = Zonemaster::CLI->run( @ARGV );
exit $exitstatus;
};
print STDERR $@;
exit $Zonemaster::CLI::EXIT_GENERIC_ERROR;
=head1 NAME
zonemaster-cli - run Zonemaster tests from the command line
=head1 SYNOPSIS
zonemaster-cli [--help | --version | --list-tests]
zonemaster-cli [OPTIONS] --dump-profile
zonemaster-cli [OPTIONS] DOMAINNAME
=head1 DESCRIPTION
L is a command-line interface to the Zonemaster test engine.
It takes instructions the user provides as command line arguments, transforms
them into suitable API calls to the engine, runs the test suite and prints the
resulting messages. By default, the messages will be translated by the engine's
translation module, with the corresponding timestamp and logging level when
printed. See the available options below.
=head1 OPTIONS
=head2 Special Options
=over 4
=item B<-h>, B<--help>
Print brief usage information and exit.
(run `man zonemaster-cli` for the full manual page)
=item B<--version>
Print version information and exit.
=for :man The printed version numbers are the versions of this program as well as the ones from the underlying
Zonemaster test engine.
=item B<--list-tests>
Print all test cases listed in the test modules, then exit.
=item B<--dump-profile>
Print the effective profile used in JSON format, then exit.
=back
=head2 Testing Options
=over 4
=item B<--test>=TESTCASE, B<--test>=TESTMODULE
Limit the testing suite to run only the specified tests.
Can be specified multiple times.
(default: all test cases)
=for :man This can be the name of a testing module, in which case all test cases from
that module will be run, or the name of a module followed by a slash and the
name of a test case (test case identifier) in that module, or the name of the
test case.
This option is case-insensitive.
=item B<--level>=LEVEL
Specify the minimum level of a message to be printed.
(default: NOTICE)
=for :man Messages with this level
(or higher) will be printed. The levels are, from highest to lowest:
CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, DEBUG2 and DEBUG3.
The lowest three levels (DEBUG) add a significant amount of messages to be shown.
They reveal some of the internal workings of the test engine, and are probably
not useful for most users.
=begin :man
=item B<--stop-level>=LEVEL
Specify the minimum severity level after which the testing suite is terminated.
(default: the empty string)
=for :man When set to the empty string, testing is allowed to complete normally
no matter what messages are emitted.
=for :man The levels are, from highest to lowest: CRITICAL, ERROR, WARNING, NOTICE,
INFO, DEBUG, DEBUG2 and DEBUG3.
=end :man
=item B<--[no-]progress>
Print an activity indicator ("spinner").
(default: enabled if the process' standard output is a TTY)
=for :man Useful to know that something is happening during a run.
=item B<--[no-]ipv4>, B<--[no-]ipv6>
Enable or disable queries over IPv4 or IPv6.
(default: both enabled)
=begin :man
=item B<--sourceaddr4>=IPADDR, B<--sourceaddr6>=IPADDR
Set IPv4 or IPv6 source address for DNS queries.
=for :man Setting an address not correctly configured on a local network interface
fails silently.
=end :man
=item B<--profile>=FILE
Override the Zonemaster Engine default profile data with values from
the given profile JSON file.
=back
=head2 Formatting Options
=over 4
=item B<--[no-]json>
Print results as JSON instead of human language.
(default: disabled)
=begin :man
=item B<--[no-]json-stream>
Stream the results as JSON.
(default: disabled)
=for :man Useful to follow the progress in a machine-readable way.
=item B<--[no-]raw>
Print messages as raw dumps (message identifiers) instead of translating them
to human language.
=end :man
=item B<--locale>=LOCALE
Specify which locale to be used by the translation system.
(default: system locale or English)
=for :man If not given, the
translation system itself will look at environment variables to try and guess.
If the requested translation does not exist, it will fall back to the local
locale, and if that does not exist either, to English.
=begin :man
=item B<--[no-]time>
Print the timestamp for each message.
(default: enabled)
=item B<--[no-]show-level>
Print the severity level for each message.
(default: enabled)
=item B<--[no-]show-module>
Print the name of the module which produced the message.
(default: disabled)
=item B<--[no-]show-testcase>
Print the name of the test case (test case identifier) which produced the message.
(default: disabled)
=end :man
=back
=begin :man
=head2 Summary Options
=over 4
=item B<--[no-]count>
Print a summary, at the end of a run, of the numbers of messages for each severity
level that were logged during the run.
(default: disabled)
=item B<--[no-]nstimes>
Print a summary, at the end of a run, of the times (in milliseconds) the zone's
name servers took to answer.
(default: disabled)
=item B<--[no-]elapsed>
Print elapsed time (in seconds) at end of a run.
(default: disabled)
=back
=head2 Undelegated Test Options
=over 4
=item B<--ns>=DOMAINNAME, B<--ns>=DOMAINNAME/IPADDR
Provide information about a nameserver, for undelegated tests.
=for :man The argument
must be either: (i) a domain name and an IP address, separated by a single
slash character (/), or (ii) only a domain name, in which case a A and AAAA
records lookup for that name is done in the live global DNS tree (unless
overridden by --hints) and from which the results of that lookup will be used.
=for :man This switch can be given multiple times. As long as any of these switches
are present, their aggregated content will be used as the
entirety of the parent-side delegation information.
=item B<--ds>=KEYTAG,ALGORITHM,TYPE,DIGEST
Provide a DS record for undelegated testing (that is, a test where the
delegating nameserver information is given via --ns switches).
=for :man The four pieces
of data (keytag, algorithm, type, digest) should be in the same format they would
have in a zone file.
=item B<--hints>=FILE
Name of a root hints file to override the defaults.
=back
=head2 Cache Options
=over 4
=item B<--save>=FILE
Write the contents of the accumulated DNS packet cache to a file with the given name
after the testing suite has finished running.
=item B<--restore>=FILE
Prime the DNS packet cache with the contents from the file with the given name
before starting the testing suite.
=for :man The format of the file should be from one produced by the --save
switch.
=back
=head2 Deprecated Options
=over 4
=item B<--encoding>=ENCODING
Deprecated: Simply remove it from your usage. It is ignored.
=item B<--[no-]json-translate>
Deprecated since v2023.1, use --no-raw instead.
=for :man For streaming JSON output, include the translated message of the tag.
=back
=head2 Option Aliases
These options are provided for compatibility with older scripts.
The first two are aliases for C<--help>.
The rest are aliases for their namesakes spelled with dash C<-> instead of
underscore C<_>.
=over 4
=item B<-?>
=item B<--usage>
=item B<--dump_profile>
=item B<--[no-]json_stream>
=item B<--[no-]json_translate>
=item B<--list_tests>
=item B<--[no-]show_level>
=item B<--[no-]show_module>
=item B<--[no-]show_testcase>
=item B<--stop_level>=LEVEL
=back
=end :man
=head1 EXAMPLES
zonemaster-cli zonemaster.net
zonemaster-cli --test=delegation --level=info --no-time zonemaster.net
zonemaster-cli --test=delegation01 --level=debug zonemaster.net
zonemaster-cli --list-tests
=head1 PROFILES
The testing and result analysis performed by Zonemaster Engine is always
guided by a profile.
Zonemaster Engine has a default profile with sensible defaults.
Zonemaster CLI allows users to override the default profile data with
values from a profile JSON file with the C<--profile> option.
For details on profiles and how they are represented in files, see
L.
=head1 CONFIGURATION
If there is a readable file F (Linux style), each line
in that file will be prepended as an argument on the command line. If no
F is found (or is not readable) but
F (FreeBSD style) is found and readable then
that file will be used instead. Only one global file is loaded.
If there is a readable file F<.zonemaster/cli.args> in the user's home
directory, it will be used in the same way even when a global file has been
loaded. Any argument in user's F will override the same argument in the
global config file.
For example, if one would like to by default run with the log
level set to DEBUG and with translation to human-readable messages turned off,
one could put this in the config file:
--raw
--level=DEBUG
Only one argument per line. If the argument has a value there must be a "="
between argument and value. A line starting with "#" is a comment. Comments
cannot be added on lines with arguments.
Any arguments actually given on the command line will override what is in any of
the loaded config files.
=head1 SEE ALSO
More complete documentation on Zonemaster and its tests can be found on
L.
=head1 AUTHOR
Calle Dybedahl and others from the Zonemaster project
Zonemaster-CLI-v7.2.0/lib/Zonemaster/ 000755 001754 001754 00000000000 14761573760 017651 5 ustar 00matsd matsd 000000 000000 Zonemaster-CLI-v7.2.0/lib/Zonemaster/CLI.pm 000644 001754 001754 00000074534 14761573655 020636 0 ustar 00matsd matsd 000000 000000 # Brief help module to define the exception we use for early exits.
package Zonemaster::Engine::Exception::NormalExit;
use 5.014002;
use warnings;
use parent 'Zonemaster::Engine::Exception';
# The actual interesting module.
package Zonemaster::CLI;
use 5.014002;
use strict;
use warnings;
use version; our $VERSION = version->declare( "v7.2.0" );
use Locale::TextDomain 'Zonemaster-CLI';
use Encode;
use File::Slurp;
use Getopt::Long qw[GetOptionsFromArray :config gnu_compat bundling no_auto_abbrev];
use JSON::XS;
use List::Util qw[max uniq];
use Net::IP::XS;
use Pod::Usage;
use POSIX qw[setlocale LC_MESSAGES LC_CTYPE];
use Readonly;
use Scalar::Util qw[blessed];
use Try::Tiny;
use Zonemaster::LDNS;
use Zonemaster::Engine;
use Zonemaster::Engine::Exception;
use Zonemaster::Engine::Normalization qw[normalize_name];
use Zonemaster::Engine::Logger::Entry;
use Zonemaster::Engine::Translator;
use Zonemaster::Engine::Util qw[parse_hints];
use Zonemaster::Engine::Validation qw[validate_ipv4 validate_ipv6];
our %numeric = Zonemaster::Engine::Logger::Entry->levels;
our $JSON = JSON::XS->new->allow_blessed->convert_blessed->canonical;
our $SCRIPT = $0;
Readonly our $EXIT_SUCCESS => 0;
Readonly our $EXIT_GENERIC_ERROR => 1;
Readonly our $EXIT_USAGE_ERROR => 2;
Readonly our $DS_RE => qr/^(?:[[:digit:]]+,){3}[[:xdigit:]]+$/;
STDOUT->autoflush( 1 );
sub my_pod2usage {
my ( %opts ) = @_;
pod2usage(
-input => $SCRIPT,
-output => $opts{output},
-verbose => $opts{verbosity},
-exitcode => 'NOEXIT',
);
return;
}
# Returns an integer representing an OS exit status.
sub run {
my ( $class, @argv ) = @_;
my $opt_count = 0;
my @opt_ds = ();
my $opt_dump_profile = 0;
my $opt_elapsed = 0;
my $opt_encoding = undef;
my $opt_help = 0;
my $opt_hints;
my $opt_ipv4 = undef;
my $opt_ipv6 = undef;
my $opt_json = undef;
my $opt_json_stream = 0;
my $opt_json_translate = undef;
my $opt_level = 'NOTICE';
my $opt_list_tests = 0;
my $opt_locale = undef;
my @opt_ns = ();
my $opt_nstimes = 0;
my $opt_profile;
my $opt_progress = undef;
my $opt_raw;
my $opt_restore;
my $opt_save;
my $opt_show_level = 1;
my $opt_show_module = 0;
my $opt_show_testcase = 0;
my $opt_sourceaddr4;
my $opt_sourceaddr6;
my $opt_stop_level = '';
my @opt_test = ();
my $opt_time = 1;
my $opt_version = 0;
{
local $SIG{__WARN__} = sub { print STDERR $_[0] };
GetOptionsFromArray(
\@argv,
'count!' => \$opt_count,
'ds=s' => \@opt_ds,
'dump-profile!' => \$opt_dump_profile,
'dump_profile!' => \$opt_dump_profile,
'elapsed!' => \$opt_elapsed,
'encoding=s' => \$opt_encoding,
'hints=s' => \$opt_hints,
'help|h|usage|?!' => \$opt_help,
'ipv4!' => \$opt_ipv4,
'ipv6!' => \$opt_ipv6,
'json!' => \$opt_json,
'json-stream!' => \$opt_json_stream,
'json_stream!' => \$opt_json_stream,
'json-translate!' => \$opt_json_translate,
'json_translate!' => \$opt_json_translate,
'level=s' => \$opt_level,
'list-tests!' => \$opt_list_tests,
'list_tests!' => \$opt_list_tests,
'locale=s' => \$opt_locale,
'ns=s' => \@opt_ns,
'nstimes!' => \$opt_nstimes,
'profile=s' => \$opt_profile,
'progress!' => \$opt_progress,
'raw!' => \$opt_raw,
'restore=s' => \$opt_restore,
'save=s' => \$opt_save,
'show-level!' => \$opt_show_level,
'show_level!' => \$opt_show_level,
'show-module!' => \$opt_show_module,
'show_module!' => \$opt_show_module,
'show-testcase!' => \$opt_show_testcase,
'show_testcase!' => \$opt_show_testcase,
'sourceaddr4=s' => \$opt_sourceaddr4,
'sourceaddr6=s' => \$opt_sourceaddr6,
'stop-level=s' => \$opt_stop_level,
'stop_level=s' => \$opt_stop_level,
'test=s' => \@opt_test,
'time!' => \$opt_time,
'version!' => \$opt_version,
) or do {
my_pod2usage( verbosity => 0, output => \*STDERR );
return 2;
};
}
if ( $opt_help ) {
my_pod2usage( verbosity => 1, output => \*STDOUT );
say "Severity levels from highest to lowest:";
say " CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, DEBUG2, DEBUG3";
return 0;
}
$opt_level = uc $opt_level;
$opt_stop_level = uc $opt_stop_level;
my @accumulator;
my %counter;
my $printed_something;
if ( $opt_locale ) {
undef $ENV{LANGUAGE};
$ENV{LC_ALL} = $opt_locale;
}
# Set LC_MESSAGES and LC_CTYPE separately (https://www.gnu.org/software/gettext/manual/html_node/Triggering.html#Triggering)
if ( not defined setlocale( LC_MESSAGES, "" ) ) {
my $locale = ($ENV{LANGUAGE} || $ENV{LC_ALL} || $ENV{LC_MESSAGES});
say STDERR __x( "Warning: setting locale category LC_MESSAGES to {locale} failed -- is it installed on this system?\n\n",
locale => $locale)
}
if ( not defined setlocale( LC_CTYPE, "" ) ) {
my $locale = ($ENV{LC_ALL} || $ENV{LC_CTYPE});
say STDERR __x( "Warning: setting locale category LC_CTYPE to {locale} failed -- is it installed on this system?\n\n",
locale => $locale)
}
if ( $opt_version ) {
print_versions();
return $EXIT_SUCCESS;
}
if ( $opt_list_tests ) {
print_test_list();
return $EXIT_SUCCESS;
}
# errors and warnings
if ( defined $opt_encoding ) {
say STDERR __( "Warning: deprecated --encoding, simply remove it from your usage." );
}
if ( $opt_json_stream and defined $opt_json and not $opt_json ) {
say STDERR __( "Error: --json-stream and --no-json cannot be used together." );
return $EXIT_USAGE_ERROR;
}
if ( defined $opt_json_translate ) {
unless ( $opt_json or $opt_json_stream ) {
printf STDERR __( "Warning: --json-translate has no effect without either --json or --json-stream." ) . "\n";
}
if ( $opt_json_translate ) {
printf STDERR __( "Warning: deprecated --json-translate, use --no-raw instead." ) . "\n";
}
else {
printf STDERR __( "Warning: deprecated --no-json-translate, use --raw instead." ) . "\n";
}
}
# align values
$opt_json = 1 if $opt_json_stream;
$opt_raw //= defined $opt_json_translate ? !$opt_json_translate : 0;
# Filehandle for diagnostics output
my $fh_diag = ( $opt_json or $opt_raw or $opt_dump_profile )
? *STDERR # Structured output mode (e.g. JSON)
: *STDOUT; # Human readable output mode
my $show_progress = $opt_progress // !!-t STDOUT && !$opt_json && !$opt_raw;
if ( $opt_profile ) {
say $fh_diag __x( "Loading profile from {path}.", path => $opt_profile );
my $json = read_file( $opt_profile );
my $foo = Zonemaster::Engine::Profile->from_json( $json );
my $profile = Zonemaster::Engine::Profile->default;
$profile->merge( $foo );
Zonemaster::Engine::Profile->effective->merge( $profile );
}
if ( defined $opt_sourceaddr4 ) {
local $@;
eval {
Zonemaster::Engine::Profile->effective->set( q{resolver.source4}, $opt_sourceaddr4 );
1;
} or do {
say STDERR __x( "Error: invalid value for --sourceaddr4: {reason}", reason => $@ );
return $EXIT_USAGE_ERROR;
};
}
if ( defined $opt_sourceaddr6 ) {
local $@;
eval {
Zonemaster::Engine::Profile->effective->set( q{resolver.source6}, $opt_sourceaddr6 );
1;
} or do {
say STDERR __x( "Error: invalid value for --sourceaddr6: {reason}", reason => $@ );
return $EXIT_USAGE_ERROR;
};
}
my @testing_suite;
if ( @opt_test ) {
my %existing_tests = Zonemaster::Engine->all_methods;
my @existing_test_modules = keys %existing_tests;
my @existing_test_cases = map { @{ $existing_tests{$_} } } @existing_test_modules;
foreach my $t ( @opt_test ) {
# There should be at most one slash character
if ( $t =~ tr/\/// > 1 ) {
say STDERR __x( "Error: Invalid input '{cli_arg}' in --test. There must be at most one slash ('/') character.",
cli_arg => $t);
return $EXIT_USAGE_ERROR;
}
# The case does not matter
$t = lc( $t );
my ( $module, $method );
# Fully qualified module and test case (e.g. Example/example12), or just a test case (e.g. example12). Note the different capturing order.
if ( ( ($module, $method) = $t =~ m#^ ( [a-z]+ ) / ( [a-z]+[0-9]{2} ) $#ix )
or
( ($method, $module) = $t =~ m#^ ( ( [a-z]+ ) [0-9]{2} ) $#ix ) )
{
# Check that test module exists
if ( grep( /^$module$/, map { lc($_) } @existing_test_modules ) ) {
# Check that test case exists
if ( grep( /^$method$/, @existing_test_cases ) ) {
push @testing_suite, "$module/$method";
}
else {
say STDERR __x( "Error: Unrecognized test case '{testcase}' in --test. Use --list-tests for a list of valid choices.",
testcase => $method );
return $EXIT_USAGE_ERROR;
}
}
else {
say STDERR __x( "Error: Unrecognized test module '{module}' in --test. Use --list-tests for a list of valid choices.",
module => $module );
return $EXIT_USAGE_ERROR;
}
}
# Just a module name (e.g. Example) or something invalid.
else {
$t =~ s{/$}{};
# Check that test module exists
if ( grep( /^$t$/, map { lc($_) } @existing_test_modules ) ) {
push @testing_suite, $t;
}
else {
say STDERR __x( "Error: Invalid input '{cli_arg}' in --test.",
cli_arg => $t);
return $EXIT_USAGE_ERROR;
}
}
}
# Start with all profile-enabled test cases
my @actual_test_cases = @{ Zonemaster::Engine::Profile->effective->get( 'test_cases' ) };
# Derive test module from each profile-enabled test case
my %actual_test_modules;
foreach my $t ( @actual_test_cases ) {
my ( $module ) = $t =~ m#^ ( [a-z]+ ) [0-9]{2} $#ix;
$actual_test_modules{$module} = 1;
}
# Check if more test cases need to be included in the profile
foreach my $t ( @testing_suite ) {
# Either a module/method, or just a module
my ( $module, $method ) = split('/', $t);
if ( $method ) {
# Test case in not already in the profile, we add it explicitly and notify the user
if ( not grep( /^$method$/, @actual_test_cases ) ) {
say $fh_diag __x( "Notice: Engine does not have test case '{testcase}' enabled in the profile. Forcing...",
testcase => $method );
push @actual_test_cases, $method;
}
}
else {
# No test case from this module is already in the profile, we can add them all
if ( not grep( /^$module$/, keys %actual_test_modules ) ) {
# Get the test module with the right case
( $module ) = grep { lc( $module ) eq lc( $_ ) } @existing_test_modules;
# No need to bother to check for duplicates here
push @actual_test_cases, @{ $existing_tests{$module} };
}
}
}
# Configure Engine to include all of the required test cases in the profile
Zonemaster::Engine::Profile->effective->set( 'test_cases', [ uniq sort @actual_test_cases ] );
}
# These two must come after any profile from command line has been loaded
# to make any IPv4/IPv6 option override the profile setting.
if ( defined( $opt_ipv4 ) ) {
Zonemaster::Engine::Profile->effective->set( q{net.ipv4}, $opt_ipv4 );
}
if ( defined( $opt_ipv6 ) ) {
Zonemaster::Engine::Profile->effective->set( q{net.ipv6}, $opt_ipv6 );
}
if ( $opt_dump_profile ) {
do_dump_profile();
return $EXIT_SUCCESS;
}
if ( $opt_stop_level and not defined( $numeric{$opt_stop_level} ) ) {
say STDERR __x( "Failed to recognize stop level '{level}'.", level => $opt_stop_level );
return $EXIT_USAGE_ERROR;
}
if ( not defined $numeric{$opt_level} ) {
say STDERR __( "--level must be one of CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, DEBUG2 or DEBUG3." );
return $EXIT_USAGE_ERROR;
}
if ( $opt_restore ) {
Zonemaster::Engine->preload_cache( $opt_restore );
}
my $level_width = 0;
foreach ( keys %numeric ) {
if ( $numeric{$opt_level} <= $numeric{$_} ) {
my $width_l10n = length( decode_utf8( translate_severity( $_ ) ) );
$level_width = $width_l10n if $width_l10n > $level_width;
}
}
my $translator;
my %field_width = (
seconds => 7,
level => $level_width,
module => 12,
testcase => 14
);
my %header_names = ();
my %remaining_space = ();
# Callback defined here so it closes over the setup above.
# But we can’t use it right now because the translator isn’t initialized.
my $message_printer = sub {
my ( $entry ) = @_;
print_spinner() if $show_progress;
$counter{ uc $entry->level } += 1;
if ( $numeric{ uc $entry->level } >= $numeric{$opt_level} ) {
$printed_something = 1;
if ( $opt_json and $opt_json_stream ) {
my %r;
$r{timestamp} = $entry->timestamp if $opt_time;
$r{module} = $entry->module if $opt_show_module;
$r{testcase} = $entry->testcase if $opt_show_testcase;
$r{tag} = $entry->tag;
$r{level} = $entry->level if $opt_show_level;
$r{args} = $entry->args if $entry->args;
$r{message} = $translator->translate_tag( $entry ) unless $opt_raw;
say $JSON->encode( \%r );
}
elsif ( $opt_json and not $opt_json_stream ) {
# Don't do anything
}
else {
my $prefix = q{};
if ( $opt_time ) {
$prefix .= sprintf "%*.2f ", ${field_width{seconds}}, $entry->timestamp;
}
if ( $opt_show_level ) {
$prefix .= $opt_raw ? $entry->level : translate_severity( $entry->level );
my $space_l10n =
${ field_width { level } } - length( decode_utf8( translate_severity( $entry->level ) ) ) + 1;
$prefix .= ' ' x $space_l10n;
}
if ( $opt_show_module ) {
$prefix .= sprintf "%-*s ", ${field_width{module}}, $entry->module;
}
if ( $opt_show_testcase ) {
$prefix .= sprintf "%-*s ", ${field_width{testcase}}, $entry->testcase;
}
if ( $opt_raw ) {
$prefix .= $entry->tag;
my $message = $entry->argstr;
my @lines = split /\n/, $message;
printf "%s%s %s\n", $prefix, ' ', @lines ? shift @lines : '';
for my $line ( @lines ) {
printf "%s%s %s\n", $prefix, '>', $line;
}
}
else {
if ( $entry->level eq q{DEBUG3} and scalar( keys %{$entry->args} ) == 1 and defined $entry->args->{packet} ) {
my $packet = $entry->args->{packet};
my $padding = q{ } x length $prefix;
$entry->args->{packet} = q{};
printf "%s%s\n", $prefix, $translator->translate_tag( $entry );
foreach my $line ( split /\n/, $packet ) {
printf "%s%s\n", $padding, $line;
}
}
else {
printf "%s%s\n", $prefix, $translator->translate_tag( $entry );
}
}
}
}
if ( $opt_stop_level and $numeric{ uc $entry->level } >= $numeric{$opt_stop_level} ) {
die( Zonemaster::Engine::Exception::NormalExit->new( { message => "Saw message at level " . $entry->level } ) );
}
};
# Instead, hold early messages in a temporary queue and switch to the
# actual callback when we are ready.
my @held_messages;
Zonemaster::Engine->logger->callback(
sub {
my ( $entry ) = @_;
push @held_messages, @_;
}
);
if ( @argv > 1 ) {
say STDERR __(
"Only one domain can be given for testing. Did you forget to prepend an option with '--