From e0047202eac13bbdc65f8823e5e1e1a05fd081bf Mon Sep 17 00:00:00 2001 From: Miod Vallat Date: Thu, 23 Jan 2025 15:28:26 +0100 Subject: [PATCH] Friendlier usage output. 1. The commands dictionary is extended with a command group, as well as the one-liner synopsis and help message. When the list of these one-liners get output, they are now output in sorted order within each group. 2. Instead of having the syntax on the leftmost 40 columns and the description on the rightmost 40 columns, causing many commands to span more than one line total, we now display the syntax on a whole line, and the description on the next one, indented by a tab. This should hopefully improve readability (especially for complex syntax lines). 3. The command synopsis is now passed to the command handler. This allows error messages in case of invocation error to be consistent with the output of `pdnsutil help`. 4. Tweak and homogeneize command synopsis, and sync the pdnsutil documentation accordingly. Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- docs/manpages/pdnsutil.1.rst | 342 +++++++++--- pdns/pdnsutil.cc | 1023 ++++++++++++++++++---------------- 2 files changed, 806 insertions(+), 559 deletions(-) diff --git a/docs/manpages/pdnsutil.1.rst b/docs/manpages/pdnsutil.1.rst index 68c3198a8d24..dd9d2c446bb7 100644 --- a/docs/manpages/pdnsutil.1.rst +++ b/docs/manpages/pdnsutil.1.rst @@ -46,58 +46,99 @@ algorithms are supported: - ed25519 - ed448 -activate-zone-key *ZONE* *KEY-ID* - Activate a key with id *KEY-ID* within a zone called *ZONE*. -add-zone-key *ZONE* [**KSK**,\ **ZSK**] [**active**,\ **inactive**] [**published**,\ **unpublished**] [*KEYBITS*] [*ALGORITHM*] +.. note:: + ed25519 and ed448 algorithms will only be available if adequate cryptographic + libraries have been available while compiling PowerDNS on your particular + system. + +In addition to the algorithm, some commands below may ask for a key size in +bits. The key size may be omitted for the ECC algorithms, which support only +one valid size per algorithm; for ECDSA256 and ED25519, it is 256; +for ECDSA384, it is 384; and for ED448, it is... 456. + +activate-zone-key *ZONE* *KEY_ID* + + Activate a key with id *KEY_ID* within a zone called *ZONE*. + +add-zone-key *ZONE* [**KSK**,\ **ZSK**] [**active**,\ **inactive**] [**published**,\ **unpublished**] *ALGORITHM* [*KEYBITS*] + Create a new key for zone *ZONE*, and make it a KSK (default) or a ZSK, with - the specified algorithm. The key is inactive by default, set it to - **active** to immediately use it to sign *ZONE*. The key is published - in the zone by default, set it to **unpublished** to keep it from - being returned in a DNSKEY query, which is useful for algorithm - rollovers. Prints the id of the added key. -create-bind-db *FILE* - Create DNSSEC database (sqlite3) at *FILE* for the BIND backend. + the specified *ALGORITHM* and *KEYBITS*. If *KEYBITS* is omitted, the value + of :ref:`setting-default-ksk-size` or :ref:`setting-default-zsk-size` are + used. + + The key is inactive by default, set it to **active** to immediately use it + to sign *ZONE*. The key is published in the zone by default, set it to + **unpublished** to keep it from being returned in a DNSKEY query, which is + useful for algorithm rollovers. + + Prints the id of the added key. + +create-bind-db *FILENAME* + + Create DNSSEC database (sqlite3) at *FILENAME* for the BIND backend. Remember to set ``bind-dnssec-db=*FILE*`` in your ``pdns.conf``. -deactivate-zone-key *ZONE* *KEY-ID* - Deactivate a key with id KEY-ID within a zone called *ZONE*. + +deactivate-zone-key *ZONE* *KEY_ID* + + Deactivate a key with id KEY_ID within a zone called *ZONE*. + disable-dnssec *ZONE* + Deactivate all keys and unset PRESIGNED in *ZONE*. -export-zone-dnskey *ZONE* *KEY-ID* - Export to standard output DNSKEY and DS of key with key id *KEY-ID* - within zone called *ZONE*. + +export-zone-dnskey *ZONE* *KEY_ID* + + Export DNSKEY and DS of key with key id *KEY_ID* within zone *ZONE* to + standard output. + export-zone-ds *ZONE* - Export to standard output all KSK DS records for *ZONE*. -export-zone-key *ZONE* *KEY-ID* - Export to standard output full (private) key with key id *KEY-ID* - within zone called *ZONE*. The format used is compatible with BIND - and NSD/LDNS. -export-zone-key-pem *ZONE* *KEY-ID* - Export to standard output full (private) key with key id *KEY-ID* - within zone called *ZONE* in the PEM file format. The format is - compatible with many non-DNS software products. + + Export all KSK DS records for *ZONE* to standard output. + +export-zone-key *ZONE* *KEY_ID* + + Export full (private) key with key id *KEY_ID* within zone *ZONE* to + standard output. The format used is compatible with BIND and NSD/LDNS. + +export-zone-key-pem *ZONE* *KEY_ID* + + Export full (private) key with key id *KEY_ID* within zone *ZONE* to + standard output in the PEM file format. The format is compatible with + many non-DNS software products. + generate-zone-key {**KSK**,\ **ZSK**} [*ALGORITHM*] [*KEYBITS*] - Generate a ZSK or KSK to stdout with specified algorithm and bits - and print it on STDOUT. If *ALGORITHM* is not set, ECDSA256 is - used. If *KEYBITS* is not set, an appropriate keysize is selected - for *ALGORITHM*. Each ECC-based algorithm supports only one valid - *KEYBITS* value: For ECDSA256 and ED25519, it is 256; for ECDSA384, - it is 384; and for ED448, it is 456. -import-zone-key *ZONE* *FILE* {**KSK**,\ **ZSK**} - Import from *FILE* a full (private) key for the zone called *ZONE*. The + + Generate a ZSK or KSK with specified algorithm and bits and print it + on standard output. If *ALGORITHM* is not set, ECDSA256 is used. + If *KEYBITS* is not set, an appropriate keysize is selected + for *ALGORITHM*: for RSA keys, 2048 bits for KSK and 1024 bits for ZSK; + for ECC keys, the algorithm-required size as mentioned above. + +import-zone-key *ZONE* *FILE* [**KSK**,\ **ZSK**] [**active**,\ **inactive**] [**published**,\ **unpublished**] + + Import from *FILE* a full (private) key for the zone *ZONE*. The format used is compatible with BIND and NSD/LDNS. **KSK** or **ZSK** - specifies the flags this key should have on import. Prints the id of - the added key. -import-zone-key-pem *ZONE* *FILE* *ALGORITHM* {**KSK**,\**ZSK**} - Import from PEM *FILE* a full (private) key for the zone called - *ZONE* with a specified *ALGORITHM*. The format used is compatible - with many non-DNS software products. **KSK** or **ZSK** specifies - the flags this key should have on import. Prints the id of the added - key. -publish-zone-key *ZONE* *KEY-ID* - Publish the key with id *KEY-ID* within a zone called *ZONE*. -remove-zone-key *ZONE* *KEY-ID* - Remove a key with id *KEY-ID* from a zone called *ZONE*. + specifies the flags this key should have on import. Defaults to KSK, + active and published. Prints the id of the added key. + +import-zone-key-pem *ZONE* *FILE* *ALGORITHM* {**KSK**,\ **ZSK**} + + Import from PEM *FILE* a full (private) key for the zone *ZONE* with a + specified *ALGORITHM*. The format used is compatible with many non-DNS + software products. **KSK** or **ZSK** specifies the flags this key should + have on import. Prints the id of the added key. + +publish-zone-key *ZONE* *KEY_ID* + + Publish the key with id *KEY_ID* within zone *ZONE*. + +remove-zone-key *ZONE* *KEY_ID* + + Remove a key with id *KEY_ID* from zone *ZONE*. + set-nsec3 *ZONE* ['*HASH-ALGORITHM* *FLAGS* *ITERATIONS* *SALT*'] [**narrow**] + Sets NSEC3 parameters for this zone. The quoted parameters are 4 values that are used for the NSEC3PARAM record and decide how NSEC3 records are created. The NSEC3 parameters must be quoted on @@ -105,36 +146,56 @@ set-nsec3 *ZONE* ['*HASH-ALGORITHM* *FLAGS* *ITERATIONS* *SALT*'] [**narrow**] *FLAGS* to 1 enables NSEC3 opt-out operation. Only do this if you know you need it. For *ITERATIONS*, please consult :rfc:`RFC 5155<5155#section-10.3>`. + And be aware that a high number might overload validating resolvers and that a limit can be set with ``max-nsec3-iterations`` in ``pdns.conf``. The *SALT* is a hexadecimal string encoding the bits - for the salt, or - to use no salt. Setting **narrow** will make PowerDNS - send out "white lies" (:rfc:`7129`) about the next secure record to - prevent zone enumeration. Instead of looking it up in the database, - it will send out the hash + 1 as the next secure record. Narrow mode - requires online signing capabilities by the nameserver and therefore - zone transfers are denied. If only the zone is provided as argument, - the 4-parameter quoted string defaults to ``'1 0 0 -'``. A sample - commandline is: ``pdnsutil set-nsec3 powerdnssec.org '1 1 1 ab' narrow``. + for the salt, or - to use no salt. + + Setting **narrow** will make PowerDNS send out "white lies" (:rfc:`7129`) + about the next secure record to prevent zone enumeration. Instead of + looking it up in the database, it will send out the hash + 1 as the next + secure record. Narrow mode requires online signing capabilities by the + nameserver and therefore zone transfers are denied. + + If only the zone is provided as argument, the 4-parameter quoted string + defaults to ``'1 0 0 -'``. + + A sample commandline would be: + + ``pdnsutil set-nsec3 powerdnssec.org '1 1 1 ab' narrow`` + **WARNING**: If running in RSASHA1 mode (algorithm 5 or 7), switching from NSEC to NSEC3 will require a DS update in the parent zone. -unpublish-zone-key *ZONE* *KEY-ID* - Unpublish the key with id *KEY-ID* within a zone called *ZONE*. + +unpublish-zone-key *ZONE* *KEY_ID* + + Unpublish the key with id *KEY_ID* within zone *ZONE*. + unset-nsec3 *ZONE* + Converts *ZONE* to NSEC operations. **WARNING**: If running in RSASHA1 mode (algorithm 5 or 7), switching from NSEC to NSEC3 will require a DS update at the parent zone! + set-publish-cds *ZONE* [*DIGESTALGOS*] + Set *ZONE* to respond to queries for its CDS records. the optional argument *DIGESTALGOS* should be a comma-separated list of DS algorithms to use. By default, this is 2 (SHA-256). 0 will publish a CDS with a DNSSEC delete algorithm. + set-publish-cdnskey *ZONE* [**delete**] + Set *ZONE* to publish CDNSKEY records. Add 'delete' to publish a CDNSKEY with a DNSSEC delete algorithm. + unset-publish-cds *ZONE* + Set *ZONE* to stop responding to queries for its CDS records. + unset-publish-cdnskey *ZONE* + Set *ZONE* to stop publishing CDNSKEY records. TSIG RELATED COMMANDS @@ -151,157 +212,262 @@ commands require an *ALGORITHM*, the following are available: - hmac-sha512 activate-tsig-key *ZONE* *NAME* {**primary**,\ **secondary**,\ **producer**,\ **consumer**} + Enable TSIG authenticated AXFR using the key *NAME* for zone *ZONE*. This sets the ``TSIG-ALLOW-AXFR`` (primary/producer) or ``AXFR-MASTER-TSIG`` (secondary/consumer) zone metadata. + deactivate-tsig-key *ZONE* *NAME* {**primary**,\ **secondary**,\ **producer**,\ **consumer**} + Disable TSIG authenticated AXFR using the key *NAME* for zone *ZONE*. + delete-tsig-key *NAME* - Delete the TSIG key *NAME*. Warning, this does not deactivate said - key. + + Delete the TSIG key *NAME*. Warning, this does not deactivate said key. + generate-tsig-key *NAME* *ALGORITHM* + Generate new TSIG key with name *NAME* and the specified algorithm. + import-tsig-key *NAME* *ALGORITHM* *KEY* + Import *KEY* of the specified algorithm as *NAME*. + list-tsig-keys + Show a list of all configured TSIG keys. ZONE MANIPULATION COMMANDS -------------------------- add-record *ZONE* *NAME* *TYPE* [*TTL*] *CONTENT* + Add one or more records of *NAME* and *TYPE* to *ZONE* with *CONTENT* and optional *TTL*. If *TTL* is not set, default will be used. + add-autoprimary *IP* *NAMESERVER* [*ACCOUNT*] - Add a autoprimary entry into the backend. This enables receiving zone updates from other servers. + + Add a autoprimary entry into the backend. This enables receiving zone + updates from other servers. + remove-autoprimary *IP* *NAMESERVER* + Remove an autoprimary from backend. Not supported by BIND backend. + list-autoprimaries + List all autoprimaries. + create-zone *ZONE* + Create an empty zone named *ZONE*. + create-secondary-zone *ZONE* *PRIMARY* [*PRIMARY*]... + Create a new secondary zone *ZONE* with primaries *PRIMARY*. All *PRIMARY*\ s need to to be space-separated IP addresses with an optional port. + change-secondary-zone-primary *ZONE* *PRIMARY* [*PRIMARY*]... + Change the primaries for secondary zone *ZONE* to new primaries *PRIMARY*. All *PRIMARY*\ s need to to be space-separated IP addresses with an optional port. + check-all-zones + Check all zones for correctness. + check-zone *ZONE* + Check zone *ZONE* for correctness. + clear-zone *ZONE* + Clear the records in zone *ZONE*, but leave actual zone and settings unchanged + delete-rrset *ZONE* *NAME* *TYPE* + Delete named RRSET from zone. + delete-zone *ZONE* + Delete the zone named *ZONE*. + edit-zone *ZONE* + Opens *ZONE* in zonefile format (regardless of backend it was loaded from) in the editor set in the environment variable **EDITOR**. if **EDITOR** is empty, *pdnsutil* falls back to using *editor*. -get-meta *ZONE* [*ATTRIBUTE*]... - Get zone metadata. If no *ATTRIBUTE* given, lists all known. -hash-password [*WORK-FACTOR*] - This convenience command asks for a password and returns a hashed - and salted version, for use as a webserver password or api key. - An optional scrypt work factor can be specified, in power of two, + +hash-password [*WORK_FACTOR*] + + This convenience command reads a password (not echoed) from standard + input and returns a hashed and salted version, for use as a webserver + password or api key. + An optional scrypt work factor can be specified, in powers of two, otherwise it defaults to 1024. + hash-zone-record *ZONE* *RNAME* + This convenience command hashes the name *RNAME* according to the NSEC3 settings of *ZONE*. Refuses to hash for zones with no NSEC3 settings. + increase-serial *ZONE* + Increases the SOA-serial by 1. Uses SOA-EDIT. + list-keys [*ZONE*] - List DNSSEC information for all keys or for *ZONE*. --verbose or -v will - also include the keys for disabled or empty zones. -list-all-zones - List all active zone names. --verbose or -v will also include disabled - or empty zones. + + List DNSSEC information for all keys or for *ZONE* only. Passing + --verbose or -v will also include the keys for disabled or empty zones. + +list-all-zones *KIND* + + List all active zone names of the given *KIND* (primary, secondary, + native, producer, consumer), or all if none given. Passing --verbose or + -v will also include disabled or empty zones. + list-member-zones *CATALOG* + List all members of catalog zone *CATALOG*" + list-zone *ZONE* + Show all records for *ZONE*. + load-zone *ZONE* *FILE* + Load records for *ZONE* from *FILE*. If *ZONE* already exists, all records are overwritten, this operation is atomic. If *ZONE* doesn't exist, it is created. + rectify-zone *ZONE* + Calculates the 'ordername' and 'auth' fields for a zone called *ZONE* so they comply with DNSSEC settings. Can be used to fix up - migrated data. Can always safely be run, it does no harm. + migrated data. + rectify-all-zones + Calculates the 'ordername' and 'auth' fields for all zones so they comply with DNSSEC settings. Can be used to fix up migrated data. - Can always safely be run, it does no harm. + replace-rrset *ZONE* *NAME* *TYPE* [*TTL*] *CONTENT* [*CONTENT*...] + Replace existing *NAME* in zone *ZONE* with a new set. + secure-zone *ZONE* + Configures a zone called *ZONE* with reasonable DNSSEC settings. You should manually run 'pdnsutil rectify-zone' afterwards. + secure-all-zones [**increase-serial**] + Configures all zones that are not currently signed with reasonable DNSSEC settings. Setting **increase-serial** will increase the serial of those zones too. You should manually run 'pdnsutil rectify-all-zones' afterwards. + set-kind *ZONE* *KIND* - Change the kind of *ZONE* to *KIND* (primary, secondary, native, producer, consumer). -set-options-json *ZONE* *JSON* - Change the options of *ZONE* to *JSON* -set-option *ZONE* [*producer*|*consumer*] [*coo*|*unique*|*group*] *VALUE* [*VALUE* ...] - Set or remove an option for *ZONE*. Providing an empty value removes an option. -set-catalog *ZONE* *CATALOG* - Change the catalog of *ZONE* to *CATALOG*. Setting *CATALOG* to an empty "" removes *ZONE* from the catalog it is in. + + Change the kind of *ZONE* to *KIND* (primary, secondary, native, producer, + consumer). + +set-options-json *ZONE* *JSONFILE* + + Change the options of *ZONE* to the contents of *JSONFILE*. + +set-option *ZONE* [*producer* | *consumer*] [*coo* | *unique* | *group*] *VALUE* [*VALUE* ...] + + Set or remove an option for *ZONE*. Providing an empty value removes + an option. + +set-catalog *ZONE* [*CATALOG*] + + Change the catalog of *ZONE* to *CATALOG*. If *CATALOG* is omitted, + removes *ZONE* from the catalog it is in. + set-account *ZONE* *ACCOUNT* + Change the account (owner) of *ZONE* to *ACCOUNT*. -add-meta *ZONE* *ATTRIBUTE* *VALUE* [*VALUE*]... - Append *VALUE* to the existing *ATTRIBUTE* metadata for *ZONE*. - Will return an error if *ATTRIBUTE* does not support multiple values, use + +add-meta *ZONE* *KIND* *VALUE* [*VALUE*]... + + Append *VALUE* to the existing *KIND* metadata for *ZONE*. + Will return an error if *KIND* does not support multiple values, use **set-meta** for these values. -set-meta *ZONE* *ATTRIBUTE* [*VALUE*]... - Set zonemetadata *ATTRIBUTE* for *ZONE* to *VALUE*. An empty value - clears it. + +get-meta *ZONE* [*KIND*]... + + Get zone metadata. If no *KIND* given, lists all known. + +set-meta *ZONE* *KIND* [*VALUE*]... + + Set zone metadata *KIND* for *ZONE* to *VALUE*, replacing all existing + values of *KIND*. An omitted value clears it. + set-presigned *ZONE* + Switches *ZONE* to presigned operation, utilizing in-zone RRSIGs. + show-zone *ZONE* + Shows all DNSSEC related settings of a zone called *ZONE*. + test-schema *ZONE* + Test database schema, this creates the zone *ZONE* + unset-presigned *ZONE* + Disables presigned operation for *ZONE*. + raw-lua-from-content *TYPE* *CONTENT* + Display record contents in a form suitable for dnsdist's `SpoofRawAction`. + zonemd-verify-file *ZONE* *FILE* + Validate ZONEMD for *ZONE* read from *FILE*. DEBUGGING TOOLS --------------- backend-cmd *BACKEND* *CMD* [*CMD...*] + Send a text command to a backend for execution. GSQL backends will take SQL commands, other backends may take different things. Be careful! -backend-lookup *BACKEND* *NAME* [*TYPE* [*CLIENT-IP-SUBNET*]] + +backend-lookup *BACKEND* *NAME* [*TYPE* [*CLIENT_IP_SUBNET*]] + Perform a backend record lookup. + bench-db [*FILE*] + Perform a benchmark of the backend-database. *FILE* can be a file with a list, one per line, of zone names to use for this. If *FILE* is not specified, powerdns.com is used. OTHER TOOLS ----------- + b2b-migrate *OLD* *NEW* + Migrate data from one backend to another. Needs ``launch=OLD,NEW`` in the configuration. -ipencrypt *IP-ADDRESS* password - Encrypt an IP address according to the 'ipcipher' standard +ipencrypt *IP_ADDRESS* PASSPHRASE_OR_KEY [**key**] + + Encrypt an IP address according to the 'ipcipher' standard. If the + passphrase is a base64 key, add the word "key" after it. + +ipdecrypt *IP_ADDRESS* PASSPHRASE_OR_KEY [**key**] -ipdecrypt *IP-ADDRESS* password - Decrypt an IP address according to the 'ipcipher' standard + Decrypt an IP address according to the 'ipcipher' standard. If the + passphrase is a base64 key, add the word "key" after it. See also -------- diff --git a/pdns/pdnsutil.cc b/pdns/pdnsutil.cc index ccca24bb6ab7..de4c81cffa6a 100644 --- a/pdns/pdnsutil.cc +++ b/pdns/pdnsutil.cc @@ -197,6 +197,13 @@ UtilBackend::~UtilBackend() } } +static int usage(const std::string_view synopsis) +{ + cerr << "Usage:" << endl; + cerr << "pdnsutil " << synopsis << endl; + return EXIT_FAILURE; +} + static bool rectifyZone(DNSSECKeeper& dk, const DNSName& zone, bool quiet = false, bool rectifyTransaction = true) { string output; @@ -1747,7 +1754,7 @@ static int deleteRRSet(const std::string& zone_, const std::string& name_, const return EXIT_SUCCESS; } -static int listAllZones(const string &type="") { +static int listAllZones(const std::string_view synopsis, const string &type="") { int kindFilter = -1; if (!type.empty()) { @@ -1762,8 +1769,7 @@ static int listAllZones(const string &type="") { else if (toUpper(type) == "CONSUMER") kindFilter = 4; else { - cerr << "Syntax: pdnsutil list-all-zones [primary|secondary|native|producer|consumer]" << endl; - return 1; + return usage(synopsis); } } @@ -1841,7 +1847,7 @@ static bool testAlgorithms() return DNSCryptoKeyEngine::testAll(); } -static void testSpeed(const DNSName& zone, const string& /* remote */, int cores) +static void testSpeed(const DNSName& zone, int cores) { DNSResourceRecord rr; rr.qname=DNSName("blah")+zone; @@ -2518,17 +2524,16 @@ static int addOrSetMeta(const DNSName& zone, const string& kind, const vector& cmds) +static int lmdbGetBackendVersion([[maybe_unused]] vector& cmds, [[maybe_unused]] const std::string_view synopsis) { cout << "5" << endl; // FIXME this should reuse the constant from lmdbbackend but that is currently a #define in a .cc return 0; } -static int testAlgorithm(vector& cmds) +static int testAlgorithm(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr << "Syntax: pdnsutil test-algorithm algonum"<(cmds.at(1)))) { return 0; @@ -2536,11 +2541,10 @@ static int testAlgorithm(vector& cmds) return 1; } -static int ipEncrypt(vector& cmds) +static int ipEncrypt(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 3 || (cmds.size() == 4 && cmds.at(3) != "key")) { - cerr<<"Syntax: pdnsutil [ipencrypt|ipdecrypt] IP passphrase [key]"<& cmds) #endif /* HAVE_IPCIPHER */ } -static int testAlgorithms([[maybe_unused]] vector& cmds) +static int testAlgorithms([[maybe_unused]] vector& cmds, [[maybe_unused]] const std::string_view synopsis) { if (testAlgorithms()) { return 0; @@ -2568,11 +2572,10 @@ static int testAlgorithms([[maybe_unused]] vector& cmds) return 1; } -static int listAlgorithms(vector& cmds) +static int listAlgorithms(vector& cmds, const std::string_view synopsis) { if ((cmds.size() == 2 && cmds.at(1) != "with-backend") || cmds.size() > 2) { - cerr<<"Syntax: pdnsutil list-algorithms [with-backend]"<& cmds) // these need reportAllTypes -static int createBindDb(vector& cmds) +static int createBindDb([[maybe_unused]] vector& cmds, [[maybe_unused]] const std::string_view synopsis) { #ifdef HAVE_SQLITE3 if(cmds.size() != 2) { - cerr << "Syntax: pdnsutil create-bind-db FNAME"<& cmds) } return 0; #else - cerr<<"bind-dnssec-db requires building PowerDNS with SQLite3"<& cmds) +static int rawLuaFromContent(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 3) { - cerr<<"Usage: raw-lua-from-content TYPE CONTENT"<& cmds) return 0; } -static int hashPassword(vector& cmds) +static int hashPassword(vector& cmds, [[maybe_unused]] const std::string_view synopsis) { uint64_t workFactor = CredentialsHolder::s_defaultWorkFactor; if (cmds.size() > 1) { @@ -2657,11 +2658,10 @@ static int hashPassword(vector& cmds) } } -static int zonemdVerifyFile(vector& cmds) +static int zonemdVerifyFile(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 3) { - cerr<<"Syntax: pdnsutil zonemd-verify-file ZONE FILENAME"<& cmds) // these need DNSSECKeeper -static int testSchema(vector& cmds) +static int testSchema(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr << "Syntax: pdnsutil test-schema ZONE"<& cmds) +static int rectifyZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 2) { - cerr << "Syntax: pdnsutil rectify-zone ZONE [ZONE..]"<& cmds) return exitCode; } -static int rectifyAllZones(vector& cmds) +static int rectifyAllZones(vector& cmds, [[maybe_unused]] const std::string_view synopsis) { bool quiet = (cmds.size() >= 2 && cmds.at(1) == "quiet"); DNSSECKeeper dk; //NOLINT(readability-identifier-length) @@ -2708,88 +2706,82 @@ static int rectifyAllZones(vector& cmds) return 0; } -static int checkZone(vector& cmds) +static int checkZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr << "Syntax: pdnsutil check-zone ZONE"<& cmds) +static int benchDb(vector& cmds, [[maybe_unused]] const std::string_view synopsis) { dbBench(cmds.size() > 1 ? cmds.at(1) : ""); return 0; } -static int checkAllZones(vector& cmds) +static int checkAllZones(vector& cmds, [[maybe_unused]] const std::string_view synopsis) { bool exitOnError = ((cmds.size() >= 2 ? cmds.at(1) : "") == "exit-on-error"); DNSSECKeeper dk; //NOLINT(readability-identifier-length) return checkAllZones(dk, exitOnError); } -static int listAllZones(vector& cmds) +static int listAllZones(vector& cmds, const std::string_view synopsis) { if (cmds.size() > 2) { - cerr << "Syntax: pdnsutil list-all-zones [primary|secondary|native|producer|consumer]" << endl; - return 0; + return usage(synopsis); } if (cmds.size() == 2) { - return listAllZones(cmds.at(1)); + return listAllZones(synopsis, cmds.at(1)); } - return listAllZones(); + return listAllZones(synopsis); } -static int listMemberZones(vector& cmds) +static int listMemberZones(vector& cmds, const std::string_view synopsis) { if (cmds.size() != 2) { - cerr << "Syntax: pdnsutil list-member-zones CATALOG" << endl; - return 0; + return usage(synopsis); } return listMemberZones(cmds.at(1)); } -static int testZone([[maybe_unused]] vector& cmds) +static int testZone([[maybe_unused]] vector& cmds, [[maybe_unused]] const std::string_view synopsis) { cerr << "Did you mean check-zone?"<& cmds) +static int testAllZones([[maybe_unused]] vector& cmds, [[maybe_unused]] const std::string_view synopsis) { cerr << "Did you mean check-all-zones?"<& cmds) +static int testSpeed(vector& cmds, const std::string_view synopsis) { - if(cmds.size() < 2) { - cerr << "Syntax: pdnsutil test-speed numcores [signing-server]"< 3) ? cmds.at(3) : "", pdns::checked_stoi(cmds.at(2))); + testSpeed(DNSName(cmds.at(1)), pdns::checked_stoi(cmds.at(2))); return 0; } -static int verifyCrypto(vector& cmds) +static int verifyCrypto(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr << "Syntax: pdnsutil verify-crypto FILE"<& cmds) +static int showZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr << "Syntax: pdnsutil show-zone ZONE"<& cmds) return 0; } -static int exportZoneDS(vector& cmds) +static int exportZoneDS(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr << "Syntax: pdnsutil export-zone-ds ZONE"<& cmds) return 0; } -static int disableDNSSEC(vector& cmds) +static int disableDNSSEC(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr << "Syntax: pdnsutil disable-dnssec ZONE"<& cmds) return 0; } -static int activateZoneKey(vector& cmds) +static int activateZoneKey(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 3) { - cerr << "Syntax: pdnsutil activate-zone-key ZONE KEY-ID"<& cmds) return 0; } -static int deactivateZoneKey(vector& cmds) +static int deactivateZoneKey(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 3) { - cerr << "Syntax: pdnsutil deactivate-zone-key ZONE KEY-ID"<(cmds.at(2)); // NOLINT(readability-identifier-length) @@ -2881,11 +2869,10 @@ static int deactivateZoneKey(vector& cmds) return 0; } -static int publishZoneKey(vector& cmds) +static int publishZoneKey(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 3) { - cerr << "Syntax: pdnsutil publish-zone-key ZONE KEY-ID"<& cmds) return 0; } -static int unpublishZoneKey(vector& cmds) +static int unpublishZoneKey(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 3) { - cerr << "Syntax: pdnsutil unpublish-zone-key ZONE KEY-ID"<& cmds) +static int addZoneKey(vector& cmds, const std::string_view synopsis) { - if(cmds.size() < 2 ) { - cerr << "Syntax: pdnsutil add-zone-key ZONE [zsk|ksk] [BITS] [active|inactive] [rsasha1|rsasha1-nsec3-sha1|rsasha256|rsasha512|ecdsa256|ecdsa384"; -#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO_ED25519) - cerr << "|ed25519"; -#endif -#if defined(HAVE_LIBCRYPTO_ED448) - cerr << "|ed448"; -#endif - cerr << "]"<& cmds) return checkZoneKey(dk, zone, id); } -static int removeZoneKey(vector& cmds) +static int removeZoneKey(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 3) { - cerr<<"Syntax: pdnsutil remove-zone-key ZONE KEY-ID"<& cmds) return 0; } -static int deleteZone(vector& cmds) +static int deleteZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr<<"Syntax: pdnsutil delete-zone ZONE"<& cmds) +static int createZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2 && cmds.size()!=3 ) { - cerr<<"Syntax: pdnsutil create-zone ZONE [nsname]"< 2 ? DNSName(cmds.at(2)) : DNSName()); } -static int createSecondaryZone(vector& cmds) +static int createSecondaryZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 3 ) { - cerr << "Syntax: pdnsutil create-secondary-zone ZONE primary-ip [primary-ip..]" << endl; - return 0; + return usage(synopsis); } UtilBackend B; // NOLINT(readability-identifier-length) DomainInfo di; // NOLINT(readability-identifier-length) @@ -3132,11 +3104,10 @@ static int createSecondaryZone(vector& cmds) return EXIT_SUCCESS; } -static int changeSecondaryZonePrimary(vector& cmds) +static int changeSecondaryZonePrimary(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 3 ) { - cerr << "Syntax: pdnsutil change-secondary-zone-primary ZONE primary-ip [primary-ip..]" << endl; - return 0; + return usage(synopsis); } UtilBackend B; // NOLINT(readability-identifier-length) DomainInfo di; // NOLINT(readability-identifier-length) @@ -3160,61 +3131,55 @@ static int changeSecondaryZonePrimary(vector& cmds) } } -static int addRecord(vector& cmds) +static int addRecord(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 5) { - cerr<& cmds) +static int addAutoprimary(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 3) { - cerr << "Syntax: pdnsutil add-autoprimary IP NAMESERVER [account]" << endl; - return 0; + return usage(synopsis); } return addAutoPrimary(cmds.at(1), cmds.at(2), cmds.size() > 3 ? cmds.at(3) : ""); } -static int removeAutoprimary(vector& cmds) +static int removeAutoprimary(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 3) { - cerr << "Syntax: pdnsutil remove-autoprimary IP NAMESERVER" << endl; - return 0; + return usage(synopsis); } return removeAutoPrimary(cmds.at(1), cmds.at(2)); } -static int listAutoprimaries([[maybe_unused]] vector& cmds) +static int listAutoprimaries([[maybe_unused]] vector& cmds, [[maybe_unused]] const std::string_view synopsis) { return listAutoPrimaries(); } -static int replaceRRSet(vector& cmds) +static int replaceRRSet(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 5) { - cerr<& cmds) +static int deleteRRSet(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 4) { - cerr<<"Syntax: pdnsutil delete-rrset ZONE name type"<& cmds) +static int listZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr<<"Syntax: pdnsutil list-zone ZONE"<& cmds) return listZone(DNSName(cmds.at(1))); } -static int editZone(vector& cmds) +static int editZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr<<"Syntax: pdnsutil edit-zone ZONE"<& cmds) return editZone(DNSName(cmds.at(1)), col); } -static int clearZone(vector& cmds) +static int clearZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 2) { - cerr<<"Syntax: pdnsutil clear-zone ZONE"<& cmds) return clearZone(DNSName(cmds.at(1))); } -static int listKeys(vector& cmds) +static int listKeys(vector& cmds, const std::string_view synopsis) { if(cmds.size() > 2) { - cerr<<"Syntax: pdnsutil list-keys [ZONE]"<& cmds) return listKeys(zname, dk); } -static int loadZone(vector& cmds) +static int loadZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 3) { - cerr<<"Syntax: pdnsutil load-zone ZONE FILENAME [ZONE FILENAME] .."<& cmds) return 0; } -static int secureZone(vector& cmds) +static int secureZone(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 2) { - cerr << "Syntax: pdnsutil secure-zone ZONE"< mustRectify; @@ -3313,11 +3273,10 @@ static int secureZone(vector& cmds) return 0; } -static int secureAllZones(vector& cmds) +static int secureAllZones(vector& cmds, const std::string_view synopsis) { if (cmds.size() >= 2 && !pdns_iequals(cmds.at(1), "increase-serial")) { - cerr << "Syntax: pdnsutil secure-all-zones [increase-serial]"<& cmds) return 0; } -static int setKind(vector& cmds) +static int setKind(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 3) { - cerr<<"Syntax: pdnsutil set-kind ZONE KIND"<& cmds) +static int setOptionsJson(vector& cmds, const std::string_view synopsis) { if (cmds.size() != 3) { - cerr << "Syntax: pdnsutil set-options ZONE VALUE" << endl; - return EXIT_FAILURE; + return usage(synopsis); } // Verify json @@ -3386,16 +3343,13 @@ static int setOptionsJson(vector& cmds) return setZoneOptionsJson(zone, cmds.at(2)); } -static int setOption(vector& cmds) +static int setOption(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 5 || (cmds.size() > 5 && (cmds.at(3) != "group"))) { - cerr << "Syntax: pdnsutil set-option ZONE [producer|consumer] [coo|unique|group] VALUE [VALUE ...]1" << endl; - return EXIT_FAILURE; + return usage(synopsis); } - if ((cmds.at(2) != "producer" && cmds.at(2) != "consumer") || (cmds.at(3) != "coo" && cmds.at(3) != "unique" && cmds.at(3) != "group")) { - cerr << "Syntax: pdnsutil set-option ZONE [producer|consumer] [coo|unique|group] VALUE [VALUE ...]" << endl; - return EXIT_FAILURE; + return usage(synopsis); } DNSName zone(cmds.at(1)); @@ -3409,35 +3363,32 @@ static int setOption(vector& cmds) return setZoneOption(zone, cmds.at(2), cmds.at(3), values); } -static int setCatalog(vector& cmds) +static int setCatalog(vector& cmds, const std::string_view synopsis) { - if (cmds.size() != 3) { - cerr << "Syntax: pdnsutil set-catalog ZONE CATALOG" << endl; - return 0; + if (cmds.size() < 2) { + return usage(synopsis); } DNSName zone(cmds.at(1)); DNSName catalog; // Create an empty DNSName() - if (!cmds.at(2).empty()) { + if (cmds.size() > 2 && !cmds.at(2).empty()) { catalog = DNSName(cmds.at(2)); } return setZoneCatalog(zone, catalog); } -static int setAccount(vector& cmds) +static int setAccount(vector& cmds, const std::string_view synopsis) { if(cmds.size() != 3) { - cerr<<"Syntax: pdnsutil set-account ZONE ACCOUNT"<& cmds) +static int setNsec3(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 2) { - cerr<<"Syntax: pdnsutil set-nsec3 ZONE 'params' [narrow]"< 2 ? cmds.at(2) : "1 0 0 -"; bool narrow = cmds.size() > 3 && cmds.at(3) == "narrow"; @@ -3475,11 +3426,10 @@ static int setNsec3(vector& cmds) return 0; } -static int setPresigned(vector& cmds) +static int setPresigned(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 2) { - cerr<<"Syntax: pdnsutil set-presigned ZONE"<& cmds) return 0; } -static int setPublishCDNSKey(vector& cmds) +static int setPublishCDNSKey(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 2 || (cmds.size() == 3 && cmds.at(2) != "delete")) { - cerr<<"Syntax: pdnsutil set-publish-cdnskey ZONE [delete]"<& cmds) return 0; } -static int setPublishCDs(vector& cmds) +static int setPublishCDs(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 2) { - cerr<<"Syntax: pdnsutil set-publish-cds ZONE [DIGESTALGOS]"<& cmds) return 0; } -static int unsetPresigned(vector& cmds) +static int unsetPresigned(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 2) { - cerr<<"Syntax: pdnsutil unset-presigned ZONE"<& cmds) return 0; } -static int unsetPublishCDNSKey(vector& cmds) +static int unsetPublishCDNSKey(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 2) { - cerr<<"Syntax: pdnsutil unset-publish-cdnskey ZONE"<& cmds) return 0; } -static int unsetPublishCDs(vector& cmds) +static int unsetPublishCDs(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 2) { - cerr<<"Syntax: pdnsutil unset-publish-cds ZONE"<& cmds) return 0; } -static int hashZoneRecord(vector& cmds) +static int hashZoneRecord(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 3) { - cerr<<"Syntax: pdnsutil hash-zone-record ZONE RNAME"<& cmds) return 0; } -static int unsetNSec3(vector& cmds) +static int unsetNSec3(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 2) { - cerr<<"Syntax: pdnsutil unset-nsec3 ZONE"<& cmds) return 0; } -static int exportZoneKey(vector& cmds) +static int exportZoneKey(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 3) { - cerr << "Syntax: pdnsutil export-zone-key ZONE KEY-ID" << endl; - return 1; + return usage(synopsis); } DNSSECKeeper dk; //NOLINT(readability-identifier-length) @@ -3619,11 +3561,10 @@ static int exportZoneKey(vector& cmds) return 0; } -static int exportZoneKeyPEM(vector& cmds) +static int exportZoneKeyPEM(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 3) { - cerr << "Syntax: pdnsutil export-zone-key-pem ZONE KEY-ID" << endl; - return 1; + return usage(synopsis); } DNSSECKeeper dk; //NOLINT(readability-identifier-length) @@ -3634,21 +3575,19 @@ static int exportZoneKeyPEM(vector& cmds) return 0; } -static int increaseSerial(vector& cmds) +static int increaseSerial(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 2) { - cerr << "Syntax: pdnsutil increase-serial ZONE" << endl; - return 1; + return usage(synopsis); } DNSSECKeeper dk; //NOLINT(readability-identifier-length) return increaseSerial(DNSName(cmds.at(1)), dk); } -static int importZoneKeyPEM(vector& cmds) +static int importZoneKeyPEM(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 4) { - cerr << "Syntax: pdnsutil import-zone-key-pem ZONE FILE ALGORITHM {ksk|zsk}" << endl; - return 1; + return usage(synopsis); } DNSName zone(cmds.at(1)); @@ -3706,11 +3645,10 @@ static int importZoneKeyPEM(vector& cmds) return checkZoneKey(dk, zone, id); } -static int importZoneKey(vector& cmds) +static int importZoneKey(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 3) { - cerr<<"Syntax: pdnsutil import-zone-key ZONE FILE [ksk|zsk] [active|inactive]"<& cmds) return checkZoneKey(dk, zone, id); } -static int expotZoneDNSKey(vector& cmds) +static int exportZoneDNSKey(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 3) { - cerr<<"Syntax: pdnsutil export-zone-dnskey ZONE KEY-ID"<& cmds) return 0; } -static int generateZoneKey(vector& cmds) +static int generateZoneKey(vector& cmds, const std::string_view synopsis) { if(cmds.size() < 2 ) { - cerr << "Syntax: pdnsutil generate-zone-key zsk|ksk [rsasha1|rsasha1-nsec3-sha1|rsasha256|rsasha512|ecdsa256|ecdsa384"; -#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO_ED25519) - cerr << "|ed25519"; -#endif -#if defined(HAVE_LIBCRYPTO_ED448) - cerr << "|ed448"; -#endif - cerr << "] [bits]"<& cmds) return 0; } -static int generateTSIGKey(vector& cmds) +static int generateTSIGKey(vector& cmds, const std::string_view synopsis) { - string usage = "Syntax: " + cmds.at(0) + " name (hmac-md5|hmac-sha1|hmac-sha224|hmac-sha256|hmac-sha384|hmac-sha512)"; if (cmds.size() < 3) { - cerr << usage << endl; - return 0; + return usage(synopsis); } DNSName name(cmds.at(1)); DNSName algo(cmds.at(2)); @@ -3875,11 +3802,10 @@ static int generateTSIGKey(vector& cmds) return 0; } -static int importTSIGKey(vector& cmds) +static int importTSIGKey(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 4) { - cerr << "Syntax: " << cmds.at(0) << " name algorithm key" << endl; - return 0; + return usage(synopsis); } DNSName name(cmds.at(1)); string algo = cmds.at(2); @@ -3896,11 +3822,10 @@ static int importTSIGKey(vector& cmds) return 0; } -static int deleteTSIGKey(vector& cmds) +static int deleteTSIGKey(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 2) { - cerr << "Syntax: " << cmds.at(0) << " name" << endl; - return 0; + return usage(synopsis); } DNSName name(cmds.at(1)); @@ -3915,7 +3840,7 @@ static int deleteTSIGKey(vector& cmds) return 0; } -static int listTSIGKeys([[maybe_unused]] vector& cmds) +static int listTSIGKeys([[maybe_unused]] vector& cmds, [[maybe_unused]] const std::string_view synopsis) { std::vector keys; UtilBackend B("default"); // NOLINT(readability-identifier-length) @@ -3927,12 +3852,11 @@ static int listTSIGKeys([[maybe_unused]] vector& cmds) return 0; } -static int activateTSIGKey(vector& cmds) +static int activateTSIGKey(vector& cmds, const std::string_view synopsis) { string metaKey; if (cmds.size() < 4) { - cerr << "Syntax: " << cmds.at(0) << " ZONE NAME {primary|secondary}" << endl; - return 0; + return usage(synopsis); } DNSName zname(cmds.at(1)); string name = cmds.at(2); @@ -3943,8 +3867,7 @@ static int activateTSIGKey(vector& cmds) metaKey = "AXFR-MASTER-TSIG"; } else { - cerr << "Invalid parameter '" << cmds.at(3) << "', expected primary or secondary type" << endl; - return 1; + return usage(synopsis); } UtilBackend B("default"); // NOLINT(readability-identifier-length) DomainInfo di; // NOLINT(readability-identifier-length) @@ -3977,12 +3900,11 @@ static int activateTSIGKey(vector& cmds) return 0; } -static int deactivateTSIGKey(vector& cmds) +static int deactivateTSIGKey(vector& cmds, const std::string_view synopsis) { string metaKey; if (cmds.size() < 4) { - cerr << "Syntax: " << cmds.at(0) << " ZONE NAME {primary|secondary|producer|consumer}" << endl; - return 0; + return usage(synopsis); } DNSName zname(cmds.at(1)); string name = cmds.at(2); @@ -3993,8 +3915,7 @@ static int deactivateTSIGKey(vector& cmds) metaKey = "AXFR-MASTER-TSIG"; } else { - cerr << "Invalid parameter '" << cmds.at(3) << "', expected primary or secondary type" << endl; - return 1; + return usage(synopsis); } UtilBackend B("default"); // NOLINT(readability-identifier-length) @@ -4027,13 +3948,12 @@ static int deactivateTSIGKey(vector& cmds) return 0; } -static int getMeta(vector& cmds) +static int getMeta(vector& cmds, const std::string_view synopsis) { - UtilBackend B("default"); // NOLINT(readability-identifier-length) if (cmds.size() < 2) { - cerr << "Syntax: " << cmds.at(0) << " zone [kind kind ..]" << endl; - return 1; + return usage(synopsis); } + UtilBackend B("default"); // NOLINT(readability-identifier-length) DNSName zone(cmds.at(1)); vector keys; @@ -4064,11 +3984,10 @@ static int getMeta(vector& cmds) return 0; } -static int setMeta(vector& cmds) +static int setMeta(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 3) { - cerr << "Syntax: " << cmds.at(0) << " ZONE KIND [VALUE VALUE ..]" << endl; - return 1; + return usage(synopsis); } DNSName zone(cmds.at(1)); string kind = cmds.at(2); @@ -4088,15 +4007,15 @@ static int setMeta(vector& cmds) } #ifdef HAVE_P11KIT1 // { -static int HSMAssign(vector& cmds) + +static int HSMAssign(vector& cmds, const std::string_view synopsis) { DNSCryptoKeyEngine::storvector_t storvect; DomainInfo di; // NOLINT(readability-identifier-length) std::vector keys; if (cmds.size() < 9) { - std::cout << "Usage: pdnsutil hsm assign ZONE ALGORITHM {ksk|zsk} MODULE TOKEN PIN LABEL (PUBLABEL)" << std::endl; - return 1; + return usage(synopsis); } UtilBackend B("default"); // NOLINT(readability-identifier-length) @@ -4174,11 +4093,10 @@ static int HSMAssign(vector& cmds) return 0; } -static int HSMCreateKey(vector& cmds) +static int HSMCreateKey(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 4) { - cerr << "Usage: pdnsutil hsm create-key ZONE KEY-ID [BITS]" << endl; - return 1; + return usage(synopsis); } UtilBackend B("default"); // NOLINT(readability-identifier-length) DomainInfo di; // NOLINT(readability-identifier-length) @@ -4229,21 +4147,48 @@ static int HSMCreateKey(vector& cmds) cerr << "Key of size " << dke->getBits() << " created" << std::endl; return 0; } + +struct HSMcommandDispatcher { + int (*handler)(std::vector&, const std::string_view); + const std::string_view synopsis; // one-line command synopsis + const std::string_view help; // short description, may span multiple lines, + // every line starts with a tab for indent +}; + +// We use std::map instead of std::unordered_map here in order to be able +// to display a sorted list of commands (the main command set does not +// need this because the help display groups them in a temporary map). +// clang-format off +static const std::map HSMcommands{ + {"assign", {HSMAssign, + "hsm assign ZONE ALGORITHM {ksk|zsk} MODULE SLOT PIN LABEL [PUBLABEL]", + "\tAssign a Hardware Signing Module to a ZONE"}}, + {"create-key", {HSMCreateKey, + "hsm create-key ZONE KEY_ID [BITS]", + "\tcreate a key using Hardware Signing Module for ZONE (use assign first);\n" + "\tBITS defaults to 2048"}} +}; +// clang-format on + #endif // } -static int HSM([[maybe_unused]] vector& cmds) +static int HSM([[maybe_unused]] vector& cmds, [[maybe_unused]] const std::string_view synopsis) { #ifdef HAVE_P11KIT1 - if (cmds.size() < 2) { - cerr << "Missing sub-command for pdnsutil hsm"<< std::endl; + if (cmds.size() < 2 || cmds.at(1) == "help") { + for (const auto& iter : HSMcommands) { + cout << iter.second.synopsis << endl; + cout << iter.second.help << endl; + } return 0; } - if (cmds.at(1) == "assign") { - return HSMAssign(cmds); - } - if (cmds.at(1) == "create-key") { - return HSMCreateKey(cmds); + + const auto iter = HSMcommands.find(cmds.at(1)); + if (iter != HSMcommands.end()) { + const auto dispatcher = iter->second; + return dispatcher.handler(cmds, dispatcher.synopsis); } + cerr<<"Unknown hsm sub-command '"<& cmds) #endif } -static int B2BMigrate(vector& cmds) +static int B2BMigrate(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 3) { - cerr << "Usage: b2b-migrate OLD NEW" << endl; - return 1; + return usage(synopsis); } if (cmds.at(1) == cmds.at(2)) { @@ -4387,11 +4331,10 @@ static int B2BMigrate(vector& cmds) return 0; } -static int backendCmd(vector& cmds) +static int backendCmd(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 3) { - cerr<<"Usage: backend-cmd BACKEND CMD [CMD..]"< matchingBackend{nullptr}; @@ -4415,11 +4358,10 @@ static int backendCmd(vector& cmds) return 0; } -static int backendLookup(vector& cmds) +static int backendLookup(vector& cmds, const std::string_view synopsis) { if (cmds.size() < 3) { - cerr << "Usage: backend-lookup BACKEND NAME [TYPE [CLIENT-IP-SUBNET]]" << endl; - return 1; + return usage(synopsis); } std::unique_ptr matchingBackend{nullptr}; @@ -4474,96 +4416,328 @@ static int backendLookup(vector& cmds) return 0; } -static const std::unordered_map&)>> commands{ - {"activate-tsig-key", {true, activateTSIGKey}}, - {"activate-zone-key", {true, activateZoneKey}}, - {"add-autoprimary", {true, addAutoprimary}}, - {"add-meta", {true, setMeta}}, - {"add-record", {true, addRecord}}, - {"add-zone-key", {true, addZoneKey}}, - {"b2b-migrate", {true, B2BMigrate}}, - {"backend-cmd", {true, backendCmd}}, - {"backend-lookup", {true, backendLookup}}, - {"bench-db", {true, benchDb}}, - {"change-secondary-zone-primary", {true, changeSecondaryZonePrimary}}, - {"check-all-zones", {true, checkAllZones}}, - {"check-zone", {true, checkZone}}, - {"clear-zone", {true, clearZone}}, - {"create-bind-db", {true, createBindDb}}, - {"create-secondary-zone", {true, createSecondaryZone}}, - {"create-zone", {true, createZone}}, - {"deactivate-tsig-key", {true, deactivateTSIGKey}}, - {"deactivate-zone-key", {true, deactivateZoneKey}}, - {"delete-rrset", {true, deleteRRSet}}, - {"delete-tsig-key", {true, deleteTSIGKey}}, - {"delete-zone", {true, deleteZone}}, - {"disable-dnssec", {true, disableDNSSEC}}, - {"edit-zone", {true, editZone}}, - {"export-zone-dnskey", {true, expotZoneDNSKey}}, - {"export-zone-ds", {true, exportZoneDS}}, - {"export-zone-key", {true, exportZoneKey}}, - {"export-zone-key-pem", {true, exportZoneKeyPEM}}, - {"generate-tsig-key", {true, generateTSIGKey}}, - {"generate-zone-key", {true, generateZoneKey}}, - {"get-meta", {true, getMeta}}, - {"hash-password", {true, hashPassword}}, - {"hash-zone-record", {true, hashZoneRecord}}, - {"hsm", {true, HSM}}, - {"import-tsig-key", {true, importTSIGKey}}, - {"import-zone-key", {true, importZoneKey}}, - {"import-zone-key-pem", {true, importZoneKeyPEM}}, - {"increase-serial", {true, increaseSerial}}, - {"ipdecrypt", {false, ipEncrypt}}, - {"ipencrypt", {false, ipEncrypt}}, - {"list-algorithms", {false, listAlgorithms}}, - {"list-all-zones", {true, listAllZones}}, - {"list-autoprimaries", {true, listAutoprimaries}}, - {"list-keys", {true, listKeys}}, - {"list-member-zones", {true, listMemberZones}}, - {"list-tsig-keys", {true, listTSIGKeys}}, - {"list-zone", {true, listZone}}, - {"lmdb-get-backend-version", {false, lmdbGetBackendVersion}}, - {"load-zone", {true, loadZone}}, - {"publish-zone-key", {true, publishZoneKey}}, - {"raw-lua-from-content", {true, rawLuaFromContent}}, - {"rectify-all-zones", {true, rectifyAllZones}}, - {"rectify-zone", {true, rectifyZone}}, - {"remove-autoprimary", {true, removeAutoprimary}}, - {"remove-zone-key", {true, removeZoneKey}}, - {"replace-rrset", {true, replaceRRSet}}, - {"secure-all-zones", {true, secureAllZones}}, - {"secure-zone", {true, secureZone}}, - {"set-account", {true, setAccount}}, - {"set-catalog", {true, setCatalog}}, - {"set-kind", {true, setKind}}, - {"set-meta", {true, setMeta}}, - {"set-nsec3", {true, setNsec3}}, - {"set-option", {true, setOption}}, - {"set-options-json", {true, setOptionsJson}}, - {"set-presigned", {true, setPresigned}}, - {"set-publish-cdnskey", {true, setPublishCDNSKey}}, - {"set-publish-cds", {true, setPublishCDs}}, - {"show-zone", {true, showZone}}, - {"test-algorithm", {false, testAlgorithm}}, - {"test-algorithms", {false, testAlgorithms}}, - {"test-all-zones", {true, testAllZones}}, - {"test-schema", {true, testSchema}}, - {"test-speed", {true, testSpeed}}, - {"test-zone", {true, testZone}}, - {"unpublish-zone-key", {true, unpublishZoneKey}}, - {"unset-nsec3", {true, unsetNSec3}}, - {"unset-presigned", {true, unsetPresigned}}, - {"unset-publish-cdnskey", {true, unsetPublishCDNSKey}}, - {"unset-publish-cds", {true, unsetPublishCDs}}, - {"verify-crypto", {true, verifyCrypto}}, - {"zonemd-verify-file", {true, zonemdVerifyFile}} +enum commandGroup { + GROUP_AUTOPRIMARY, + GROUP_CATALOG, + GROUP_META, + GROUP_ZONE, + GROUP_RRSET, + GROUP_DNSSEC, + GROUP_CDNSKEY, + GROUP_NSEC3, + GROUP_TSIGKEY, + GROUP_ZONEKEY, + GROUP_OTHER, + GROUP_LAST, + GROUP_FIRST = GROUP_AUTOPRIMARY, +}; + +static const std::array groupNames{ + "Autoprimary", + "Catalog", + "Metadata", + "Zone", + "RRSet", + "DNSSEC", + "CDS/CDNSKEY", + "NSEC3", + "TSIG key", + "Zone key", + "Other" +}; + +struct commandDispatcher { + bool requiresInitialization; // need to invoke reportAllTypes() before handler + int (*handler)(std::vector&, const std::string_view); + commandGroup group; + const std::string_view synopsis; // one-line command synopsis + const std::string_view help; // short description, may span multiple lines, + // every line starts with a tab for indent +}; + +// clang-format off +static const std::unordered_map commands{ + {"activate-tsig-key", {true, activateTSIGKey, GROUP_TSIGKEY, + "activate-tsig-key ZONE NAME {primary|secondary|producer|consumer}", + "\tEnable TSIG authenticated AXFR using the key NAME for ZONE"}}, + {"activate-zone-key", {true, activateZoneKey, GROUP_ZONEKEY, + "activate-zone-key ZONE KEY_ID", + "\tActivate the key with key id KEY_ID in ZONE"}}, + {"add-autoprimary", {true, addAutoprimary, GROUP_AUTOPRIMARY, + "add-autoprimary IP NAMESERVER [ACCOUNT]", + "\tAdd a new autoprimary "}}, + {"add-meta", {true, setMeta, GROUP_META, + "add-meta ZONE KIND VALUE [VALUE...]", + "\tAdd zone metadata, this adds to the existing KIND"}}, + {"add-record", {true, addRecord, GROUP_ZONE, + R"(add-record ZONE NAME TYPE [TTL] "CONTENT" ["CONTENT"...])", + "\tAdd one or more records to ZONE"}}, + {"add-zone-key", {true, addZoneKey, GROUP_ZONEKEY, + "add-zone-key ZONE [zsk|ksk] [BITS] [active|inactive] [published|unpublished]\n" + " [rsasha1|rsasha1-nsec3-sha1|rsasha256|rsasha512|ecdsa256|ecdsa384" +#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO_ED25519) + "|ed25519" +#endif +#if defined(HAVE_LIBCRYPTO_ED448) + "|ed448" +#endif + "]", + "\tAdd a ZSK or KSK to zone with specific algorithm and size in bits.\n" + "\tIf zsk or ksk is omitted, defaults to zsk"}}, + {"b2b-migrate", {true, B2BMigrate, GROUP_OTHER, + "b2b-migrate OLD NEW", + "\tMove all data from one backend to another"}}, + {"backend-cmd", {true, backendCmd, GROUP_OTHER, + "backend-cmd BACKEND CMD [CMD...]", + "\tPerform one or more backend commands"}}, + {"backend-lookup", {true, backendLookup, GROUP_OTHER, + "backend-lookup BACKEND NAME [[TYPE] CLIENT_IP_SUBNET]", + "\tPerform a backend lookup of NAME, TYPE (defaulting to ANY) and\n" + "\tCLIENT_IP_SUBNET"}}, + {"bench-db", {true, benchDb, GROUP_OTHER, + "bench-db [FILENAME]", + "\tBenchmark database backend with queries, one zone per line"}}, + {"change-secondary-zone-primary", {true, changeSecondaryZonePrimary, GROUP_ZONE, + "change-secondary-zone-primary ZONE PRIMARY_IP [PRIMARY_IP...]", + "\tChange secondary zone ZONE primary IP address(es) to PRIMARY_IP"}}, + {"check-all-zones", {true, checkAllZones, GROUP_ZONE, + "check-all-zones [exit-on-error]", + "\tCheck all zones for correctness. Use exit-on-error to exit immediately\n" + "\tupon finding the first error in any zone"}}, + {"check-zone", {true, checkZone, GROUP_ZONE, + "check-zone ZONE", + "\tCheck a zone for correctness"}}, + {"clear-zone", {true, clearZone, GROUP_ZONE, + "clear-zone ZONE", + "\tClear all records of a zone, but keep everything else"}}, + {"create-bind-db", {true, createBindDb, GROUP_DNSSEC, + "create-bind-db FILENAME", + "\tCreate DNSSEC db for BIND backend (bind-dnssec-db)"}}, + {"create-secondary-zone", {true, createSecondaryZone, GROUP_ZONE, + "create-secondary-zone ZONE PRIMARY_IP [PRIMARY_IP...]", + "\tCreate secondary zone ZONE with primary IP address(es) PRIMARY_IP"}}, + {"create-zone", {true, createZone, GROUP_ZONE, + "create-zone ZONE [NSNAME]", + "\tCreate empty zone ZONE"}}, + {"deactivate-tsig-key", {true, deactivateTSIGKey, GROUP_TSIGKEY, + "deactivate-tsig-key ZONE NAME {primary|secondary|producer|consumer}", + "\tDisable TSIG authenticated AXFR using the key NAME for ZONE"}}, + {"deactivate-zone-key", {true, deactivateZoneKey, GROUP_ZONEKEY, + "deactivate-zone-key ZONE KEY_ID", + "\tDeactivate the key with key id KEY_ID in ZONE"}}, + {"delete-rrset", {true, deleteRRSet, GROUP_RRSET, + "delete-rrset ZONE NAME TYPE", + "\tDelete named RRSET from ZONE"}}, + {"delete-tsig-key", {true, deleteTSIGKey, GROUP_TSIGKEY, + "delete-tsig-key NAME", + "\tDelete TSIG key (warning: will not unmap key!)"}}, + {"delete-zone", {true, deleteZone, GROUP_ZONE, + "delete-zone ZONE", + "\tDelete zone ZONE"}}, + {"disable-dnssec", {true, disableDNSSEC, GROUP_DNSSEC, + "disable-dnssec ZONE", + "\tDeactivate all keys and unset PRESIGNED in ZONE"}}, + {"edit-zone", {true, editZone, GROUP_ZONE, + "edit-zone ZONE", + "\tEdit zone contents using $EDITOR"}}, + {"export-zone-dnskey", {true, exportZoneDNSKey, GROUP_CDNSKEY, + "export-zone-dnskey ZONE KEY_ID", + "\tExport the public DNSKEY with the given ID to stdout"}}, + {"export-zone-ds", {true, exportZoneDS, GROUP_CDNSKEY, + "export-zone-ds ZONE", + "\tExport all KSK DS records for ZONE to stdout"}}, + {"export-zone-key", {true, exportZoneKey, GROUP_ZONEKEY, + "export-zone-key ZONE KEY_ID", + "\tExport the private key with the given ID to stdout"}}, + {"export-zone-key-pem", {true, exportZoneKeyPEM, GROUP_ZONEKEY, + "export-zone-key-pem ZONE KEY_ID", + "\tExport the private key with the given ID to stdout in PEM format"}}, + {"generate-tsig-key", {true, generateTSIGKey, GROUP_TSIGKEY, + "generate-tsig-key NAME ALGORITHM", + "\tGenerate new TSIG key.\n" + "\tALGORITHM is one of hmac-{md5,sha1,sha224,sha256,sha384,sha512}"}}, + {"generate-zone-key", {true, generateZoneKey, GROUP_ZONEKEY, + "generate-zone-key {zsk|ksk} [ALGORITHM] [BITS]", + "\tGenerate a ZSK or KSK to stdout with specified ALGORITHM and BITS"}}, + {"get-meta", {true, getMeta, GROUP_META, + "get-meta ZONE [KIND...]", + "\tGet zone metadata. If no KIND is given, lists all known"}}, + {"hash-password", {true, hashPassword, GROUP_OTHER, + "hash-password [WORK FACTOR]", + "\tAsk for a plaintext password or API key and output a salted and hashed\n" + "\tversion"}}, + {"hash-zone-record", {true, hashZoneRecord, GROUP_NSEC3, + "hash-zone-record ZONE RNAME", + "\tCalculate the NSEC3 hash for RNAME in ZONE"}}, + {"hsm", {true, HSM, GROUP_OTHER, +#ifdef HAVE_P11KIT1 + "hsm SUB_COMMAND [ARGUMENTS...]", + "\tHardware Signing Module-related commands.\n" + "\tUse \"hsm help\" to get more information" +#else + "", "" // not functional so hide it +#endif + }}, + {"import-tsig-key", {true, importTSIGKey, GROUP_TSIGKEY, + "import-tsig-key NAME ALGORITHM KEY", + "\tImport TSIG key"}}, + {"import-zone-key", {true, importZoneKey, GROUP_ZONEKEY, + "import-zone-key ZONE FILE [active|inactive] [ksk|zsk] [published|unpublished]", + "\tImport from a file a private key, ZSK or KSK; defaults to KSK, active\n" + "\tand published"}}, + {"import-zone-key-pem", {true, importZoneKeyPEM, GROUP_ZONEKEY, + "import-zone-key-pem ZONE FILE ALGORITHM [ksk|zsk]}", + "\tImport a private key from a PEM file\n"}}, + {"increase-serial", {true, increaseSerial, GROUP_ZONE, + "increase-serial ZONE", + "\tIncreases the SOA-serial by 1. Uses SOA-EDIT"}}, + {"ipdecrypt", {false, ipEncrypt, GROUP_OTHER, + "ipdecrypt IP_ADDRESS PASSPHRASE_OR_KEY [key]", + "\tDecrypt IP address using passphrase or base64 key"}}, + {"ipencrypt", {false, ipEncrypt, GROUP_OTHER, + "ipencrypt IP_ADDRESS PASSPHRASE_OR_KEY [key]", + "\tEncrypt IP address using passphrase or base64 key"}}, + {"list-algorithms", {false, listAlgorithms, GROUP_DNSSEC, + "list-algorithms [with-backend]", + "\tList all DNSSEC algorithms supported, optionally also listing the\n" + "\tcryptographic library used"}}, + {"list-all-zones", {true, listAllZones, GROUP_ZONE, + "list-all-zones [primary|secondary|native|producer|consumer]", + "\tList all active zone names.\n" + "\tUse --verbose (-v) to include disabled or empty zones"}}, + {"list-autoprimaries", {true, listAutoprimaries, GROUP_AUTOPRIMARY, + "list-autoprimaries", + "\tList all autoprimaries"}}, + {"list-keys", {true, listKeys, GROUP_DNSSEC, + "list-keys [ZONE]", + "\tList DNSSEC keys for ZONE.\n" + "\tWhen ZONE is unset, display keys for all active zones"}}, + {"list-member-zones", {true, listMemberZones, GROUP_ZONE, + "list-member-zones CATALOG", + "\tList all members of catalog zone CATALOG"}}, + {"list-tsig-keys", {true, listTSIGKeys, GROUP_TSIGKEY, + "list-tsig-keys", + "\tList all TSIG keys"}}, + {"list-zone", {true, listZone, GROUP_ZONE, + "list-zone ZONE", + "\tList zone contents"}}, + {"lmdb-get-backend-version", {false, lmdbGetBackendVersion, GROUP_OTHER, + "lmdb-get-backend-version", + "\tGet schema version supported by backend"}}, + {"load-zone", {true, loadZone, GROUP_ZONE, + "load-zone ZONE FILENAME [ZONE FILENAME]...", + "\tLoad ZONE from FILENAME, possibly creating zone or atomically replacing\n" + "\tcontents; --verbose or -v will also include the keys for disabled or\n" + "\tempty zones"}}, + {"publish-zone-key", {true, publishZoneKey, GROUP_ZONEKEY, + "publish-zone-key ZONE KEY_ID", + "\tPublish the zone key with key id KEY_ID in ZONE"}}, + {"raw-lua-from-content", {true, rawLuaFromContent, GROUP_OTHER, + "raw-lua-from-content TYPE CONTENT", + "\tDisplay record contents in a form suitable for dnsdist's\n" + "\t`SpoofRawAction`"}}, + {"rectify-all-zones", {true, rectifyAllZones, GROUP_DNSSEC, + "rectify-all-zones [quiet]", + "\tRectify all zones. Optionally quiet output with errors only"}}, + {"rectify-zone", {true, rectifyZone, GROUP_DNSSEC, + "rectify-zone ZONE [ZONE...]", + "\tFix up DNSSEC fields (order, auth)"}}, + {"remove-autoprimary", {true, removeAutoprimary, GROUP_AUTOPRIMARY, + "remove-autoprimary IP NAMESERVER", + "\tRemove an autoprimary"}}, + {"remove-zone-key", {true, removeZoneKey, GROUP_ZONEKEY, + "remove-zone-key ZONE KEY_ID", + "\tRemove key with KEY_ID from ZONE"}}, + {"replace-rrset", {true, replaceRRSet, GROUP_RRSET, + R"(replace-rrset ZONE NAME TYPE [TTL] "CONTENT" ["CONTENT"...])", + "\tReplace named RRSET from ZONE"}}, + {"secure-all-zones", {true, secureAllZones, GROUP_DNSSEC, + "secure-all-zones [increase-serial]", + "\tSecure all zones without keys"}}, + {"secure-zone", {true, secureZone, GROUP_DNSSEC, + "secure-zone ZONE [ZONE...]", + "\tAdd DNSSEC to zone ZONE"}}, + {"set-account", {true, setAccount, GROUP_ZONE, + "set-account ZONE ACCOUNT", + "\tChange the account (owner) of ZONE to ACCOUNT"}}, + {"set-catalog", {true, setCatalog, GROUP_CATALOG, + "set-catalog ZONE [CATALOG]", + "\tChange the catalog of ZONE to CATALOG, or removes ZONE from its current\n" + "\tcatalog if no catalog provided"}}, + {"set-kind", {true, setKind, GROUP_ZONE, + "set-kind ZONE KIND", + "\tChange the kind of ZONE to KIND (primary, secondary, native, producer,\n" + "\tor consumer)"}}, + {"set-meta", {true, setMeta, GROUP_META, + "set-meta ZONE KIND [VALUE...]", + "\tSet zone metadata, replacing all existing records of KIND, optionally\n" + "\tproviding a value. An omitted value clears the metadata"}}, + {"set-nsec3", {true, setNsec3, GROUP_NSEC3, + "set-nsec3 ZONE ['PARAMS' [narrow]]", + "\tEnable NSEC3 with PARAMS (default: '1 0 0 -'). Optionally narrow"}}, + {"set-option", {true, setOption, GROUP_ZONE, + "set-option ZONE [producer|consumer] [coo|unique|group] VALUE [VALUE...]", + "\tSet or remove an option for ZONE. Providing an empty value removes the\n" + "\toption"}}, + {"set-options-json", {true, setOptionsJson, GROUP_ZONE, + "set-options-json ZONE JSONFILE", + "\tChange the options of ZONE to JSONFILE"}}, + {"set-presigned", {true, setPresigned, GROUP_DNSSEC, + "set-presigned ZONE", + "\tUse presigned RRSIGs from storage"}}, + {"set-publish-cdnskey", {true, setPublishCDNSKey, GROUP_CDNSKEY, + "set-publish-cdnskey ZONE [delete]", + "\tEnable sending CDNSKEY responses for ZONE. Add 'delete' to publish\n" + "\ta CDNSKEY with a DNSSEC delete algorithm"}}, + {"set-publish-cds", {true, setPublishCDs, GROUP_CDNSKEY, + "set-publish-cds ZONE [DIGESTALGOS]", + "\tEnable sending CDS responses for ZONE, using DIGESTALGOS as signature\n" + "\talgorithms; DIGESTALGOS should be a comma-separated list of numbers,\n" + "\t(default: '2')"}}, + {"show-zone", {true, showZone, GROUP_DNSSEC, + "show-zone ZONE", + "\tShow DNSSEC (public) key details about a zone"}}, + {"test-algorithm", {false, testAlgorithm, GROUP_OTHER, + "test-algorithm ALGONUM", + ""}}, // TODO: short help line + {"test-algorithms", {false, testAlgorithms, GROUP_OTHER, + "", ""}}, // TODO: synopsis and short help line + {"test-all-zones", {true, testAllZones, GROUP_ZONE, + "", ""}}, // TODO: synopsis and short help line + {"test-schema", {true, testSchema, GROUP_OTHER, + "test-schema ZONE", + "\tTest DB schema - will create ZONE"}}, + {"test-speed", {true, testSpeed, GROUP_OTHER, + "test-speed ZONE NUM_CORES", ""}}, // TODO: short help line + {"test-zone", {true, testZone, GROUP_ZONE, + "", ""}}, // TODO: synopsis and short help line + {"unpublish-zone-key", {true, unpublishZoneKey, GROUP_ZONEKEY, + "unpublish-zone-key ZONE KEY_ID", + "\tUnpublish the zone key with key id KEY_ID in ZONE"}}, + {"unset-nsec3", {true, unsetNSec3, GROUP_NSEC3, + "unset-nsec3 ZONE", + "\tSwitch ZONE back to NSEC"}}, + {"unset-presigned", {true, unsetPresigned, GROUP_DNSSEC, + "unset-presigned ZONE", + "\tStop using presigned RRSIGs on ZONE"}}, + {"unset-publish-cdnskey", {true, unsetPublishCDNSKey, GROUP_CDNSKEY, + "unset-publish-cdnskey ZONE", + "\tDisable sending CDNSKEY responses for ZONE"}}, + {"unset-publish-cds", {true, unsetPublishCDs, GROUP_CDNSKEY, + "unset-publish-cds ZONE", + "\tDisable sending CDS responses for ZONE"}}, + {"verify-crypto", {true, verifyCrypto, GROUP_OTHER, + "verify-crypto FILENAME", ""}}, // TODO: short help line + {"zonemd-verify-file", {true, zonemdVerifyFile, GROUP_ZONE, + "zonemd-verify-file ZONE FILENAME", + "\tValidate ZONEMD for ZONE"}} }; +// clang-format on -// NOLINTNEXTLINE(readability-function-cognitive-complexity): TODO Clean this function up. int main(int argc, char** argv) try { - po::options_description desc("Allowed options"); + po::options_description desc("Common options"); desc.add_options() ("help,h", "produce help message") ("version", "show version") @@ -4593,122 +4767,29 @@ try } if (cmds.empty() || g_vm.count("help") != 0 || cmds.at(0) == "help") { - cout << "Usage: \npdnsutil [options] [params ..]\n" + cout << "Usage:\npdnsutil [options] [params...]\n" << endl; - cout << "Commands:" << endl; - cout << "activate-tsig-key ZONE NAME {primary|secondary|producer|consumer}" << endl; - cout << " Enable TSIG authenticated AXFR using the key NAME for ZONE" << endl; - cout << "activate-zone-key ZONE KEY-ID Activate the key with key id KEY-ID in ZONE" << endl; - cout << "add-record ZONE NAME TYPE [ttl] content" << endl; - cout << " [content..] Add one or more records to ZONE" << endl; - cout << "add-autoprimary IP NAMESERVER [account]" << endl; - cout << " Add a new autoprimary " << endl; - cout << "remove-autoprimary IP NAMESERVER Remove an autoprimary" << endl; - cout << "list-autoprimaries List all autoprimaries" << endl; - cout << "add-zone-key ZONE {zsk|ksk} [BITS] [active|inactive] [published|unpublished]" << endl; - cout << " [rsasha1|rsasha1-nsec3-sha1|rsasha256|rsasha512|ecdsa256|ecdsa384"; -#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO_ED25519) - cout << "|ed25519"; -#endif -#if defined(HAVE_LIBCRYPTO_ED448) - cout << "|ed448"; -#endif - cout << "]" << endl; - cout << " Add a ZSK or KSK to zone and specify algo&bits" << endl; - cout << "backend-cmd BACKEND CMD [CMD..] Perform one or more backend commands" << endl; - cout << "backend-lookup BACKEND NAME [[TYPE] CLIENT-IP-SUBNET]" << endl; - cout << " Perform a backend lookup of NAME, TYPE and CLIENT-IP-SUBNET" << endl; - cout << "b2b-migrate OLD NEW Move all data from one backend to another" << endl; - cout << "bench-db [filename] Bench database backend with queries, one zone per line" << endl; - cout << "check-zone ZONE Check a zone for correctness" << endl; - cout << "check-all-zones [exit-on-error] Check all zones for correctness. Set exit-on-error to exit immediately" << endl; - cout << " after finding an error in a zone." << endl; - cout << "clear-zone ZONE Clear all records of a zone, but keep everything else" << endl; - cout << "create-bind-db FNAME Create DNSSEC db for BIND backend (bind-dnssec-db)" << endl; - cout << "create-secondary-zone ZONE primary-ip [primary-ip..]" << endl; - cout << " Create secondary zone ZONE with primary IP address primary-ip" << endl; - cout << "change-secondary-zone-primary ZONE primary-ip [primary-ip..]" << endl; - cout << " Change secondary zone ZONE primary IP address to primary-ip" << endl; - cout << "create-zone ZONE [nsname] Create empty zone ZONE" << endl; - cout << "deactivate-tsig-key ZONE NAME {primary|secondary}" << endl; - cout << " Disable TSIG authenticated AXFR using the key NAME for ZONE" << endl; - cout << "deactivate-zone-key ZONE KEY-ID Deactivate the key with key id KEY-ID in ZONE" << endl; - cout << "delete-rrset ZONE NAME TYPE Delete named RRSET from zone" << endl; - cout << "delete-tsig-key NAME Delete TSIG key (warning! will not unmap key!)" << endl; - cout << "delete-zone ZONE Delete the zone" << endl; - cout << "disable-dnssec ZONE Deactivate all keys and unset PRESIGNED in ZONE" << endl; - cout << "edit-zone ZONE Edit zone contents using $EDITOR" << endl; - cout << "export-zone-dnskey ZONE KEY-ID Export to stdout the public DNSKEY described" << endl; - cout << "export-zone-ds ZONE Export to stdout all KSK DS records for ZONE" << endl; - cout << "export-zone-key ZONE KEY-ID Export to stdout the private key described" << endl; - cout << "export-zone-key-pem ZONE KEY-ID Export to stdout in PEM the private key described" << endl; - cout << "generate-tsig-key NAME ALGORITHM Generate new TSIG key" << endl; - cout << "generate-zone-key {zsk|ksk} [ALGORITHM] [BITS]" << endl; - cout << " Generate a ZSK or KSK to stdout with specified ALGORITHM and BITS" << endl; - cout << "get-meta ZONE [KIND ...] Get zone metadata. If no KIND given, lists all known" << endl; - cout << "hash-password [WORK FACTOR] Ask for a plaintext password or api key and output a hashed and salted version" << endl; - cout << "hash-zone-record ZONE RNAME Calculate the NSEC3 hash for RNAME in ZONE" << endl; -#ifdef HAVE_P11KIT1 - cout << "hsm assign ZONE ALGORITHM {ksk|zsk} MODULE SLOT PIN LABEL" << endl << - " Assign a hardware signing module to a ZONE" << endl; - cout << "hsm create-key ZONE KEY-ID [BITS] Create a key using hardware signing module for ZONE (use assign first)" << endl; - cout << " BITS defaults to 2048" << endl; -#endif - cout << "increase-serial ZONE Increases the SOA-serial by 1. Uses SOA-EDIT" << endl; - cout << "import-tsig-key NAME ALGORITHM KEY Import TSIG key" << endl; - cout << "import-zone-key ZONE FILE Import from a file a private key, ZSK or KSK" << endl; - cout << " [active|inactive] [ksk|zsk] [published|unpublished] Defaults to KSK, active and published" << endl; - cout << "import-zone-key-pem ZONE FILE Import a private key from a PEM file" << endl; - cout << " ALGORITHM {ksk|zsk}" << endl; - cout << "ipdecrypt IP passphrase/key [key] Decrypt IP address using passphrase or base64 key" << endl; - cout << "ipencrypt IP passphrase/key [key] Encrypt IP address using passphrase or base64 key" << endl; - cout << "load-zone ZONE FILE Load ZONE from FILE, possibly creating zone or atomically" << endl; - cout << " replacing contents" << endl; - cout << "list-algorithms [with-backend] List all DNSSEC algorithms supported, optionally also listing the crypto library used" << endl; - cout << "list-keys [ZONE] List DNSSEC keys for ZONE. When ZONE is unset, display all keys for all active zones" << endl; - cout << " --verbose or -v will also include the keys for disabled or empty zones" << endl; - cout << "list-zone ZONE List zone contents" << endl; - cout << "list-all-zones [primary|secondary|native|producer|consumer]" << endl; - cout << " List all active zone names. --verbose or -v will also include disabled or empty zones" << endl; - cout << "list-member-zones CATALOG List all members of catalog zone CATALOG" << endl; - - cout << "list-tsig-keys List all TSIG keys" << endl; - cout << "publish-zone-key ZONE KEY-ID Publish the zone key with key id KEY-ID in ZONE" << endl; - cout << "rectify-zone ZONE [ZONE ..] Fix up DNSSEC fields (order, auth)" << endl; - cout << "rectify-all-zones [quiet] Rectify all zones. Optionally quiet output with errors only" << endl; - cout << "remove-zone-key ZONE KEY-ID Remove key with KEY-ID from ZONE" << endl; - cout << "replace-rrset ZONE NAME TYPE [ttl] Replace named RRSET from zone" << endl; - cout << " content [content..]" << endl; - cout << "secure-all-zones [increase-serial] Secure all zones without keys" << endl; - cout << "secure-zone ZONE [ZONE ..] Add DNSSEC to zone ZONE" << endl; - cout << "set-kind ZONE KIND Change the kind of ZONE to KIND (primary, secondary, native, producer, consumer)" << endl; - cout << "set-options-json ZONE JSON Change the options of ZONE to JSON" << endl; - cout << "set-option ZONE Set or remove an option for ZONE Providing an empty value removes an option" << endl; - cout << " [producer|consumer]" << endl; - cout << " [coo|unique|group] VALUE" << endl; - cout << " [VALUE ...]" << endl; - cout << "set-catalog ZONE CATALOG Change the catalog of ZONE to CATALOG. Setting CATALOG to an empty "" removes ZONE from the catalog it is in" << endl; - cout << "set-account ZONE ACCOUNT Change the account (owner) of ZONE to ACCOUNT" << endl; - cout << "set-nsec3 ZONE ['PARAMS' [narrow]] Enable NSEC3 with PARAMS. Optionally narrow" << endl; - cout << "set-presigned ZONE Use presigned RRSIGs from storage" << endl; - cout << "set-publish-cdnskey ZONE [delete] Enable sending CDNSKEY responses for ZONE. Add 'delete' to publish a CDNSKEY with a" << endl; - cout << " DNSSEC delete algorithm" << endl; - cout << "set-publish-cds ZONE [DIGESTALGOS] Enable sending CDS responses for ZONE, using DIGESTALGOS as signature algorithms" << endl; - cout << " DIGESTALGOS should be a comma separated list of numbers, it is '2' by default" << endl; - cout << "add-meta ZONE KIND VALUE Add zone metadata, this adds to the existing KIND" << endl; - cout << " [VALUE ...]" << endl; - cout << "set-meta ZONE KIND [VALUE] [VALUE] Set zone metadata, optionally providing a value. *No* value clears meta" << endl; - cout << " Note - this will replace all metadata records of KIND!" << endl; - cout << "show-zone ZONE Show DNSSEC (public) key details about a zone" << endl; - cout << "unpublish-zone-key ZONE KEY-ID Unpublish the zone key with key id KEY-ID in ZONE" << endl; - cout << "unset-nsec3 ZONE Switch back to NSEC" << endl; - cout << "unset-presigned ZONE No longer use presigned RRSIGs" << endl; - cout << "unset-publish-cdnskey ZONE Disable sending CDNSKEY responses for ZONE" << endl; - cout << "unset-publish-cds ZONE Disable sending CDS responses for ZONE" << endl; - cout << "test-schema ZONE Test DB schema - will create ZONE" << endl; - cout << "raw-lua-from-content TYPE CONTENT Display record contents in a form suitable for dnsdist's `SpoofRawAction`" << endl; - cout << "zonemd-verify-file ZONE FILE Validate ZONEMD for ZONE" << endl; - cout << "lmdb-get-backend-version Get schema version supported by backend" << endl; + + for (unsigned int group = GROUP_FIRST; group < GROUP_LAST; ++group) { + cout << groupNames.at(group) << " commands:" << endl << endl; + std::map groupCommands; + for (const auto& iter : commands) { + if (iter.second.group == group) { + groupCommands.insert(iter); + } + } + for (const auto& iter : groupCommands) { + auto synopsis = iter.second.synopsis; + auto help = iter.second.help; + if (synopsis.empty() || help.empty()) { // Don't mention "HSM" command if support not compiled in + continue; + } + cout << synopsis << endl; + cout << help << endl; + } + cout << endl; + } + cout << desc << endl; return 0; @@ -4718,11 +4799,11 @@ try const auto iter = commands.find(cmds.at(0)); if (iter != commands.end()) { - auto [initRequired, handler] = iter->second; - if (initRequired) { + const auto dispatcher = iter->second; + if (dispatcher.requiresInitialization) { reportAllTypes(); } - return handler(cmds); + return dispatcher.handler(cmds, dispatcher.synopsis); } cerr << "Unknown command '" << cmds.at(0) << "'" << endl;