From 8ec0f4b062159d68799ccae7984fc09627e31a2a Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Mon, 7 Apr 2025 15:25:15 +0200 Subject: [PATCH] Add views-related commands to pdnsutil. --- pdns/pdnsutil.cc | 181 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 176 insertions(+), 5 deletions(-) diff --git a/pdns/pdnsutil.cc b/pdns/pdnsutil.cc index e7c59df3f3..12463a79a6 100644 --- a/pdns/pdnsutil.cc +++ b/pdns/pdnsutil.cc @@ -1,6 +1,7 @@ #include "dnsname.hh" #include "dnsparser.hh" #include "dnsrecords.hh" +#include "iputils.hh" #include "qtype.hh" #include #ifdef HAVE_CONFIG_H @@ -122,6 +123,7 @@ static void loadMainConfig(const std::string& configdir) ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0"; ::arg().set("max-include-depth", "Maximum nested $INCLUDE depth when loading a zone from a file")="20"; ::arg().setSwitch("upgrade-unknown-types","Transparently upgrade known TYPExxx records. Recommended to keep off, except for PowerDNS upgrades until data sources are cleaned up")="no"; + ::arg().setSwitch("views", "Enable views (variants) of zones, for backends which support them") = "no"; ::arg().laxFile(configname); if(!::arg()["load-modules"].empty()) { @@ -1554,6 +1556,11 @@ static int loadZone(const ZoneName& zone, const string& fname) { cerr << "Zone '" << zone << "' was not created." << endl; return EXIT_FAILURE; } + if (zone.hasVariant() && (B.getCapabilities() & DNSBackend::CAP_VIEWS) == 0) { + cerr << "None of the configured backends support views." << endl; + cerr << "Zone '" << zone << "' was not created." << endl; + return EXIT_FAILURE; + } cerr<<"Creating '"<(), ""); @@ -1612,6 +1619,11 @@ static int createZone(const ZoneName &zone, const DNSName& nsname) { cerr << "Zone '" << zone << "' was not created." << endl; return EXIT_FAILURE; } + if (zone.hasVariant() && (B.getCapabilities() & DNSBackend::CAP_VIEWS) == 0) { + cerr << "None of the configured backends support views." << endl; + cerr << "Zone '" << zone << "' was not created." << endl; + return EXIT_FAILURE; + } DNSResourceRecord rr; rr.qname = zone.operator const DNSName&(); @@ -1641,7 +1653,7 @@ static int createZone(const ZoneName &zone, const DNSName& nsname) { cerr<<"Creating empty zone '"<(), ""); if(!B.getDomainInfo(zone, di)) { - cerr << "Zone '" << zone << "' was not created!" << endl; + cerr << "Zone '" << zone << "' was not created." << endl; return EXIT_FAILURE; } @@ -1886,6 +1898,10 @@ static int listAllZones(const std::string_view synopsis, const string &type="") vector domains; B.getAllDomains(&domains, false, g_verbose); + // Sort results, so that domains which have variants will appear + // grouped in the output. + std::sort(domains.begin(), domains.end()); + int count = 0; for (const auto& di: domains) { if (di.kind == kindFilter || kindFilter == -1) { @@ -2176,6 +2192,10 @@ static bool showZone(DNSSECKeeper& dnsseckeeper, const ZoneName& zone, bool expo } if (!exportDS) { cout<<"This is a "<& cmds, const std::string_view syno cerr << "Zone '" << zone << "' was not created." << endl; return EXIT_FAILURE; } + if (zone.hasVariant() && (B.getCapabilities() & DNSBackend::CAP_VIEWS) == 0) { + cerr << "None of the configured backends support views." << endl; + cerr << "Zone '" << zone << "' was not created." << endl; + return EXIT_FAILURE; + } vector primaries; for (unsigned i=2; i < cmds.size(); i++) { primaries.emplace_back(cmds.at(i), 53); @@ -3199,7 +3224,7 @@ static int createSecondaryZone(vector& cmds, const std::string_view syno cerr << "Creating secondary zone '" << zone << "', with primaries '" << comboAddressVecToString(primaries) << "'" << endl; B.createDomain(zone, DomainInfo::Secondary, primaries, ""); if(!B.getDomainInfo(zone, di)) { - cerr << "Zone '" << zone << "' was not created!" << endl; + cerr << "Zone '" << zone << "' was not created." << endl; return EXIT_FAILURE; } return EXIT_SUCCESS; @@ -4359,8 +4384,12 @@ static int B2BMigrate(vector& cmds, const std::string_view synopsis) DNSResourceRecord rr; // NOLINT(readability-identifier-length) cout<<"Processing '"<getCapabilities() & DNSBackend::CAP_VIEWS) == 0) { + cerr << "Target backend does not support views." << endl; + throw PDNSException("Failed to create zone"); + } if (!tgt->createDomain(di.zone, di.kind, di.primaries, di.account)) { - throw PDNSException("Failed to create zone"); + throw PDNSException("Failed to create zone"); } if (!tgt->getDomainInfo(di.zone, di_new)) { throw PDNSException("Failed to create zone"); @@ -4496,7 +4525,23 @@ static int backendLookup(vector& cmds, const std::string_view synopsis) type = DNSRecordContent::TypeToNumber(cmds.at(3)); } - DNSName name{cmds.at(2)}; + ZoneName name{cmds.at(2)}; + domainid_t domain_id{UnknownDomainID}; + + if (name.hasVariant()) { + ZoneName zone(name); + do { + SOAData soa; + if (matchingBackend->getSOA(zone, UnknownDomainID, soa)) { + domain_id = soa.domain_id; + break; + } + } while (zone.chopOff()); + if (domain_id == UnknownDomainID) { + cerr << "Backend found no matching zone" << endl; + return 1; + } + } DNSPacket queryPacket(true); Netmask clientNetmask; @@ -4505,7 +4550,7 @@ static int backendLookup(vector& cmds, const std::string_view synopsis) queryPacket.setRealRemote(clientNetmask); } - matchingBackend->lookup(type, name, -1, &queryPacket); + matchingBackend->lookup(type, name.operator const DNSName&(), domain_id, &queryPacket); bool found = false; DNSZoneRecord resultZoneRecord; @@ -4530,6 +4575,112 @@ static int backendLookup(vector& cmds, const std::string_view synopsis) return 0; } +static int listView(vector& cmds, const std::string_view synopsis) +{ + if (cmds.size() != 2) { + return usage(synopsis); + } + + UtilBackend B("default"); //NOLINT(readability-identifier-length) + + vector ret; + B.viewListZones(cmds.at(1), ret); + + for (const auto& zone : ret) { + cout << zone << endl; + } + return 0; +} + +static int listViews(vector& cmds, const std::string_view synopsis) +{ + if (cmds.size() != 1) { + return usage(synopsis); + } + + UtilBackend B("default"); //NOLINT(readability-identifier-length) + + vector ret; + B.viewList(ret); + + for (const auto& view : ret) { + cout << view << endl; + } + return 0; +} + +static int viewAddZone(vector& cmds, const std::string_view synopsis) +{ + if (cmds.size() < 3) { + return usage(synopsis); + } + + UtilBackend B("default"); //NOLINT(readability-identifier-length) + + string view{cmds.at(1)}; + ZoneName zone{cmds.at(2)}; + if (!B.viewAddZone(view, zone)) { + cerr<<"Operation failed."<& cmds, const std::string_view synopsis) +{ + if (cmds.size() < 3) { + return usage(synopsis); + } + + UtilBackend B("default"); //NOLINT(readability-identifier-length) + + string view{cmds.at(1)}; + ZoneName zone{cmds.at(2)}; + if (!B.viewDelZone(view, zone)) { + cerr<<"Operation failed."<& cmds, const std::string_view synopsis) +{ + if (cmds.empty()) { + return usage(synopsis); + } + + UtilBackend B("default"); //NOLINT(readability-identifier-length) + + vector > ret; + + B.networkList(ret); + + for (auto &[net, view] : ret) { + cout<& cmds, const std::string_view synopsis) +{ + if (cmds.size() < 2) { + return usage(synopsis); + } + + UtilBackend B("default"); //NOLINT(readability-identifier-length) + + Netmask net{cmds.at(1)}; + string view{}; + if (cmds.size() > 2) { + view = cmds.at(2); + } + if (!B.networkSet(net, view)) { + cerr<<"Operation failed."< groupNames{ "NSEC3", "TSIG key", "Zone key", + "Views", "Other" }; @@ -4729,9 +4882,18 @@ static const std::unordered_map commands{ {"list-member-zones", {true, listMemberZones, GROUP_ZONE, "list-member-zones CATALOG", "\tList all members of catalog zone CATALOG"}}, + {"list-networks", {true, listNetwork, GROUP_VIEWS, + "list-networks", + "\tList all defined networks with their chosen views"}}, {"list-tsig-keys", {true, listTSIGKeys, GROUP_TSIGKEY, "list-tsig-keys", "\tList all TSIG keys"}}, + {"list-view", {true, listView, GROUP_VIEWS, + "list-view VIEW", + "\tList all zones within VIEW"}}, + {"list-views", {true, listViews, GROUP_VIEWS, + "list-view", + "\tList all view names"}}, {"list-zone", {true, listZone, GROUP_ZONE, "list-zone ZONE", "\tList zone contents"}}, @@ -4786,6 +4948,9 @@ static const std::unordered_map commands{ "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-network", {true, setNetwork, GROUP_VIEWS, + "set-network NET [VIEW]", + "\tSet the view for a network, or delete if no view argument."}}, {"set-nsec3", {true, setNsec3, GROUP_NSEC3, "set-nsec3 ZONE ['PARAMS' [narrow]]", "\tEnable NSEC3 with PARAMS (default: '1 0 0 -'). Optionally narrow"}}, @@ -4838,6 +5003,12 @@ static const std::unordered_map commands{ "\tDisable sending CDS responses for ZONE"}}, {"verify-crypto", {true, verifyCrypto, GROUP_OTHER, "verify-crypto FILENAME", ""}}, // TODO: short help line + {"view-add-zone", {true, viewAddZone, GROUP_VIEWS, + "view-add-zone VIEW ZONE..VARIANT", + "\tAdd a zone variant to a view"}}, + {"view-del-zone", {true, viewDelZone, GROUP_VIEWS, + "view-del-zone VIEW ZONE..VARIANT", + "\tRemove a zone variant from a view"}}, {"zonemd-verify-file", {true, zonemdVerifyFile, GROUP_ZONE, "zonemd-verify-file ZONE FILENAME", "\tValidate ZONEMD for ZONE"}} -- 2.47.2