From e8984b87b1da9efe8ea2da57cf5faa627fa8d987 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 12 Feb 2019 15:28:41 +0100 Subject: [PATCH 001/127] sholder.hh: pragma once --- pdns/sholder.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/pdns/sholder.hh b/pdns/sholder.hh index 5a57ddc59c9e..75837c30f146 100644 --- a/pdns/sholder.hh +++ b/pdns/sholder.hh @@ -19,6 +19,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once #include #include #include From 559b6c9331eac5bd419eb31db41e3063c25e0254 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 12 Feb 2019 15:44:44 +0100 Subject: [PATCH 002/127] rec: Implement options to not throttle servers --- pdns/pdns_recursor.cc | 23 +++++++++++++++++++ pdns/recursordist/docs/settings.rst | 34 ++++++++++++++++++++++++++++ pdns/recursordist/test-syncres_cc.cc | 2 ++ pdns/syncres.cc | 9 ++++++-- pdns/syncres.hh | 4 ++++ 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index f4842e9e7c8d..749c6750a490 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -237,6 +237,10 @@ unsigned int g_numThreads; uint16_t g_outgoingEDNSBufsize; bool g_logRPZChanges{false}; +// Used in the Syncres to not throttle certain servers +GlobalStateHolder g_dontThrottleNames; +GlobalStateHolder g_dontThrottleNetmasks; + #define LOCAL_NETS "127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10" #define LOCAL_NETS_INVERSE "!127.0.0.0/8, !10.0.0.0/8, !100.64.0.0/10, !169.254.0.0/16, !192.168.0.0/16, !172.16.0.0/12, !::1/128, !fc00::/7, !fe80::/10" // Bad Nets taken from both: @@ -3715,6 +3719,23 @@ static int serviceMain(int argc, char*argv[]) g_statisticsInterval = ::arg().asNum("statistics-interval"); + { + SuffixMatchNode dontThrottleNames; + vector parts; + stringtok(parts, ::arg()["dont-throttle-names"]); + for (const auto &p : parts) { + dontThrottleNames.add(DNSName(p)); + } + g_dontThrottleNames.setState(dontThrottleNames); + + NetmaskGroup dontThrottleNetmasks; + stringtok(parts, ::arg()["dont-throttle-netmasks"]); + for (const auto &p : parts) { + dontThrottleNetmasks.addMask(Netmask(p)); + } + g_dontThrottleNetmasks.setState(dontThrottleNetmasks); + } + #ifdef SO_REUSEPORT g_reusePort = ::arg().mustDo("reuseport"); #endif @@ -4219,6 +4240,8 @@ int main(int argc, char **argv) ::arg().set("max-tcp-clients","Maximum number of simultaneous TCP clients")="128"; ::arg().set("server-down-max-fails","Maximum number of consecutive timeouts (and unreachables) to mark a server as down ( 0 => disabled )")="64"; ::arg().set("server-down-throttle-time","Number of seconds to throttle all queries to a server after being marked as down")="60"; + ::arg().set("dont-throttle-names", "Do not throttle nameservers with this name or suffix")=""; + ::arg().set("dont-throttle-netmasks", "Do not throttle nameservers with this IP netmask")=""; ::arg().set("hint-file", "If set, load root hints from this file")=""; ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache")="1000000"; ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory")="3600"; diff --git a/pdns/recursordist/docs/settings.rst b/pdns/recursordist/docs/settings.rst index 5d44334fdbc4..6e225f36fc5c 100644 --- a/pdns/recursordist/docs/settings.rst +++ b/pdns/recursordist/docs/settings.rst @@ -268,6 +268,40 @@ Operate in the background. Which domains we only accept delegations from (a Verisign special). +.. _setting-dont-throttle-names: + +``dont-throttle-names`` +---------------------------- +.. versionadded:: 4.2.0 + +- Comma separated list of domain-names +- Default: (empty) + +When an authotitative server does not answer a query or sends a reply the recursor does lot like, it is throttled. +Any servers' name suffix-matching the supplied names will never be throttled. + +.. warning:: + Most servers on the internet do not respond for a good reason (overloaded or unreachable), ``dont-throttle-names`` could make this load on the upstream server even higher, resulting in further service degredation. + +.. _setting-dont-throttle-netmasks: + +``dont-throttle-netmasks`` +---------------------------- +.. versionadded:: 4.2.0 + +- Comma separated list of netmasks +- Default: (empty) + +When an authotitative server does not answer a query or sends a reply the recursor does lot like, it is throttled. +Any servers matching the supplied netmasks will never be throttled. + +This can come in handy on lossy networks when forwarding, where the same server is configured multiple times (e.g. with ``forward-zones-recurse=example.com=192.0.2.1;192.0.2.1``). +By default, the PowerDNS Recursor would throttle the "first" server on a timeout and hence not retry the "second" one. +In this case, ``dont-throttle-netmasks`` could be set to ``192.0.2.1``. + +.. warning:: + Most servers on the internet do not respond for a good reason (overloaded or unreachable), ``dont-throttle-netmasks`` could make this load on the upstream server even higher, resulting in further service degredation. + .. _setting-disable-packetcache: ``disable-packetcache`` diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index eb38f362c57a..bcec3a4f181a 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -17,6 +17,8 @@ RecursorStats g_stats; GlobalStateHolder g_luaconfs; +GlobalStateHolder g_dontThrottleNames; +GlobalStateHolder g_dontThrottleNetmasks; thread_local std::unique_ptr t_RC{nullptr}; unsigned int g_numThreads = 1; bool g_lowercaseOutgoing = false; diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 52d3e1b659e2..2bc62e4b7b41 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -2693,7 +2693,12 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, LOG(prefix<check(nsName) || dontThrottleNetmasks->match(remoteIP))) { + // don't account for resource limits, they are our own fault + // And don't throttle when the IP address is on the dontThrottleNetmasks list or the name is part of dontThrottleNames t_sstorage.nsSpeeds[nsName.empty()? DNSName(remoteIP.toStringWithPort()) : nsName].submit(remoteIP, 1000000, &d_now); // 1 sec // code below makes sure we don't filter COM or the root @@ -2707,7 +2712,7 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, t_sstorage.throttle.throttle(d_now.tv_sec, boost::make_tuple(remoteIP, qname, qtype.getCode()), 60, 100); } else { - // timeout + // timeout, 10 seconds or 5 queries t_sstorage.throttle.throttle(d_now.tv_sec, boost::make_tuple(remoteIP, qname, qtype.getCode()), 10, 5); } } diff --git a/pdns/syncres.hh b/pdns/syncres.hh index 53825ac81ed7..ce44ba24b1ca 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -49,6 +49,7 @@ #include "ednssubnet.hh" #include "filterpo.hh" #include "negcache.hh" +#include "sholder.hh" #ifdef HAVE_CONFIG_H #include "config.h" @@ -58,6 +59,9 @@ #include #endif +extern GlobalStateHolder g_dontThrottleNames; +extern GlobalStateHolder g_dontThrottleNetmasks; + class RecursorLua4; typedef map< From 0c366a8e8183228b86f715d8dd9dbbaf49f7d3e9 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Wed, 13 Feb 2019 11:07:43 +0100 Subject: [PATCH 003/127] rec: Add rec_control getters for dont-throttle-* --- pdns/rec_channel.hh | 4 ++++ pdns/rec_channel_rec.cc | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/pdns/rec_channel.hh b/pdns/rec_channel.hh index 0f1dd7973e7f..8f590340a8cc 100644 --- a/pdns/rec_channel.hh +++ b/pdns/rec_channel.hh @@ -29,8 +29,12 @@ #include #include "iputils.hh" #include "dnsname.hh" +#include "sholder.hh" #include +extern GlobalStateHolder g_dontThrottleNames; +extern GlobalStateHolder g_dontThrottleNetmasks; + /** this class is used both to send and answer channel commands to the PowerDNS Recursor */ class RecursorControlChannel { diff --git a/pdns/rec_channel_rec.cc b/pdns/rec_channel_rec.cc index 0259c6875832..a7fdab2ec712 100644 --- a/pdns/rec_channel_rec.cc +++ b/pdns/rec_channel_rec.cc @@ -1288,6 +1288,16 @@ static string* nopFunction() return new string("pong\n"); } +string getDontThrottleNames() { + auto dtn = g_dontThrottleNames.getLocal(); + return dtn->toString() + "\n"; +} + +string getDontThrottleNetmasks() { + auto dtn = g_dontThrottleNetmasks.getLocal(); + return dtn->toString() + "\n"; +} + string RecursorControlParser::getAnswer(const string& question, RecursorControlParser::func_t** command) { *command=nop; @@ -1315,6 +1325,8 @@ string RecursorControlParser::getAnswer(const string& question, RecursorControlP "dump-throttlemap dump the contents of the throttle to the named file\n" "get [key1] [key2] .. get specific statistics\n" "get-all get all statistics\n" +"get-dont-throttle-names get the list of names that are not allowed to be throttled\n" +"get-dont-throttle-netmasks get the list of netmasks that are not allowed to be throttled\n" "get-ntas get all configured Negative Trust Anchors\n" "get-tas get all configured Trust Anchors\n" "get-parameter [key1] [key2] .. get configuration parameters\n" @@ -1540,5 +1552,13 @@ string RecursorControlParser::getAnswer(const string& question, RecursorControlP if (cmd=="set-dnssec-log-bogus") return doSetDnssecLogBogus(begin, end); + if (cmd == "get-dont-throttle-names") { + return getDontThrottleNames(); + } + + if (cmd == "get-dont-throttle-netmasks") { + return getDontThrottleNetmasks(); + } + return "Unknown command '"+cmd+"', try 'help'\n"; } From c4ce5418419ff696d9d5d42a7c88141783777eb3 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Wed, 13 Feb 2019 12:15:44 +0100 Subject: [PATCH 004/127] Add rec_control setters for dont-throttle-* --- pdns/rec_channel_rec.cc | 86 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/pdns/rec_channel_rec.cc b/pdns/rec_channel_rec.cc index a7fdab2ec712..850cf935aeae 100644 --- a/pdns/rec_channel_rec.cc +++ b/pdns/rec_channel_rec.cc @@ -1298,6 +1298,81 @@ string getDontThrottleNetmasks() { return dtn->toString() + "\n"; } +template +string addDontThrottleNames(T begin, T end) { + if (begin == end) { + return "No names specified, keeping existing list\n"; + } + vector toAdd; + while (begin != end) { + try { + auto d = DNSName(*begin); + toAdd.push_back(d); + } + catch(const std::exception &e) { + return "Problem parsing '" + *begin + "': "+ e.what() + ", nothing added\n"; + } + begin++; + } + + string ret = "Added"; + auto dnt = g_dontThrottleNames.getCopy(); + bool first = true; + for (auto const &d : toAdd) { + if (!first) { + ret += ","; + } + first = false; + ret += " " + d.toLogString(); + dnt.add(d); + } + + g_dontThrottleNames.setState(dnt); + + ret += " to the list of nameservers that may not be throttled"; + g_log< +string addDontThrottleNetmasks(T begin, T end) { + if (begin == end) { + return "No netmasks specified, keeping existing list\n"; + } + vector toAdd; + while (begin != end) { + try { + auto n = Netmask(*begin); + toAdd.push_back(n); + } + catch(const std::exception &e) { + return "Problem parsing '" + *begin + "': "+ e.what() + ", nothing added\n"; + } + catch(const PDNSException &e) { + return "Problem parsing '" + *begin + "': "+ e.reason + ", nothing added\n"; + } + begin++; + } + + string ret = "Added"; + auto dnt = g_dontThrottleNetmasks.getCopy(); + bool first = true; + for (auto const &t : toAdd) { + if (!first) { + ret += ","; + } + first = false; + ret += " " + t.toString(); + dnt.addMask(t); + } + + g_dontThrottleNetmasks.setState(dnt); + + ret += " to the list of nameserver netmasks that may not be throttled"; + g_log< Date: Wed, 13 Feb 2019 16:10:56 +0100 Subject: [PATCH 005/127] SuffixMatchTree: add remove() functions --- pdns/dnsname.hh | 43 +++++++++++++++++++++++++++++++++++++++++ pdns/test-dnsname_cc.cc | 10 ++++++++++ 2 files changed, 53 insertions(+) diff --git a/pdns/dnsname.hh b/pdns/dnsname.hh index f2d9eee21c19..f55d6cbf6881 100644 --- a/pdns/dnsname.hh +++ b/pdns/dnsname.hh @@ -286,6 +286,39 @@ struct SuffixMatchTree } } + void remove(const DNSName &name) const + { + remove(name.getRawLabels()); + } + + /* Removes the node at `labels`, also make sure that no empty + * children will be left behind in memory + */ + void remove(std::vector labels) const + { + SuffixMatchTree smt(*labels.rbegin()); + auto child = children.find(smt); + if (child == children.end()) { + // No subnode found, we're done + return; + } + + // We have found a child + labels.pop_back(); + if (labels.empty()) { + // The child is no longer an endnode + child->endNode = false; + // If the child has no further children, just remove it from the set. + if (child->children.empty()) { + children.erase(child); + } + return; + } + + // We are not at the end, let the child figure out what to do + child->remove(labels); + } + T* lookup(const DNSName& name) const { if(children.empty()) { // speed up empty set @@ -340,6 +373,16 @@ struct SuffixMatchNode d_tree.add(labels, true); } + void remove(const DNSName& name) + { + d_tree.remove(name); + } + + void remove(std::vector labels) + { + d_tree.remove(labels); + } + bool check(const DNSName& dnsname) const { return d_tree.lookup(dnsname) != nullptr; diff --git a/pdns/test-dnsname_cc.cc b/pdns/test-dnsname_cc.cc index d003b8ddcc0c..ac81e6679cf2 100644 --- a/pdns/test-dnsname_cc.cc +++ b/pdns/test-dnsname_cc.cc @@ -507,6 +507,11 @@ BOOST_AUTO_TEST_CASE(test_suffixmatch) { smn.add(net); BOOST_CHECK(smn.check(examplenet)); BOOST_CHECK(smn.check(net)); + + // Remove .net and check that example.net still exists + smn.remove(net); + BOOST_CHECK_EQUAL(smn.check(net), false); + BOOST_CHECK(smn.check(examplenet)); } BOOST_AUTO_TEST_CASE(test_suffixmatch_tree) { @@ -558,6 +563,11 @@ BOOST_AUTO_TEST_CASE(test_suffixmatch_tree) { BOOST_CHECK_EQUAL(*smt.lookup(examplenet), examplenet); BOOST_REQUIRE(smt.lookup(net)); BOOST_CHECK_EQUAL(*smt.lookup(net), net); + + // Remove .net, and check that example.net remains + smt.remove(net); + BOOST_CHECK(smt.lookup(net) == nullptr); + BOOST_CHECK_EQUAL(*smt.lookup(examplenet), examplenet); } From fa7cc8af522b5f94df1044231b7c43b9e3d1a179 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Wed, 13 Feb 2019 16:11:28 +0100 Subject: [PATCH 006/127] rec_control: add clear-dont-throttle-* functions --- pdns/rec_channel_rec.cc | 110 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/pdns/rec_channel_rec.cc b/pdns/rec_channel_rec.cc index 850cf935aeae..d130d3e0a32d 100644 --- a/pdns/rec_channel_rec.cc +++ b/pdns/rec_channel_rec.cc @@ -1373,6 +1373,105 @@ string addDontThrottleNetmasks(T begin, T end) { return ret + "\n"; } +template +string clearDontThrottleNames(T begin, T end) { + if(begin == end) + return "No names specified, doing nothing.\n"; + + if (begin + 1 == end && *begin == "*"){ + SuffixMatchNode smn; + g_dontThrottleNames.setState(smn); + string ret = "Cleared list of nameserver names that may not be throttled"; + g_log< toRemove; + while (begin != end) { + try { + if (*begin == "*") { + return "Please don't mix '*' with other names, nothing removed\n"; + } + toRemove.push_back(DNSName(*begin)); + } + catch (const std::exception &e) { + return "Problem parsing '" + *begin + "': "+ e.what() + ", nothing removed\n"; + } + begin++; + } + + string ret = "Removed"; + bool first = true; + auto dnt = g_dontThrottleNames.getCopy(); + for (const auto &name : toRemove) { + if (!first) { + ret += ","; + } + first = false; + ret += " " + name.toLogString(); + dnt.remove(name); + } + + g_dontThrottleNames.setState(dnt); + + ret += " from the list of nameservers that may not be throttled"; + g_log< +string clearDontThrottleNetmasks(T begin, T end) { + if(begin == end) + return "No netmasks specified, doing nothing.\n"; + + if (begin + 1 == end && *begin == "*"){ + auto nmg = g_dontThrottleNetmasks.getCopy(); + nmg.clear(); + g_dontThrottleNetmasks.setState(nmg); + + string ret = "Cleared list of nameserver addresses that may not be throttled"; + g_log< toRemove; + while (begin != end) { + try { + if (*begin == "*") { + return "Please don't mix '*' with other netmasks, nothing removed\n"; + } + auto n = Netmask(*begin); + toRemove.push_back(n); + } + catch(const std::exception &e) { + return "Problem parsing '" + *begin + "': "+ e.what() + ", nothing added\n"; + } + catch(const PDNSException &e) { + return "Problem parsing '" + *begin + "': "+ e.reason + ", nothing added\n"; + } + begin++; + } + + string ret = "Removed"; + bool first = true; + auto dnt = g_dontThrottleNetmasks.getCopy(); + for (const auto &mask : toRemove) { + if (!first) { + ret += ","; + } + first = false; + ret += " " + mask.toString(); + dnt.deleteMask(mask); + } + + g_dontThrottleNetmasks.setState(dnt); + + ret += " from the list of nameservers that may not be throttled"; + g_log< dump cache contents to the named file\n" @@ -1646,5 +1748,13 @@ string RecursorControlParser::getAnswer(const string& question, RecursorControlP return addDontThrottleNetmasks(begin, end); } + if (cmd == "clear-dont-throttle-names") { + return clearDontThrottleNames(begin, end); + } + + if (cmd == "clear-dont-throttle-netmasks") { + return clearDontThrottleNetmasks(begin, end); + } + return "Unknown command '"+cmd+"', try 'help'\n"; } From 606400e75a0a538c63eb9de82d0b4ea392025776 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Wed, 13 Feb 2019 16:54:12 +0100 Subject: [PATCH 007/127] SuffixMatchNode: implement a proper toString() --- pdns/dnsname.hh | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/pdns/dnsname.hh b/pdns/dnsname.hh index f55d6cbf6881..5c2b6bf0161d 100644 --- a/pdns/dnsname.hh +++ b/pdns/dnsname.hh @@ -348,6 +348,20 @@ struct SuffixMatchTree return child->lookup(labels); } + // Returns all end-nodes, fully qualified (not as separate labels) + std::vector getNodes() const { + std::vector ret; + if (endNode) { + ret.push_back(DNSName(d_name)); + } + for (const auto& child : children) { + auto nodes = child.getNodes(); + for (const auto &node: nodes) { + ret.push_back(node + DNSName(d_name)); + } + } + return ret; + } }; /* Quest in life: serve as a rapid block list. If you add a DNSName to a root SuffixMatchNode, @@ -356,15 +370,10 @@ struct SuffixMatchNode { SuffixMatchNode() {} - std::string d_human; SuffixMatchTree d_tree; void add(const DNSName& dnsname) { - if(!d_human.empty()) - d_human.append(", "); - d_human += dnsname.toString(); - d_tree.add(dnsname, true); } @@ -390,7 +399,16 @@ struct SuffixMatchNode std::string toString() const { - return d_human; + std::string ret; + bool first = true; + for (const auto &n: d_tree.getNodes()) { + if (!first) { + ret += ", "; + } + first = false; + ret += n.toString(); + } + return ret; } }; From 67689ec72ed56b7add16f9c534992155afcbf08f Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Wed, 13 Feb 2019 17:06:04 +0100 Subject: [PATCH 008/127] SuffixMatchTree: add comment on value replacement --- pdns/dnsname.hh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pdns/dnsname.hh b/pdns/dnsname.hh index 5c2b6bf0161d..cb69cac88a95 100644 --- a/pdns/dnsname.hh +++ b/pdns/dnsname.hh @@ -308,6 +308,9 @@ struct SuffixMatchTree if (labels.empty()) { // The child is no longer an endnode child->endNode = false; + // TODO clear d_value for this node as d_value = T() would break for types + // that require initialization + // If the child has no further children, just remove it from the set. if (child->children.empty()) { children.erase(child); From 478eda5a229294c73a3111e91235d2bcfcfe6157 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Wed, 13 Feb 2019 17:23:07 +0100 Subject: [PATCH 009/127] rec_control.1: document dont-throttle commands --- .../docs/manpages/rec_control.1.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pdns/recursordist/docs/manpages/rec_control.1.rst b/pdns/recursordist/docs/manpages/rec_control.1.rst index d6b8dfe80498..37c314527afd 100644 --- a/pdns/recursordist/docs/manpages/rec_control.1.rst +++ b/pdns/recursordist/docs/manpages/rec_control.1.rst @@ -45,6 +45,12 @@ Options Commands -------- +add-dont-throttle-names NAME [NAME...] + Add names for nameserver domains that may not be throttled. + +add-dont-throttle-netmasks NETMASK [NETMASK...] + Add netmasks for nameservers that may not be throttled. + add-nta *DOMAIN* [*REASON*] Add a Negative Trust Anchor for *DOMAIN*, suffixed optionally with *REASON*. @@ -56,6 +62,12 @@ add-ta *DOMAIN* *DSRECORD* current-queries Shows the currently active queries. +clear-dont-throttle-names NAME [NAME...] + Remove names that are not allowed to be throttled. If *NAME* is '*', remove all + +clear-dont-throttle-netmasks NETMASK [NETMASK...] + Remove netmasks that are not allowed to be throttled. If *NETMASK* is '*', remove all + clear-nta *DOMAIN*... Remove Negative Trust Anchor for one or more *DOMAIN*\ s. Set domain to '*' to remove all NTA's. @@ -144,6 +156,12 @@ get *STATISTIC* [*STATISTIC*]... get-all Retrieve all known statistics. +get-dont-throttle-names + Get the list of names that are not allowed to be throttled. + +get-dont-throttle-netmasks + Get the list of netmasks that are not allowed to be throttled. + get-ntas Get a list of the currently configured Negative Trust Anchors. From 68bcc8d0c8413c658bca9b2f815afd64cfb7c21e Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 19 Feb 2019 10:20:04 +0100 Subject: [PATCH 010/127] Add more unit tests for removal from a SuffixMatchTree --- pdns/test-dnsname_cc.cc | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/pdns/test-dnsname_cc.cc b/pdns/test-dnsname_cc.cc index ac81e6679cf2..833f2fc31e43 100644 --- a/pdns/test-dnsname_cc.cc +++ b/pdns/test-dnsname_cc.cc @@ -568,6 +568,61 @@ BOOST_AUTO_TEST_CASE(test_suffixmatch_tree) { smt.remove(net); BOOST_CHECK(smt.lookup(net) == nullptr); BOOST_CHECK_EQUAL(*smt.lookup(examplenet), examplenet); + + smt = SuffixMatchTree(); + smt.add(examplenet, examplenet); + smt.add(net, net); + smt.add(DNSName("news.bbc.co.uk."), DNSName("news.bbc.co.uk.")); + smt.add(apowerdnscom, apowerdnscom); + + smt.remove(DNSName("not-such-entry.news.bbc.co.uk.")); + BOOST_REQUIRE(smt.lookup(DNSName("news.bbc.co.uk."))); + smt.remove(DNSName("news.bbc.co.uk.")); + BOOST_CHECK(smt.lookup(DNSName("news.bbc.co.uk.")) == nullptr); + + smt.remove(net); + BOOST_REQUIRE(smt.lookup(examplenet)); + BOOST_CHECK_EQUAL(*smt.lookup(examplenet), examplenet); + BOOST_CHECK(smt.lookup(net) == nullptr); + + smt.remove(examplenet); + BOOST_CHECK(smt.lookup(net) == nullptr); + BOOST_CHECK(smt.lookup(examplenet) == nullptr); + + smt.add(examplenet, examplenet); + smt.add(net, net); + BOOST_REQUIRE(smt.lookup(examplenet)); + BOOST_CHECK_EQUAL(*smt.lookup(examplenet), examplenet); + BOOST_REQUIRE(smt.lookup(net)); + BOOST_CHECK_EQUAL(*smt.lookup(net), net); + + smt.remove(examplenet); + BOOST_CHECK_EQUAL(*smt.lookup(examplenet), net); + BOOST_CHECK_EQUAL(*smt.lookup(net), net); + smt.remove(examplenet); + BOOST_CHECK_EQUAL(*smt.lookup(examplenet), net); + BOOST_CHECK_EQUAL(*smt.lookup(net), net); + smt.remove(net); + BOOST_CHECK(smt.lookup(net) == nullptr); + BOOST_CHECK(smt.lookup(examplenet) == nullptr); + smt.remove(net); + + size_t count = 0; + smt.visit([apowerdnscom, &count](const SuffixMatchTree& smt) { + count++; + BOOST_CHECK_EQUAL(smt.d_value, apowerdnscom); + }); + BOOST_CHECK_EQUAL(count, 1); + + BOOST_CHECK_EQUAL(*smt.lookup(apowerdnscom), apowerdnscom); + smt.remove(apowerdnscom); + BOOST_CHECK(smt.lookup(apowerdnscom) == nullptr); + + count = 0; + smt.visit([&count](const SuffixMatchTree& smt) { + count++; + }); + BOOST_CHECK_EQUAL(count, 0); } From 15d0cc71ce9c8e8d4586c57c642784dad9ce7ba6 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 19 Feb 2019 11:37:24 +0100 Subject: [PATCH 011/127] rec: pre-calculate string for SuffixMatchNode on change --- pdns/dnsname.hh | 96 +++++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 35 deletions(-) diff --git a/pdns/dnsname.hh b/pdns/dnsname.hh index cb69cac88a95..5533da0f600e 100644 --- a/pdns/dnsname.hh +++ b/pdns/dnsname.hh @@ -371,49 +371,75 @@ struct SuffixMatchTree anything part of that domain will return 'true' in check */ struct SuffixMatchNode { - SuffixMatchNode() - {} - SuffixMatchTree d_tree; + public: + SuffixMatchNode() + {} + SuffixMatchTree d_tree; + + void add(const DNSName& dnsname) + { + d_tree.add(dnsname, true); + d_nodes.insert(dnsname); + updateHuman(); + } - void add(const DNSName& dnsname) - { - d_tree.add(dnsname, true); - } + void add(std::vector labels) + { + d_tree.add(labels, true); + DNSName tmp; + while (!labels.empty()) { + tmp.appendRawLabel(labels.back()); + labels.pop_back(); // This is safe because we have a copy of labels + } + d_nodes.insert(tmp); + updateHuman(); + } - void add(std::vector labels) - { - d_tree.add(labels, true); - } + void remove(const DNSName& name) + { + d_tree.remove(name); + d_nodes.erase(name); + updateHuman(); + } - void remove(const DNSName& name) - { - d_tree.remove(name); - } + void remove(std::vector labels) + { + d_tree.remove(labels); + DNSName tmp; + while (!labels.empty()) { + tmp.appendRawLabel(labels.back()); + labels.pop_back(); // This is safe because we have a copy of labels + } + d_nodes.erase(tmp); + updateHuman(); + } - void remove(std::vector labels) - { - d_tree.remove(labels); - } + bool check(const DNSName& dnsname) const + { + return d_tree.lookup(dnsname) != nullptr; + } - bool check(const DNSName& dnsname) const - { - return d_tree.lookup(dnsname) != nullptr; - } + std::string toString() const + { + return d_human; + } - std::string toString() const - { - std::string ret; - bool first = true; - for (const auto &n: d_tree.getNodes()) { - if (!first) { - ret += ", "; + private: + mutable std::string d_human; + mutable std::set d_nodes; // Only used for string generation + + void updateHuman() { + std::string tmp; + bool first = true; + for (const auto& n : d_nodes) { + if (!first) { + tmp += ", "; + } + first = false; + tmp += n.toString(); } - first = false; - ret += n.toString(); + d_human = tmp; } - return ret; - } - }; std::ostream & operator<<(std::ostream &os, const DNSName& d); From 3f5115d1e74eb9aaa02683e599ce36ab365e8ce9 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Thu, 21 Feb 2019 13:02:04 +0100 Subject: [PATCH 012/127] Don't do O(n) operation needlessly --- pdns/dnsname.hh | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/pdns/dnsname.hh b/pdns/dnsname.hh index 5533da0f600e..ff64e2097f95 100644 --- a/pdns/dnsname.hh +++ b/pdns/dnsname.hh @@ -380,7 +380,6 @@ struct SuffixMatchNode { d_tree.add(dnsname, true); d_nodes.insert(dnsname); - updateHuman(); } void add(std::vector labels) @@ -392,14 +391,12 @@ struct SuffixMatchNode labels.pop_back(); // This is safe because we have a copy of labels } d_nodes.insert(tmp); - updateHuman(); } void remove(const DNSName& name) { d_tree.remove(name); d_nodes.erase(name); - updateHuman(); } void remove(std::vector labels) @@ -411,7 +408,6 @@ struct SuffixMatchNode labels.pop_back(); // This is safe because we have a copy of labels } d_nodes.erase(tmp); - updateHuman(); } bool check(const DNSName& dnsname) const @@ -421,25 +417,21 @@ struct SuffixMatchNode std::string toString() const { - return d_human; - } - - private: - mutable std::string d_human; - mutable std::set d_nodes; // Only used for string generation - - void updateHuman() { - std::string tmp; + std::string ret; bool first = true; for (const auto& n : d_nodes) { if (!first) { - tmp += ", "; + ret += ", "; } first = false; - tmp += n.toString(); + ret += n.toString(); } - d_human = tmp; + return ret; } + + private: + mutable std::string d_human; + mutable std::set d_nodes; // Only used for string generation }; std::ostream & operator<<(std::ostream &os, const DNSName& d); From 6dbf337fefdeb053088293dd2e4efdbd21ae289e Mon Sep 17 00:00:00 2001 From: Kees Monshouwer Date: Wed, 28 Nov 2018 00:04:16 +0100 Subject: [PATCH 013/127] auth: no dnssec processing for non dnssec zones and avoid a lot of isSecuredZone() calls --- pdns/packethandler.cc | 43 ++++++++++++++++++++----------------------- pdns/packethandler.hh | 1 + 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/pdns/packethandler.cc b/pdns/packethandler.cc index 72888474af8c..944583606d91 100644 --- a/pdns/packethandler.cc +++ b/pdns/packethandler.cc @@ -585,9 +585,6 @@ void PacketHandler::emitNSEC3(DNSPacket *r, const SOAData& sd, const NSEC3PARAMR */ void PacketHandler::addNSECX(DNSPacket *p, DNSPacket *r, const DNSName& target, const DNSName& wildcard, const DNSName& auth, int mode) { - if(!p->d_dnssecOk && mode != 5) - return; - NSEC3PARAMRecordContent ns3rc; bool narrow; if(d_dk.getNSEC3PARAM(auth, &ns3rc, &narrow)) { @@ -971,7 +968,6 @@ void PacketHandler::makeNXDomain(DNSPacket* p, DNSPacket* r, const DNSName& targ DNSZoneRecord rr; rr.dr.d_name=sd.qname; rr.dr.d_type=QType::SOA; - rr.dr.d_content=makeSOAContent(sd); rr.dr.d_ttl=min(sd.ttl, sd.default_ttl); rr.signttl=sd.ttl; @@ -980,8 +976,9 @@ void PacketHandler::makeNXDomain(DNSPacket* p, DNSPacket* r, const DNSName& targ rr.auth = 1; r->addRecord(rr); - if(d_dk.isSecuredZone(sd.qname)) + if(d_dnssec) { addNSECX(p, r, target, wildcard, sd.qname, 4); + } r->setRcode(RCode::NXDomain); } @@ -992,7 +989,6 @@ void PacketHandler::makeNOError(DNSPacket* p, DNSPacket* r, const DNSName& targe rr.dr.d_name=sd.qname; rr.dr.d_type=QType::SOA; rr.dr.d_content=makeSOAContent(sd); - rr.dr.d_ttl=sd.ttl; rr.dr.d_ttl=min(sd.ttl, sd.default_ttl); rr.signttl=sd.ttl; rr.domain_id=sd.domain_id; @@ -1000,8 +996,9 @@ void PacketHandler::makeNOError(DNSPacket* p, DNSPacket* r, const DNSName& targe rr.auth = 1; r->addRecord(rr); - if(d_dk.isSecuredZone(sd.qname)) + if(d_dnssec) { addNSECX(p, r, target, wildcard, sd.qname, mode); + } S.ringAccount("noerror-queries",p->qdomain.toLogString()+"/"+p->qtype.getName()); } @@ -1034,20 +1031,15 @@ bool PacketHandler::tryReferral(DNSPacket *p, DNSPacket*r, SOAData& sd, const DN if(!retargeted) r->setA(false); - if(d_dk.isSecuredZone(sd.qname) && !addDSforNS(p, r, sd, rrset.begin()->dr.d_name)) + if(d_dnssec && !addDSforNS(p, r, sd, rrset.begin()->dr.d_name)) { addNSECX(p, r, rrset.begin()->dr.d_name, DNSName(), sd.qname, 1); - + } + return true; } void PacketHandler::completeANYRecords(DNSPacket *p, DNSPacket*r, SOAData& sd, const DNSName &target) { - if(!p->d_dnssecOk) - return; // Don't send dnssec info to non validating resolvers. - - if(!d_dk.isSecuredZone(sd.qname)) - return; - addNSECX(p, r, target, DNSName(), sd.qname, 5); if(sd.qname == p->qdomain) { addDNSKEY(p, r, sd); @@ -1099,9 +1091,10 @@ bool PacketHandler::tryWildcard(DNSPacket *p, DNSPacket*r, SOAData& sd, DNSName r->addRecord(rr); } } - if(d_dk.isSecuredZone(sd.qname) && !nodata) { + if(d_dnssec && !nodata) { addNSECX(p, r, bestmatch, wildcard, sd.qname, 3); } + return true; } @@ -1280,7 +1273,11 @@ DNSPacket *PacketHandler::doQuestion(DNSPacket *p) goto sendit; } DLOG(g_log<qtype.getCode() == QType::ANY && !p->d_dnssecOk && (rr.dr.d_type == QType:: DNSKEY || rr.dr.d_type == QType::NSEC3PARAM)) - continue; // Don't send dnssec info to non validating resolvers. + if (!d_dnssec && p->qtype.getCode() == QType::ANY && (rr.dr.d_type == QType:: DNSKEY || rr.dr.d_type == QType::NSEC3PARAM)) + continue; // Don't send dnssec info. if (rr.dr.d_type == QType::RRSIG) // RRSIGS are added later any way. continue; // TODO: this actually means addRRSig should check if the RRSig is already there @@ -1512,7 +1509,7 @@ DNSPacket *PacketHandler::doQuestion(DNSPacket *p) } if (haveRecords) { - if(p->qtype.getCode() == QType::ANY) + if(d_dnssec && p->qtype.getCode() == QType::ANY) completeANYRecords(p, r, sd, target); } else @@ -1548,7 +1545,7 @@ DNSPacket *PacketHandler::doQuestion(DNSPacket *p) break; } } - if(p->d_dnssecOk) + if(authSet.size()) addRRSigs(d_dk, B, authSet, r->getRRS()); r->wrapup(); // needed for inserting in cache diff --git a/pdns/packethandler.hh b/pdns/packethandler.hh index 0a38735af6f0..fba95ff50cad 100644 --- a/pdns/packethandler.hh +++ b/pdns/packethandler.hh @@ -109,6 +109,7 @@ private: bool d_doIPv6AdditionalProcessing; bool d_doDNAME; bool d_doExpandALIAS; + bool d_dnssec; std::unique_ptr d_pdl; std::unique_ptr d_update_policy_lua; From 49ec5db1de3ad650d37b8735c3da229fc11f1168 Mon Sep 17 00:00:00 2001 From: Kees Monshouwer Date: Wed, 27 Feb 2019 09:34:16 +0100 Subject: [PATCH 014/127] auth: update root direct-ns and ref-3ld expected results and add a direct DS test --- regression-tests.rootzone/tests/direct-ds/command | 1 + regression-tests.rootzone/tests/direct-ds/description | 1 + regression-tests.rootzone/tests/direct-ds/expected_result | 4 ++++ .../tests/direct-ds/expected_result.dnssec | 5 +++++ regression-tests.rootzone/tests/direct-ns/command | 2 +- regression-tests.rootzone/tests/direct-ns/expected_result | 1 + .../tests/direct-ns/expected_result.dnssec | 2 ++ regression-tests.rootzone/tests/ref-3ld/command | 2 +- regression-tests.rootzone/tests/ref-3ld/expected_result | 1 + .../tests/ref-3ld/expected_result.dnssec | 2 ++ 10 files changed, 19 insertions(+), 2 deletions(-) create mode 100755 regression-tests.rootzone/tests/direct-ds/command create mode 100644 regression-tests.rootzone/tests/direct-ds/description create mode 100644 regression-tests.rootzone/tests/direct-ds/expected_result create mode 100644 regression-tests.rootzone/tests/direct-ds/expected_result.dnssec diff --git a/regression-tests.rootzone/tests/direct-ds/command b/regression-tests.rootzone/tests/direct-ds/command new file mode 100755 index 000000000000..fee1baef16f8 --- /dev/null +++ b/regression-tests.rootzone/tests/direct-ds/command @@ -0,0 +1 @@ +cleandig net DS dnssec diff --git a/regression-tests.rootzone/tests/direct-ds/description b/regression-tests.rootzone/tests/direct-ds/description new file mode 100644 index 000000000000..3c8d8b17a36b --- /dev/null +++ b/regression-tests.rootzone/tests/direct-ds/description @@ -0,0 +1 @@ +DS query for an existing TLD should get an answer diff --git a/regression-tests.rootzone/tests/direct-ds/expected_result b/regression-tests.rootzone/tests/direct-ds/expected_result new file mode 100644 index 000000000000..8d454a0989c3 --- /dev/null +++ b/regression-tests.rootzone/tests/direct-ds/expected_result @@ -0,0 +1,4 @@ +0 net. IN DS 86400 35886 8 2 7862b27f5f516ebe19680444d4ce5e762981931842c465f00236401d8bd973ee +2 . IN OPT 32768 +Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0 +Reply to question for qname='net.', qtype=DS diff --git a/regression-tests.rootzone/tests/direct-ds/expected_result.dnssec b/regression-tests.rootzone/tests/direct-ds/expected_result.dnssec new file mode 100644 index 000000000000..3b95c8b98e3c --- /dev/null +++ b/regression-tests.rootzone/tests/direct-ds/expected_result.dnssec @@ -0,0 +1,5 @@ +0 net. IN DS 86400 35886 8 2 7862b27f5f516ebe19680444d4ce5e762981931842c465f00236401d8bd973ee +0 net. IN RRSIG 86400 DS 13 1 86400 [expiry] [inception] [keytag] . ... +2 . IN OPT 32768 +Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0 +Reply to question for qname='net.', qtype=DS diff --git a/regression-tests.rootzone/tests/direct-ns/command b/regression-tests.rootzone/tests/direct-ns/command index 3051e14ce415..6681bf5a4f0d 100755 --- a/regression-tests.rootzone/tests/direct-ns/command +++ b/regression-tests.rootzone/tests/direct-ns/command @@ -1 +1 @@ -cleandig net NS +cleandig net NS dnssec diff --git a/regression-tests.rootzone/tests/direct-ns/expected_result b/regression-tests.rootzone/tests/direct-ns/expected_result index 7fc229651b72..e8cd4fe499af 100644 --- a/regression-tests.rootzone/tests/direct-ns/expected_result +++ b/regression-tests.rootzone/tests/direct-ns/expected_result @@ -1,4 +1,5 @@ 1 net. IN NS 172800 a.gtld-servers.net. +2 . IN OPT 32768 2 a.gtld-servers.net. IN A 172800 192.5.6.30 2 a.gtld-servers.net. IN AAAA 172800 2001:503:a83e::2:30 Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 0, opcode: 0 diff --git a/regression-tests.rootzone/tests/direct-ns/expected_result.dnssec b/regression-tests.rootzone/tests/direct-ns/expected_result.dnssec index 99b191559301..34b452e7499f 100644 --- a/regression-tests.rootzone/tests/direct-ns/expected_result.dnssec +++ b/regression-tests.rootzone/tests/direct-ns/expected_result.dnssec @@ -1,5 +1,7 @@ 1 net. IN DS 86400 35886 8 2 7862b27f5f516ebe19680444d4ce5e762981931842c465f00236401d8bd973ee 1 net. IN NS 172800 a.gtld-servers.net. +1 net. IN RRSIG 86400 DS 13 1 86400 [expiry] [inception] [keytag] . ... +2 . IN OPT 32768 2 a.gtld-servers.net. IN A 172800 192.5.6.30 2 a.gtld-servers.net. IN AAAA 172800 2001:503:a83e::2:30 Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 0, opcode: 0 diff --git a/regression-tests.rootzone/tests/ref-3ld/command b/regression-tests.rootzone/tests/ref-3ld/command index 9284a40dea45..324d0de13b13 100755 --- a/regression-tests.rootzone/tests/ref-3ld/command +++ b/regression-tests.rootzone/tests/ref-3ld/command @@ -1 +1 @@ -cleandig some-host.domain.net A +cleandig some-host.domain.net A dnssec diff --git a/regression-tests.rootzone/tests/ref-3ld/expected_result b/regression-tests.rootzone/tests/ref-3ld/expected_result index 1d4f635e00b6..87f4a7671027 100644 --- a/regression-tests.rootzone/tests/ref-3ld/expected_result +++ b/regression-tests.rootzone/tests/ref-3ld/expected_result @@ -1,4 +1,5 @@ 1 net. IN NS 172800 a.gtld-servers.net. +2 . IN OPT 32768 2 a.gtld-servers.net. IN A 172800 192.5.6.30 2 a.gtld-servers.net. IN AAAA 172800 2001:503:a83e::2:30 Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 0, opcode: 0 diff --git a/regression-tests.rootzone/tests/ref-3ld/expected_result.dnssec b/regression-tests.rootzone/tests/ref-3ld/expected_result.dnssec index 40c00dd2e2a2..abd2224cebd0 100644 --- a/regression-tests.rootzone/tests/ref-3ld/expected_result.dnssec +++ b/regression-tests.rootzone/tests/ref-3ld/expected_result.dnssec @@ -1,5 +1,7 @@ 1 net. IN DS 86400 35886 8 2 7862b27f5f516ebe19680444d4ce5e762981931842c465f00236401d8bd973ee 1 net. IN NS 172800 a.gtld-servers.net. +1 net. IN RRSIG 86400 DS 13 1 86400 [expiry] [inception] [keytag] . ... +2 . IN OPT 32768 2 a.gtld-servers.net. IN A 172800 192.5.6.30 2 a.gtld-servers.net. IN AAAA 172800 2001:503:a83e::2:30 Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 0, opcode: 0 From 7865becacac3d1105f9215c20caccf2b071f2fa4 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Fri, 1 Mar 2019 09:04:30 +0100 Subject: [PATCH 015/127] SuffixMatchTree: Remove TODO comment --- pdns/dnsname.hh | 2 -- 1 file changed, 2 deletions(-) diff --git a/pdns/dnsname.hh b/pdns/dnsname.hh index ff64e2097f95..dc083fa6820d 100644 --- a/pdns/dnsname.hh +++ b/pdns/dnsname.hh @@ -308,8 +308,6 @@ struct SuffixMatchTree if (labels.empty()) { // The child is no longer an endnode child->endNode = false; - // TODO clear d_value for this node as d_value = T() would break for types - // that require initialization // If the child has no further children, just remove it from the set. if (child->children.empty()) { From 8d983800709fbce5ce62b02632df64cea92b7ee7 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Fri, 1 Mar 2019 09:05:42 +0100 Subject: [PATCH 016/127] SuffixMatchTree: Remove unused d_human member --- pdns/dnsname.hh | 1 - 1 file changed, 1 deletion(-) diff --git a/pdns/dnsname.hh b/pdns/dnsname.hh index dc083fa6820d..030057b40af4 100644 --- a/pdns/dnsname.hh +++ b/pdns/dnsname.hh @@ -428,7 +428,6 @@ struct SuffixMatchNode } private: - mutable std::string d_human; mutable std::set d_nodes; // Only used for string generation }; From c3491dd68e99df9f78ab2fc0bf2d29357af19158 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Fri, 1 Mar 2019 14:59:00 +0100 Subject: [PATCH 017/127] dnsrecords: make DNAME mimic CNAME --- pdns/dnsrecords.hh | 3 +++ pdns/packethandler.cc | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pdns/dnsrecords.hh b/pdns/dnsrecords.hh index d9f5a18ec474..4258f5b26f66 100644 --- a/pdns/dnsrecords.hh +++ b/pdns/dnsrecords.hh @@ -272,6 +272,9 @@ class DNAMERecordContent : public DNSRecordContent { public: includeboilerplate(DNAME) + DNAMERecordContent(const DNSName& content) : d_content(content){} + DNSName getTarget() const { return d_content; } +private: DNSName d_content; }; diff --git a/pdns/packethandler.cc b/pdns/packethandler.cc index 72888474af8c..726b6c88024e 100644 --- a/pdns/packethandler.cc +++ b/pdns/packethandler.cc @@ -331,7 +331,7 @@ vector PacketHandler::getBestDNAMESynth(DNSPacket *p, SOAData& sd ret.push_back(rr); // put in the original rr.dr.d_type = QType::CNAME; rr.dr.d_name = prefix + rr.dr.d_name; - rr.dr.d_content = std::make_shared(CNAMERecordContent(prefix + getRR(rr.dr)->d_content)); + rr.dr.d_content = std::make_shared(CNAMERecordContent(prefix + getRR(rr.dr)->getTarget())); rr.auth = 0; // don't sign CNAME target= getRR(rr.dr)->getTarget(); ret.push_back(rr); From 123da0f8bc6e66e63a85cc04d10808891616a882 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Fri, 1 Mar 2019 14:59:32 +0100 Subject: [PATCH 018/127] SyncRes: Process DNAME answers --- pdns/recursordist/test-syncres_cc.cc | 66 ++++++++++++++++++++++++++++ pdns/syncres.cc | 37 +++++++++++++--- pdns/syncres.hh | 1 + 3 files changed, 98 insertions(+), 6 deletions(-) diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index 8736f32aee43..1a336cf22c5c 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -10792,6 +10792,72 @@ BOOST_AUTO_TEST_CASE(test_records_sanitization_scrubs_ns_nxd) { BOOST_CHECK_LT(t_RC->get(now, DNSName("spoofed.ns."), QType(QType::AAAA), false, &cached, who), 0); } +BOOST_AUTO_TEST_CASE(test_dname_processing) { + std::unique_ptr sr; + initSR(sr); + + primeHints(); + + const DNSName dnameOwner("powerdns.com"); + const DNSName dnameTarget("powerdns.net"); + + const DNSName target("dname.powerdns.com."); + const DNSName cnameTarget("dname.powerdns.net"); + + size_t queries = 0; + + sr->setAsyncCallback([dnameOwner, dnameTarget, target, cnameTarget, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, LWResult* res, bool* chained) { + queries++; + + if (isRootServer(ip)) { + if (domain.isPartOf(dnameOwner)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, dnameOwner, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + if (domain.isPartOf(dnameTarget)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, dnameTarget, QType::NS, "b.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "b.gtld-servers.net.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + } else if (ip == ComboAddress("192.0.2.1:53")) { + if (domain == target) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString()); + addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); + return 1; + } + } else if (ip == ComboAddress("192.0.2.2:53")) { + if (domain == cnameTarget) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, domain, QType::A, "192.0.2.2"); + } + return 1; + } + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_REQUIRE_EQUAL(ret.size(), 3); + + BOOST_CHECK_EQUAL(queries, 4); + + BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(ret[1].d_name, target); + + BOOST_CHECK(ret[2].d_type == QType::A); + BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget); +} + /* // cerr<<"asyncresolve called to ask "< SyncRes::s_redirectionQTypes = {QType::CNAME, QType::DNAME}; unsigned int SyncRes::s_maxnegttl; unsigned int SyncRes::s_maxbogusttl; @@ -2079,7 +2080,7 @@ void SyncRes::sanitizeRecords(const std::string& prefix, LWResult& lwr, const DN continue; } - if (rec->d_place == DNSResourceRecord::ANSWER && (qtype != QType::ANY && rec->d_type != qtype.getCode() && rec->d_type != QType::CNAME && rec->d_type != QType::SOA && rec->d_type != QType::RRSIG)) { + if (rec->d_place == DNSResourceRecord::ANSWER && (qtype != QType::ANY && rec->d_type != qtype.getCode() && s_redirectionQTypes.count(rec->d_type) == 0 && rec->d_type != QType::SOA && rec->d_type != QType::RRSIG)) { LOG(prefix<<"Removing irrelevant record '"<d_name<<"|"<d_type)<<"|"<d_content->getZoneRepresentation()<<"' in the "<<(int)rec->d_place<<" section received from "<& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, const bool needWildcardProof, const unsigned int wildcardLabelsCount) { bool done = false; + DNSName dnameTarget, dnameOwner; for(auto& rec : lwr.d_records) { if (rec.d_type!=QType::OPT && rec.d_class!=QClass::IN) @@ -2513,10 +2515,19 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co negindic=true; } - else if(rec.d_place==DNSResourceRecord::ANSWER && rec.d_type==QType::CNAME && (!(qtype==QType(QType::CNAME))) && rec.d_name == qname) { - ret.push_back(rec); - if (auto content = getRR(rec)) { - newtarget=content->getTarget(); + else if(rec.d_place==DNSResourceRecord::ANSWER && s_redirectionQTypes.count(rec.d_type) > 0 && // CNAME or DNAME answer + s_redirectionQTypes.count(qtype.getCode()) == 0) { // But not in response to a CNAME or DNAME query + if (rec.d_type == QType::CNAME && rec.d_name == qname) { + ret.push_back(rec); + if (auto content = getRR(rec)) { + newtarget=content->getTarget(); + } + } else if (rec.d_type == QType::DNAME && qname.isPartOf(rec.d_name)) { // DNAME + ret.push_back(rec); + if (auto content = getRR(rec)) { + dnameOwner = rec.d_name; + dnameTarget = content->getTarget(); + } } } /* if we have a positive answer synthetized from a wildcard, we need to @@ -2571,8 +2582,14 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co ret.push_back(rec); } else if((rec.d_type==QType::RRSIG || rec.d_type==QType::NSEC || rec.d_type==QType::NSEC3) && rec.d_place==DNSResourceRecord::ANSWER) { - if(rec.d_type != QType::RRSIG || rec.d_name == qname) + if(rec.d_type != QType::RRSIG || rec.d_name == qname) { ret.push_back(rec); // enjoy your DNSSEC + } else if(rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) { + auto rrsig = getRR(rec); + if (rrsig != nullptr && rrsig->d_type == QType::DNAME) { + ret.push_back(rec); + } + } } else if(rec.d_place==DNSResourceRecord::AUTHORITY && rec.d_type==QType::NS && qname.isPartOf(rec.d_name)) { if(moreSpecificThan(rec.d_name,auth)) { @@ -2662,6 +2679,14 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co } } + if (!dnameTarget.empty() && !newtarget.empty()) { + DNSName substTarget = qname.makeRelative(dnameOwner) + dnameTarget; + if (substTarget != newtarget) { + throw ImmediateServFailException("Received wrong DNAME substitution. qname='" + qname.toLogString() + + "', DNAME owner='" + dnameOwner.toLogString() + "', DNAME target='" + dnameTarget.toLogString() + + "', received CNAME='" + newtarget.toLogString() + "', substituted CNAME='" + substTarget.toLogString() + "'"); + } + } return done; } diff --git a/pdns/syncres.hh b/pdns/syncres.hh index a19f21e54e6d..27a2e5d6bcff 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -728,6 +728,7 @@ private: static EDNSSubnetOpts s_ecsScopeZero; static LogMode s_lm; static std::unique_ptr s_dontQuery; + const static std::unordered_set s_redirectionQTypes; struct GetBestNSAnswer { From 8838c88f478bc2bd20fd8d842ecda1b1f1b6cc95 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Fri, 1 Mar 2019 18:33:50 +0100 Subject: [PATCH 019/127] rec: Validate DNAME sigs --- pdns/recursordist/test-syncres_cc.cc | 103 +++++++++++++++++++++++++++ pdns/syncres.cc | 32 +++++++-- 2 files changed, 130 insertions(+), 5 deletions(-) diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index 1a336cf22c5c..e0e4ca17c4a0 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -10856,6 +10856,109 @@ BOOST_AUTO_TEST_CASE(test_dname_processing) { BOOST_CHECK(ret[2].d_type == QType::A); BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget); + + // TODO add cached tests once the caching of DNAME is implemented +} + +BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { + std::unique_ptr sr; + initSR(sr, true); + setDNSSECValidation(sr, DNSSECMode::ValidateAll); + + primeHints(); + + const DNSName dnameOwner("powerdns"); + const DNSName dnameTarget("example"); + + const DNSName target("dname.powerdns"); + const DNSName cnameTarget("dname.example"); + + testkeysset_t keys; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + generateKeyMaterial(dnameOwner, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys); + generateKeyMaterial(dnameTarget, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys); + g_luaconfs.setState(luaconfsCopy); + + size_t queries = 0; + + sr->setAsyncCallback([dnameOwner, dnameTarget, target, cnameTarget, keys, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, LWResult* res, bool* chained) { + queries++; + + if (type == QType::DS || type == QType::DNSKEY) { + bool proveCut=true; + auto auth{domain}; + if (domain.countLabels() > 1) { + auth.chopOff(); + proveCut = false; + } + return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, proveCut); + } + + if (isRootServer(ip)) { + if (domain.isPartOf(dnameOwner)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, dnameOwner, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addDS(dnameOwner, 300, res->d_records, keys); + addRRSIG(keys, res->d_records, DNSName("."), 300); + addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + if (domain.isPartOf(dnameTarget)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, dnameTarget, QType::NS, "b.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addDS(dnameTarget, 300, res->d_records, keys); + addRRSIG(keys, res->d_records, DNSName("."), 300); + addRecordToLW(res, "b.gtld-servers.net.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + } else if (ip == ComboAddress("192.0.2.1:53")) { + if (domain == target) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString()); + addRRSIG(keys, res->d_records, DNSName("."), 300); + addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); + return 1; + } + } else if (ip == ComboAddress("192.0.2.2:53")) { + if (domain == cnameTarget) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, domain, QType::A, "192.0.2.2"); + addRRSIG(keys, res->d_records, DNSName("."), 300); + } + return 1; + } + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), Secure); + BOOST_REQUIRE_EQUAL(ret.size(), 5); /* DNAME + RRSIG(DNAME) + CNAME + A + RRSIG(A) */ + + BOOST_CHECK_EQUAL(queries, 9); + + BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::RRSIG); + BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner); + + BOOST_CHECK(ret[2].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(ret[2].d_name, target); + + BOOST_CHECK(ret[3].d_type == QType::A); + BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget); + + BOOST_CHECK(ret[4].d_type == QType::RRSIG); + BOOST_CHECK_EQUAL(ret[4].d_name, cnameTarget); + + // TODO add cached tests once the caching of DNAME is implemented } /* diff --git a/pdns/syncres.cc b/pdns/syncres.cc index d247efd4868f..01d22c530d51 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -2171,6 +2171,7 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr std::vector> authorityRecs; const unsigned int labelCount = qname.countLabels(); bool isCNAMEAnswer = false; + bool isDNAMEAnswer = false; for(const auto& rec : lwr.d_records) { if (rec.d_class != QClass::IN) { continue; @@ -2179,6 +2180,9 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr if(!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype==QType(QType::CNAME))) && rec.d_name == qname) { isCNAMEAnswer = true; } + if(!isDNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::DNAME && (!(qtype==QType(QType::DNAME))) && qname.isPartOf(rec.d_name)) { + isDNAMEAnswer = true; + } /* if we have a positive answer synthetized from a wildcard, we need to store the corresponding NSEC/NSEC3 records proving @@ -2338,6 +2342,10 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr expectSignature = false; } + if (isDNAMEAnswer && !isAA && i->first.place == DNSResourceRecord::ANSWER && i->first.type == QType::DNAME && qname.isPartOf(i->first.name)) { + isAA = true; + } + if (isCNAMEAnswer && i->first.place == DNSResourceRecord::AUTHORITY && i->first.type == QType::NS && auth == i->first.name) { /* These NS can't be authoritative since we have a CNAME answer for which (see above) only the record describing that alias is necessarily authoritative. @@ -2367,11 +2375,25 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr recordState = validateDNSKeys(i->first.name, i->second.records, i->second.signatures, depth); } else { - LOG(d_prefix<<"Validating non-additional record for "<first.name<first.name, i->second.records, i->second.signatures); - /* we might have missed a cut (zone cut within the same auth servers), causing the NS query for an Insecure zone to seem Bogus during zone cut determination */ - if (qtype == QType::NS && i->second.signatures.empty() && recordState == Bogus && haveExactValidationStatus(i->first.name) && getValidationStatus(i->first.name) == Indeterminate) { - recordState = Indeterminate; + /* + * RFC 6672 section 5.3.1 + * In any response, a signed DNAME RR indicates a non-terminal + * redirection of the query. There might or might not be a server- + * synthesized CNAME in the answer section; if there is, the CNAME will + * never be signed. For a DNSSEC validator, verification of the DNAME + * RR and then that the CNAME was properly synthesized is sufficient + * proof. + * + * We do the synthesis check in processRecords, here we make sure we + * don't validate the CNAME. + */ + if (!(isDNAMEAnswer && i->first.type == QType::CNAME)) { + LOG(d_prefix<<"Validating non-additional record for "<first.name<first.name, i->second.records, i->second.signatures); + /* we might have missed a cut (zone cut within the same auth servers), causing the NS query for an Insecure zone to seem Bogus during zone cut determination */ + if (qtype == QType::NS && i->second.signatures.empty() && recordState == Bogus && haveExactValidationStatus(i->first.name) && getValidationStatus(i->first.name) == Indeterminate) { + recordState = Indeterminate; + } } } } From d30b54935bc9037bdded105812ef232a133433f0 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Mon, 4 Mar 2019 17:51:22 +0100 Subject: [PATCH 020/127] Retrieve DNAMEs from the cache --- pdns/syncres.cc | 106 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 22 deletions(-) diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 01d22c530d51..28216fbb0e49 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -936,15 +936,43 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector return true; } - LOG(prefix< cset; vector> signatures; vector> authorityRecs; bool wasAuth; uint32_t capTTL = std::numeric_limits::max(); - /* we don't require auth data for forward-recurse lookups */ - if(t_RC->get(d_now.tv_sec, qname, QType(QType::CNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) { + DNSName foundName; + QType foundQT = QType(0); // 0 == QTYPE::ENT + + if (qname != g_rootdnsname) { + // look for a DNAME cache hit + auto labels = qname.getRawLabels(); + DNSName dnameName(g_rootdnsname); + + do { + dnameName.prependRawLabel(labels.back()); + labels.pop_back(); + LOG(prefix<get(d_now.tv_sec, dnameName, QType(QType::DNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) { + if (dnameName != qname && qtype != QType::DNAME) { + foundName = dnameName; + foundQT = QType(QType::DNAME); + break; + } + } + } while(!labels.empty()); + } + if (foundName.empty()) { + LOG(prefix<get(d_now.tv_sec, qname, QType(QType::CNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) { + foundName = qname; + foundQT = QType(QType::CNAME); + } + } + + if(!foundName.empty()) { for(auto j=cset.cbegin() ; j != cset.cend() ; ++j) { if (j->d_class != QClass::IN) { continue; @@ -956,28 +984,28 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector /* This means we couldn't figure out the state when this entry was cached, most likely because we hadn't computed the zone cuts yet. */ /* make sure they are computed before validating */ - DNSName subdomain(qname); + DNSName subdomain(foundName); /* if we are retrieving a DS, we only care about the state of the parent zone */ if(qtype == QType::DS) subdomain.chopOff(); computeZoneCuts(subdomain, g_rootdnsname, depth); - vState recordState = getValidationStatus(qname, false); + vState recordState = getValidationStatus(foundName, false); if (recordState == Secure) { - LOG(prefix<d_content->getZoneRepresentation()<<"', validation state is "<d_content->getZoneRepresentation()<<"', validation state is "<beenthere; + DNSName newTarget; + if (foundQT == QType::DNAME) { + if (qtype == QType::DNAME && qname == foundName) { // client wanted the DNAME, no need to synthesize a CNAME + res = 0; + return true; + } + // Synthesize a CNAME + auto dnameRR = getRR(*j); + if (dnameRR == nullptr) { + throw ImmediateServFailException("Unable to get record content for "+foundName.toLogString()+"|DNAME cache entry"); + } + auto dnameSuffix = dnameRR->getTarget(); + DNSName targetPrefix = qname.makeRelative(foundName); + dr.d_type = QType::CNAME; + dr.d_name = targetPrefix + foundName; + newTarget = targetPrefix + dnameSuffix; + dr.d_content = std::make_shared(CNAMERecordContent(newTarget)); + ret.push_back(dr); + + LOG(prefix<(*j); - if (cnameContent) { - res=doResolve(cnameContent->getTarget(), qtype, ret, depth+1, beenthere, cnameState); - LOG(prefix<getTarget(); } - else - res=0; + + setbeenthere; + vState cnameState = Indeterminate; + res = doResolve(newTarget, qtype, ret, depth+1, beenthere, cnameState); + LOG(prefix<empty()) { // Check if we are authoritative for a zone in this answer DNSName tmp_qname(rec.d_name); From 40e3c9881ae1fcad05204c1bf8d169dfd48359d3 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 5 Mar 2019 12:00:51 +0100 Subject: [PATCH 021/127] Add DNAME cache tests --- pdns/recursordist/test-syncres_cc.cc | 93 +++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index e0e4ca17c4a0..8459786459ab 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -10857,7 +10857,24 @@ BOOST_AUTO_TEST_CASE(test_dname_processing) { BOOST_CHECK(ret[2].d_type == QType::A); BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget); - // TODO add cached tests once the caching of DNAME is implemented + // Now check the cache + ret.clear(); + res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_REQUIRE_EQUAL(ret.size(), 3); + + BOOST_CHECK_EQUAL(queries, 4); + + BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(ret[1].d_name, target); + + BOOST_CHECK(ret[2].d_type == QType::A); + BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget); } BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { @@ -10887,17 +10904,20 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { sr->setAsyncCallback([dnameOwner, dnameTarget, target, cnameTarget, keys, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, LWResult* res, bool* chained) { queries++; - if (type == QType::DS || type == QType::DNSKEY) { - bool proveCut=true; - auto auth{domain}; - if (domain.countLabels() > 1) { - auth.chopOff(); - proveCut = false; - } - return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, proveCut); - } - if (isRootServer(ip)) { + if (domain.countLabels() == 0 && type == QType::DNSKEY) { // .|DNSKEY + setLWResult(res, 0, true, false, true); + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, DNSName("."), 300); + return 1; + } + if (domain.countLabels() == 1 && type == QType::DS) { // powerdns|DS or example|DS + setLWResult(res, 0, true, false, true); + addDS(domain, 300, res->d_records, keys); + addRRSIG(keys, res->d_records, DNSName("."), 300); + return 1; + } + // For the rest, delegate! if (domain.isPartOf(dnameOwner)) { setLWResult(res, 0, false, false, true); addRecordToLW(res, dnameOwner, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); @@ -10915,18 +10935,36 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { return 1; } } else if (ip == ComboAddress("192.0.2.1:53")) { + if (domain.countLabels() == 1 && type == QType::DNSKEY) { // powerdns|DNSKEY + setLWResult(res, 0, true, false, true); + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + return 1; + } + if (domain == target && type == QType::DS) { // dname.powerdns|DS + return genericDSAndDNSKEYHandler(res, domain, dnameOwner, type, keys); + } if (domain == target) { setLWResult(res, 0, true, false, false); addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString()); - addRRSIG(keys, res->d_records, DNSName("."), 300); + addRRSIG(keys, res->d_records, dnameOwner, 300); addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); return 1; } } else if (ip == ComboAddress("192.0.2.2:53")) { + if (domain.countLabels() == 1 && type == QType::DNSKEY) { // example|DNSKEY + setLWResult(res, 0, true, false, true); + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + return 1; + } + if (domain == target && type == QType::DS) { // dname.example|DS + return genericDSAndDNSKEYHandler(res, domain, dnameTarget, type, keys); + } if (domain == cnameTarget) { setLWResult(res, 0, true, false, false); addRecordToLW(res, domain, QType::A, "192.0.2.2"); - addRRSIG(keys, res->d_records, DNSName("."), 300); + addRRSIG(keys, res->d_records, dnameTarget, 300); } return 1; } @@ -10940,7 +10978,33 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { BOOST_CHECK_EQUAL(sr->getValidationState(), Secure); BOOST_REQUIRE_EQUAL(ret.size(), 5); /* DNAME + RRSIG(DNAME) + CNAME + A + RRSIG(A) */ - BOOST_CHECK_EQUAL(queries, 9); + BOOST_CHECK_EQUAL(queries, 11); + + BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::RRSIG); + BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner); + + BOOST_CHECK(ret[2].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(ret[2].d_name, target); + + BOOST_CHECK(ret[3].d_type == QType::A); + BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget); + + BOOST_CHECK(ret[4].d_type == QType::RRSIG); + BOOST_CHECK_EQUAL(ret[4].d_name, cnameTarget); + + // And the cache + ret.clear(); + res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), Secure); + BOOST_REQUIRE_EQUAL(ret.size(), 5); /* DNAME + RRSIG(DNAME) + CNAME + A + RRSIG(A) */ + + BOOST_CHECK_EQUAL(queries, 11); BOOST_CHECK(ret[0].d_type == QType::DNAME); BOOST_CHECK(ret[0].d_name == dnameOwner); @@ -10958,7 +11022,6 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { BOOST_CHECK(ret[4].d_type == QType::RRSIG); BOOST_CHECK_EQUAL(ret[4].d_name, cnameTarget); - // TODO add cached tests once the caching of DNAME is implemented } /* From 60cb670d72a870eddaf0b2291649b386bb26d36c Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 5 Mar 2019 13:57:45 +0100 Subject: [PATCH 022/127] DNAME: add insecure test --- pdns/recursordist/test-syncres_cc.cc | 138 +++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index 8459786459ab..dbe8166b6352 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -11024,6 +11024,144 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { } +BOOST_AUTO_TEST_CASE(test_dname_dnssec_insecure) { + /* + * The DNAME itself is signed, but the final A record is not + */ + std::unique_ptr sr; + initSR(sr, true); + setDNSSECValidation(sr, DNSSECMode::ValidateAll); + + primeHints(); + + const DNSName dnameOwner("powerdns"); + const DNSName dnameTarget("example"); + + const DNSName target("dname.powerdns"); + const DNSName cnameTarget("dname.example"); + + testkeysset_t keys; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + generateKeyMaterial(dnameOwner, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys); + g_luaconfs.setState(luaconfsCopy); + + size_t queries = 0; + + sr->setAsyncCallback([dnameOwner, dnameTarget, target, cnameTarget, keys, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, LWResult* res, bool* chained) { + queries++; + + if (isRootServer(ip)) { + if (domain.countLabels() == 0 && type == QType::DNSKEY) { // .|DNSKEY + setLWResult(res, 0, true, false, true); + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, DNSName("."), 300); + return 1; + } + if (domain == dnameOwner && type == QType::DS) { // powerdns|DS + setLWResult(res, 0, true, false, true); + addDS(domain, 300, res->d_records, keys); + addRRSIG(keys, res->d_records, DNSName("."), 300); + return 1; + } + if (domain == dnameTarget && type == QType::DS) { // example|DS + return genericDSAndDNSKEYHandler(res, domain, DNSName("."), type, keys); + } + // For the rest, delegate! + if (domain.isPartOf(dnameOwner)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, dnameOwner, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addDS(dnameOwner, 300, res->d_records, keys); + addRRSIG(keys, res->d_records, DNSName("."), 300); + addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + if (domain.isPartOf(dnameTarget)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, dnameTarget, QType::NS, "b.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addDS(dnameTarget, 300, res->d_records, keys); + addRRSIG(keys, res->d_records, DNSName("."), 300); + addRecordToLW(res, "b.gtld-servers.net.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + } else if (ip == ComboAddress("192.0.2.1:53")) { + if (domain.countLabels() == 1 && type == QType::DNSKEY) { // powerdns|DNSKEY + setLWResult(res, 0, true, false, true); + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + return 1; + } + if (domain == target && type == QType::DS) { // dname.powerdns|DS + return genericDSAndDNSKEYHandler(res, domain, dnameOwner, type, keys); + } + if (domain == target) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString()); + addRRSIG(keys, res->d_records, dnameOwner, 300); + addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); + return 1; + } + } else if (ip == ComboAddress("192.0.2.2:53")) { + if (domain == target && type == QType::DS) { // dname.example|DS + return genericDSAndDNSKEYHandler(res, domain, dnameTarget, type, keys); + } + if (domain == cnameTarget) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, domain, QType::A, "192.0.2.2"); + } + return 1; + } + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure); + BOOST_REQUIRE_EQUAL(ret.size(), 4); /* DNAME + RRSIG(DNAME) + CNAME + A */ + + BOOST_CHECK_EQUAL(queries, 9); + + BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::RRSIG); + BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner); + + BOOST_CHECK(ret[2].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(ret[2].d_name, target); + + BOOST_CHECK(ret[3].d_type == QType::A); + BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget); + + // And the cache + ret.clear(); + res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure); + BOOST_REQUIRE_EQUAL(ret.size(), 4); /* DNAME + RRSIG(DNAME) + CNAME + A */ + + BOOST_CHECK_EQUAL(queries, 9); + + BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::RRSIG); + BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner); + + BOOST_CHECK(ret[2].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(ret[2].d_name, target); + + BOOST_CHECK(ret[3].d_type == QType::A); + BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget); +} + /* // cerr<<"asyncresolve called to ask "< Date: Tue, 5 Mar 2019 16:12:40 +0100 Subject: [PATCH 023/127] Add DNAME regression tests --- .../basicDNSSEC.py | 125 ++++++++++++++++++ .../recursortests.py | 30 ++++- 2 files changed, 154 insertions(+), 1 deletion(-) diff --git a/regression-tests.recursor-dnssec/basicDNSSEC.py b/regression-tests.recursor-dnssec/basicDNSSEC.py index e754dffa8488..aef9fc2a3436 100644 --- a/regression-tests.recursor-dnssec/basicDNSSEC.py +++ b/regression-tests.recursor-dnssec/basicDNSSEC.py @@ -147,3 +147,128 @@ def testSecureToInsecureCNAMEAnswer(self): self.assertRRsetInAnswer(res, expectedA) self.assertMatchingRRSIGInAnswer(res, expectedCNAME) + def testSecureDNAMEToSecureAnswer(self): + res = self.sendQuery('host1.dname-secure.secure.example.', 'A') + expectedDNAME = dns.rrset.from_text('dname-secure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.') + expectedCNAME = dns.rrset.from_text('host1.dname-secure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'host1.dname-secure.example.') + expectedA = dns.rrset.from_text('host1.dname-secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.21') + + self.assertRcodeEqual(res, dns.rcode.NOERROR) + self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'AD'], ['DO']) + self.assertRRsetInAnswer(res, expectedA) + self.assertRRsetInAnswer(res, expectedCNAME) + self.assertRRsetInAnswer(res, expectedDNAME) + self.assertMatchingRRSIGInAnswer(res, expectedDNAME) + self.assertMatchingRRSIGInAnswer(res, expectedA) + + def testSecureDNAMEToSecureNXDomain(self): + res = self.sendQuery('nxd.dname-secure.secure.example.', 'A') + expectedDNAME = dns.rrset.from_text('dname-secure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.') + expectedCNAME = dns.rrset.from_text('nxd.dname-secure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'nxd.dname-secure.example.') + + self.assertRcodeEqual(res, dns.rcode.NXDOMAIN) + self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'AD'], ['DO']) + self.assertRRsetInAnswer(res, expectedCNAME) + self.assertRRsetInAnswer(res, expectedDNAME) + self.assertMatchingRRSIGInAnswer(res, expectedDNAME) + + def testSecureDNAMEToInsecureAnswer(self): + res = self.sendQuery('node1.dname-insecure.secure.example.', 'A') + expectedDNAME = dns.rrset.from_text('dname-insecure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'insecure.example.') + expectedCNAME = dns.rrset.from_text('node1.dname-insecure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'node1.insecure.example.') + expectedA = dns.rrset.from_text('node1.insecure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.6') + + self.assertRcodeEqual(res, dns.rcode.NOERROR) + self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO']) + self.assertRRsetInAnswer(res, expectedA) + self.assertRRsetInAnswer(res, expectedCNAME) + self.assertRRsetInAnswer(res, expectedDNAME) + self.assertMatchingRRSIGInAnswer(res, expectedDNAME) + + def testSecureDNAMEToInsecureNXDomain(self): + res = self.sendQuery('nxd.dname-insecure.secure.example.', 'A') + expectedDNAME = dns.rrset.from_text('dname-insecure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'insecure.example.') + expectedCNAME = dns.rrset.from_text('nxd.dname-insecure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'nxd.insecure.example.') + + self.assertRcodeEqual(res, dns.rcode.NXDOMAIN) + self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO']) + self.assertRRsetInAnswer(res, expectedCNAME) + self.assertRRsetInAnswer(res, expectedDNAME) + self.assertMatchingRRSIGInAnswer(res, expectedDNAME) + + def testSecureDNAMEToBogusAnswer(self): + res = self.sendQuery('ted.dname-bogus.secure.example.', 'A') + + self.assertRcodeEqual(res, dns.rcode.SERVFAIL) + self.assertAnswerEmpty(res) + + def testSecureDNAMEToBogusNXDomain(self): + res = self.sendQuery('nxd.dname-bogus.secure.example.', 'A') + + self.assertRcodeEqual(res, dns.rcode.SERVFAIL) + self.assertAnswerEmpty(res) + + def testInsecureDNAMEtoSecureAnswer(self): + res = self.sendQuery('host1.dname-to-secure.insecure.example.', 'A') + expectedDNAME = dns.rrset.from_text('dname-to-secure.insecure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.') + expectedCNAME = dns.rrset.from_text('host1.dname-to-secure.insecure.example.', 0, dns.rdataclass.IN, 'CNAME', 'host1.dname-secure.example.') + expectedA = dns.rrset.from_text('host1.dname-secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.21') + + self.assertRcodeEqual(res, dns.rcode.NOERROR) + self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO']) + self.assertRRsetInAnswer(res, expectedA) + self.assertRRsetInAnswer(res, expectedCNAME) + self.assertRRsetInAnswer(res, expectedDNAME) + self.assertMatchingRRSIGInAnswer(res, expectedA) + + def testSecureDNAMEToSecureCNAMEAnswer(self): + res = self.sendQuery('cname-to-secure.dname-secure.secure.example.', 'A') + + expectedDNAME = dns.rrset.from_text('dname-secure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.') + expectedCNAME1 = dns.rrset.from_text('cname-to-secure.dname-secure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'cname-to-secure.dname-secure.example.') + expectedCNAME2 = dns.rrset.from_text('cname-to-secure.dname-secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'host1.secure.example.') + expectedA = dns.rrset.from_text('host1.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.2') + + self.assertRcodeEqual(res, dns.rcode.NOERROR) + self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'AD'], ['DO']) + self.assertRRsetInAnswer(res, expectedA) + self.assertRRsetInAnswer(res, expectedCNAME1) + self.assertRRsetInAnswer(res, expectedCNAME2) + self.assertMatchingRRSIGInAnswer(res, expectedCNAME2) + self.assertRRsetInAnswer(res, expectedDNAME) + self.assertMatchingRRSIGInAnswer(res, expectedDNAME) + self.assertMatchingRRSIGInAnswer(res, expectedA) + + def testSecureDNAMEToInsecureCNAMEAnswer(self): + res = self.sendQuery('cname-to-insecure.dname-secure.secure.example.', 'A') + + expectedDNAME = dns.rrset.from_text('dname-secure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.') + expectedCNAME1 = dns.rrset.from_text('cname-to-insecure.dname-secure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'cname-to-insecure.dname-secure.example.') + expectedCNAME2 = dns.rrset.from_text('cname-to-insecure.dname-secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'node1.insecure.example.') + expectedA = dns.rrset.from_text('node1.insecure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.6') + + self.assertRcodeEqual(res, dns.rcode.NOERROR) + self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO']) + self.assertRRsetInAnswer(res, expectedA) + self.assertRRsetInAnswer(res, expectedCNAME1) + self.assertRRsetInAnswer(res, expectedCNAME2) + self.assertMatchingRRSIGInAnswer(res, expectedCNAME2) + self.assertRRsetInAnswer(res, expectedDNAME) + self.assertMatchingRRSIGInAnswer(res, expectedDNAME) + + def testSecureDNAMEToBogusCNAMEAnswer(self): + res = self.sendQuery('cname-to-bogus.dname-secure.secure.example.', 'A') + + self.assertRcodeEqual(res, dns.rcode.SERVFAIL) + self.assertAnswerEmpty(res) + + def testInsecureDNAMEtoSecureNXDomain(self): + res = self.sendQuery('nxd.dname-to-secure.insecure.example.', 'A') + expectedDNAME = dns.rrset.from_text('dname-to-secure.insecure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.') + expectedCNAME = dns.rrset.from_text('nxd.dname-to-secure.insecure.example.', 0, dns.rdataclass.IN, 'CNAME', 'nxd.dname-secure.example.') + + self.assertRcodeEqual(res, dns.rcode.NXDOMAIN) + self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO']) + self.assertRRsetInAnswer(res, expectedCNAME) + self.assertRRsetInAnswer(res, expectedDNAME) + self.assertMatchingRRSIGInAnswer(res, expectedDNAME) diff --git a/regression-tests.recursor-dnssec/recursortests.py b/regression-tests.recursor-dnssec/recursortests.py index 46ca97c4677b..cfbd87bad4a6 100644 --- a/regression-tests.recursor-dnssec/recursortests.py +++ b/regression-tests.recursor-dnssec/recursortests.py @@ -85,6 +85,10 @@ class RecursorTest(unittest.TestCase): cname-secure.example. 3600 IN DS 49148 13 1 a10314452d5ec4d97fcc6d7e275d217261fe790f ns.cname-secure.example. 3600 IN A {prefix}.15 +dname-secure.example. 3600 IN NS ns.dname-secure.example. +dname-secure.example. 3600 IN DS 42043 13 2 11c67f46b7c4d5968bc5f6cc944d58377b762bda53ddb4f3a6dbe6faf7a9940f +ns.dname-secure.example. 3600 IN A {prefix}.13 + bogus.example. 3600 IN NS ns.bogus.example. bogus.example. 3600 IN DS 65034 13 1 6df3bb50ea538e90eacdd7ae5419730783abb0ee ns.bogus.example. 3600 IN A {prefix}.12 @@ -137,7 +141,22 @@ class RecursorTest(unittest.TestCase): *.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesntexist.secure.example. cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example. + +dname-secure.secure.example. 3600 IN DNAME dname-secure.example. +dname-insecure.secure.example. 3600 IN DNAME insecure.example. +dname-bogus.secure.example. 3600 IN DNAME bogus.example. """, + 'dname-secure.example': """ +dname-secure.example. 3600 IN SOA {soa} +dname-secure.example. 3600 IN NS ns.dname-secure.example. +ns.dname-secure.example. 3600 IN A {prefix}.13 + +host1.dname-secure.example. IN A 192.0.2.21 + +cname-to-secure.dname-secure.example. 3600 IN CNAME host1.secure.example. +cname-to-insecure.dname-secure.example. 3600 IN CNAME node1.insecure.example. +cname-to-bogus.dname-secure.example. 3600 IN CNAME ted.bogus.example. +""", 'cname-secure.example': """ cname-secure.example. 3600 IN SOA {soa} cname-secure.example. 3600 IN NS ns.cname-secure.example. @@ -165,6 +184,8 @@ class RecursorTest(unittest.TestCase): node1.insecure.example. 3600 IN A 192.0.2.6 cname-to-secure.insecure.example. 3600 IN CNAME host1.secure.example. + +dname-to-secure.insecure.example. 3600 IN DNAME dname-secure.example. """, 'optout.example': """ optout.example. 3600 IN SOA {soa} @@ -262,6 +283,12 @@ class RecursorTest(unittest.TestCase): Private-key-format: v1.2 Algorithm: 13 (ECDSAP256SHA256) PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA= +""", + + 'dname-secure.example': """ +Private-key-format: v1.2 +Algorithm: 13 (ECDSAP256SHA256) +PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04= """ } @@ -274,7 +301,7 @@ class RecursorTest(unittest.TestCase): '10': ['example'], '11': ['example'], '12': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example'], - '13': ['insecure.example', 'insecure.sub2.secure.example'], + '13': ['insecure.example', 'insecure.sub2.secure.example', 'dname-secure.example'], '14': ['optout.example'], '15': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example'] } @@ -333,6 +360,7 @@ def generateAuthConfig(cls, confdir): log-dns-queries=yes log-dns-details=yes loglevel=9 +dname-processing=yes distributor-threads=1""".format(confdir=confdir, bind_dnssec_db=bind_dnssec_db)) From dc0d34db7b2b8721e838f951146b51149c28859e Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 5 Mar 2019 16:12:55 +0100 Subject: [PATCH 024/127] Update pdns/dnsrecords.hh Co-Authored-By: pieterlexis --- pdns/dnsrecords.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdns/dnsrecords.hh b/pdns/dnsrecords.hh index 4258f5b26f66..1031ddf59dd7 100644 --- a/pdns/dnsrecords.hh +++ b/pdns/dnsrecords.hh @@ -273,7 +273,7 @@ class DNAMERecordContent : public DNSRecordContent public: includeboilerplate(DNAME) DNAMERecordContent(const DNSName& content) : d_content(content){} - DNSName getTarget() const { return d_content; } + const DNSName& getTarget() const { return d_content; } private: DNSName d_content; }; From d083a8af13e721457db00bfb12ce31f9973b3f9f Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 5 Mar 2019 16:43:00 +0100 Subject: [PATCH 025/127] DNAME: CNAME synthesis tests --- pdns/recursordist/test-syncres_cc.cc | 54 +++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index dbe8166b6352..7d707fa753e8 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -10804,9 +10804,15 @@ BOOST_AUTO_TEST_CASE(test_dname_processing) { const DNSName target("dname.powerdns.com."); const DNSName cnameTarget("dname.powerdns.net"); + const DNSName uncachedTarget("dname-uncached.powerdns.com."); + const DNSName uncachedCNAMETarget("dname-uncached.powerdns.net."); + + const DNSName synthCNAME("cname-uncached.powerdns.com."); + const DNSName synthCNAMETarget("cname-uncached.powerdns.net."); + size_t queries = 0; - sr->setAsyncCallback([dnameOwner, dnameTarget, target, cnameTarget, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, LWResult* res, bool* chained) { + sr->setAsyncCallback([dnameOwner, dnameTarget, target, cnameTarget, uncachedTarget, uncachedCNAMETarget, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, LWResult* res, bool* chained) { queries++; if (isRootServer(ip)) { @@ -10834,6 +10840,10 @@ BOOST_AUTO_TEST_CASE(test_dname_processing) { setLWResult(res, 0, true, false, false); addRecordToLW(res, domain, QType::A, "192.0.2.2"); } + if (domain == uncachedCNAMETarget) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, domain, QType::A, "192.0.2.3"); + } return 1; } return 0; @@ -10875,6 +10885,48 @@ BOOST_AUTO_TEST_CASE(test_dname_processing) { BOOST_CHECK(ret[2].d_type == QType::A); BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget); + + // Check if we correctly return a synthesizd CNAME, should send out just 1 more query + ret.clear(); + res = sr->beginResolve(uncachedTarget, QType(QType::A), QClass::IN, ret); + + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(queries, 5); + + BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(ret[1].d_name, uncachedTarget); + BOOST_CHECK_EQUAL(getRR(ret[1])->getTarget(), uncachedCNAMETarget); + + BOOST_CHECK(ret[2].d_type == QType::A); + BOOST_CHECK_EQUAL(ret[2].d_name, uncachedCNAMETarget); + + // Check if we correctly return the DNAME from cache when asked + ret.clear(); + res = sr->beginResolve(dnameOwner, QType(QType::DNAME), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(queries, 5); + + BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + // Check if we correctly return the synthesized CNAME from cache when asked + ret.clear(); + res = sr->beginResolve(synthCNAME, QType(QType::CNAME), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(queries, 5); + + BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::CNAME); + BOOST_CHECK(ret[1].d_name == synthCNAME); + BOOST_CHECK_EQUAL(getRR(ret[1])->getTarget(), synthCNAMETarget); } BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { From 71e9cc53742cf57571276f484d21e0606e077ceb Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 5 Mar 2019 16:45:35 +0100 Subject: [PATCH 026/127] DNAME: skip one unneeded cache lookup --- pdns/syncres.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 28216fbb0e49..81fa46916008 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -952,13 +952,14 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector do { dnameName.prependRawLabel(labels.back()); labels.pop_back(); + if (dnameName == qname && qtype != QType::DNAME) { // The client does not want a DNAME, but we've reached the QNAME already. So there is no match + break; + } LOG(prefix<get(d_now.tv_sec, dnameName, QType(QType::DNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) { - if (dnameName != qname && qtype != QType::DNAME) { - foundName = dnameName; - foundQT = QType(QType::DNAME); - break; - } + foundName = dnameName; + foundQT = QType(QType::DNAME); + break; } } while(!labels.empty()); } From 3643203e4a0b7c1360fcb7b3b1f1fbb5b76cead0 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 5 Mar 2019 16:56:31 +0100 Subject: [PATCH 027/127] DNAME: use BOOST_REQUIRE where needed --- pdns/recursordist/test-syncres_cc.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index 7d707fa753e8..cba955f3c996 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -10857,7 +10857,7 @@ BOOST_AUTO_TEST_CASE(test_dname_processing) { BOOST_CHECK_EQUAL(queries, 4); - BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); BOOST_CHECK(ret[0].d_name == dnameOwner); BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); @@ -10876,7 +10876,7 @@ BOOST_AUTO_TEST_CASE(test_dname_processing) { BOOST_CHECK_EQUAL(queries, 4); - BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); BOOST_CHECK(ret[0].d_name == dnameOwner); BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); @@ -10893,11 +10893,11 @@ BOOST_AUTO_TEST_CASE(test_dname_processing) { BOOST_CHECK_EQUAL(res, RCode::NoError); BOOST_CHECK_EQUAL(queries, 5); - BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); BOOST_CHECK(ret[0].d_name == dnameOwner); BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); - BOOST_CHECK(ret[1].d_type == QType::CNAME); + BOOST_REQUIRE(ret[1].d_type == QType::CNAME); BOOST_CHECK_EQUAL(ret[1].d_name, uncachedTarget); BOOST_CHECK_EQUAL(getRR(ret[1])->getTarget(), uncachedCNAMETarget); @@ -10910,7 +10910,7 @@ BOOST_AUTO_TEST_CASE(test_dname_processing) { BOOST_CHECK_EQUAL(res, RCode::NoError); BOOST_CHECK_EQUAL(queries, 5); - BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); BOOST_CHECK(ret[0].d_name == dnameOwner); BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); @@ -10920,11 +10920,11 @@ BOOST_AUTO_TEST_CASE(test_dname_processing) { BOOST_CHECK_EQUAL(res, RCode::NoError); BOOST_CHECK_EQUAL(queries, 5); - BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); BOOST_CHECK(ret[0].d_name == dnameOwner); BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); - BOOST_CHECK(ret[1].d_type == QType::CNAME); + BOOST_REQUIRE(ret[1].d_type == QType::CNAME); BOOST_CHECK(ret[1].d_name == synthCNAME); BOOST_CHECK_EQUAL(getRR(ret[1])->getTarget(), synthCNAMETarget); } @@ -11032,11 +11032,11 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { BOOST_CHECK_EQUAL(queries, 11); - BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); BOOST_CHECK(ret[0].d_name == dnameOwner); BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); - BOOST_CHECK(ret[1].d_type == QType::RRSIG); + BOOST_REQUIRE(ret[1].d_type == QType::RRSIG); BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner); BOOST_CHECK(ret[2].d_type == QType::CNAME); @@ -11058,7 +11058,7 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { BOOST_CHECK_EQUAL(queries, 11); - BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); BOOST_CHECK(ret[0].d_name == dnameOwner); BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); @@ -11177,7 +11177,7 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_insecure) { BOOST_CHECK_EQUAL(queries, 9); - BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); BOOST_CHECK(ret[0].d_name == dnameOwner); BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); @@ -11200,7 +11200,7 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_insecure) { BOOST_CHECK_EQUAL(queries, 9); - BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); BOOST_CHECK(ret[0].d_name == dnameOwner); BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); From 623b30032972ce6705d72ba9448ceeaa817651df Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 5 Mar 2019 17:02:07 +0100 Subject: [PATCH 028/127] DNAME: remove useless piece of code --- pdns/syncres.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 81fa46916008..4456a48156e2 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -2405,10 +2405,6 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr expectSignature = false; } - if (isDNAMEAnswer && !isAA && i->first.place == DNSResourceRecord::ANSWER && i->first.type == QType::DNAME && qname.isPartOf(i->first.name)) { - isAA = true; - } - if (isCNAMEAnswer && i->first.place == DNSResourceRecord::AUTHORITY && i->first.type == QType::NS && auth == i->first.name) { /* These NS can't be authoritative since we have a CNAME answer for which (see above) only the record describing that alias is necessarily authoritative. From ee9b179cd4d5ff746421d32834697cad36dfee19 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 5 Mar 2019 17:07:57 +0100 Subject: [PATCH 029/127] DNAME: reserve extra spot in ret for synthesized CNAME --- pdns/syncres.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 4456a48156e2..f5d14e1307ad 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -1012,7 +1012,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector dr.d_ttl -= d_now.tv_sec; dr.d_ttl = std::min(dr.d_ttl, capTTL); const uint32_t ttl = dr.d_ttl; - ret.reserve(ret.size() + 1 + signatures.size() + authorityRecs.size()); + ret.reserve(ret.size() + 2 + signatures.size() + authorityRecs.size()); ret.push_back(dr); for(const auto& signature : signatures) { From 92934404e837b81359601b4b16f2d3c83d9050c5 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Wed, 6 Mar 2019 10:37:39 +0100 Subject: [PATCH 030/127] DNAME: fix regression test --- regression-tests.recursor-dnssec/basicDNSSEC.py | 1 - 1 file changed, 1 deletion(-) diff --git a/regression-tests.recursor-dnssec/basicDNSSEC.py b/regression-tests.recursor-dnssec/basicDNSSEC.py index aef9fc2a3436..04fc82308914 100644 --- a/regression-tests.recursor-dnssec/basicDNSSEC.py +++ b/regression-tests.recursor-dnssec/basicDNSSEC.py @@ -271,4 +271,3 @@ def testInsecureDNAMEtoSecureNXDomain(self): self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO']) self.assertRRsetInAnswer(res, expectedCNAME) self.assertRRsetInAnswer(res, expectedDNAME) - self.assertMatchingRRSIGInAnswer(res, expectedDNAME) From ee9360745e74862a148e35b6a1b47474f420c678 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Wed, 6 Mar 2019 13:34:21 +0100 Subject: [PATCH 031/127] DNAME: synthesize and check CNAME from response --- pdns/syncres.cc | 49 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/pdns/syncres.cc b/pdns/syncres.cc index f5d14e1307ad..db3dd5512b46 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -1045,11 +1045,20 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector } auto dnameSuffix = dnameRR->getTarget(); DNSName targetPrefix = qname.makeRelative(foundName); - dr.d_type = QType::CNAME; - dr.d_name = targetPrefix + foundName; - newTarget = targetPrefix + dnameSuffix; - dr.d_content = std::make_shared(CNAMERecordContent(newTarget)); - ret.push_back(dr); + try { + dr.d_type = QType::CNAME; + dr.d_name = targetPrefix + foundName; + newTarget = targetPrefix + dnameSuffix; + dr.d_content = std::make_shared(CNAMERecordContent(newTarget)); + ret.push_back(dr); + } catch (const std::exception &e) { + // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2) + // But this is consistent with processRecords + throw ImmediateServFailException("Unable to perform DNAME substitution(DNAME owner: '" + foundName.toLogString() + + "', DNAME target: '" + dnameSuffix.toLogString() + "', substituted name: '" + + targetPrefix.toLogString() + "." + dnameSuffix.toLogString() + + "' : " + e.what()); + } LOG(prefix< 0 && // CNAME or DNAME answer s_redirectionQTypes.count(qtype.getCode()) == 0) { // But not in response to a CNAME or DNAME query if (rec.d_type == QType::CNAME && rec.d_name == qname) { + if (!dnameOwner.empty()) { // We synthesize ourselves + continue; + } ret.push_back(rec); if (auto content = getRR(rec)) { newtarget=content->getTarget(); @@ -2608,6 +2621,17 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co if (auto content = getRR(rec)) { dnameOwner = rec.d_name; dnameTarget = content->getTarget(); + dnameTTL = rec.d_ttl; + try { + newtarget = qname.makeRelative(dnameOwner) + dnameTarget; + } catch (const std::exception &e) { + // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2) + // But there is no way to set the RCODE from this function + throw ImmediateServFailException("Unable to perform DNAME substitution(DNAME owner: '" + dnameOwner.toLogString() + + "', DNAME target: '" + dnameTarget.toLogString() + "', substituted name: '" + + qname.makeRelative(dnameOwner).toLogString() + "." + dnameTarget.toLogString() + + "' : " + e.what()); + } } } } @@ -2760,13 +2784,14 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co } } - if (!dnameTarget.empty() && !newtarget.empty()) { - DNSName substTarget = qname.makeRelative(dnameOwner) + dnameTarget; - if (substTarget != newtarget) { - throw ImmediateServFailException("Received wrong DNAME substitution. qname='" + qname.toLogString() + - "', DNAME owner='" + dnameOwner.toLogString() + "', DNAME target='" + dnameTarget.toLogString() + - "', received CNAME='" + newtarget.toLogString() + "', substituted CNAME='" + substTarget.toLogString() + "'"); - } + if (!dnameTarget.empty()) { + // Synthesize a CNAME + auto cnamerec = DNSRecord(); + cnamerec.d_name = qname; + cnamerec.d_type = QType::CNAME; + cnamerec.d_ttl = dnameTTL; + cnamerec.d_content = std::make_shared(CNAMERecordContent(newtarget)); + ret.push_back(cnamerec); } return done; } From 44671635761cc86b789cfcfc31c4b32b009d9706 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Wed, 6 Mar 2019 13:36:33 +0100 Subject: [PATCH 032/127] DNAME: add some comments to the unit tests --- pdns/recursordist/test-syncres_cc.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index cba955f3c996..fbdb287a5b42 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -11000,7 +11000,7 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { setLWResult(res, 0, true, false, false); addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString()); addRRSIG(keys, res->d_records, dnameOwner, 300); - addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); + addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); // CNAME from a DNAME is not signed return 1; } } else if (ip == ComboAddress("192.0.2.2:53")) { @@ -11152,7 +11152,7 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_insecure) { setLWResult(res, 0, true, false, false); addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString()); addRRSIG(keys, res->d_records, dnameOwner, 300); - addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); + addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); // CNAME from a DNAME is not signed return 1; } } else if (ip == ComboAddress("192.0.2.2:53")) { From 3e0d25f37e3f6097ca95da856f30efa3530bbd1f Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 8 Mar 2019 13:59:03 +0100 Subject: [PATCH 033/127] Update pdns/syncres.cc Co-Authored-By: pieterlexis --- pdns/syncres.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdns/syncres.cc b/pdns/syncres.cc index db3dd5512b46..e7002c20f7a0 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -1043,7 +1043,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector if (dnameRR == nullptr) { throw ImmediateServFailException("Unable to get record content for "+foundName.toLogString()+"|DNAME cache entry"); } - auto dnameSuffix = dnameRR->getTarget(); + const auto& dnameSuffix = dnameRR->getTarget(); DNSName targetPrefix = qname.makeRelative(foundName); try { dr.d_type = QType::CNAME; From 88bc98dc2346cec87f11059d24719e93be427878 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Mon, 11 Mar 2019 10:13:01 +0100 Subject: [PATCH 034/127] DNAME: Add comment in the tests --- pdns/recursordist/test-syncres_cc.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index fbdb287a5b42..a02f9ba3d375 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -10955,6 +10955,9 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure) { sr->setAsyncCallback([dnameOwner, dnameTarget, target, cnameTarget, keys, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, LWResult* res, bool* chained) { queries++; + /* We don't use the genericDSAndDNSKEYHandler here, as it would deny names existing at the wrong level of the tree, due to the way computeZoneCuts works + * As such, we need to do some more work to make the answers correct. + */ if (isRootServer(ip)) { if (domain.countLabels() == 0 && type == QType::DNSKEY) { // .|DNSKEY From 10ea4320ab0653f1dc875e4bed5b27bfb18511a4 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Mon, 11 Mar 2019 10:25:02 +0100 Subject: [PATCH 035/127] DNAME: add test for CNAME synthesis when none is sent --- pdns/recursordist/test-syncres_cc.cc | 85 ++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index a02f9ba3d375..d0a817896271 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -11217,6 +11217,91 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_insecure) { BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget); } +BOOST_AUTO_TEST_CASE(test_dname_processing_no_CNAME) { + std::unique_ptr sr; + initSR(sr); + + primeHints(); + + const DNSName dnameOwner("powerdns.com"); + const DNSName dnameTarget("powerdns.net"); + + const DNSName target("dname.powerdns.com."); + const DNSName cnameTarget("dname.powerdns.net"); + + size_t queries = 0; + + sr->setAsyncCallback([dnameOwner, dnameTarget, target, cnameTarget, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, LWResult* res, bool* chained) { + queries++; + + if (isRootServer(ip)) { + if (domain.isPartOf(dnameOwner)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, dnameOwner, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + if (domain.isPartOf(dnameTarget)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, dnameTarget, QType::NS, "b.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "b.gtld-servers.net.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + } else if (ip == ComboAddress("192.0.2.1:53")) { + if (domain == target) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString()); + // No CNAME, recursor should synth + return 1; + } + } else if (ip == ComboAddress("192.0.2.2:53")) { + if (domain == cnameTarget) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, domain, QType::A, "192.0.2.2"); + } + return 1; + } + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_REQUIRE_EQUAL(ret.size(), 3); + + BOOST_CHECK_EQUAL(queries, 4); + + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(ret[1].d_name, target); + + BOOST_CHECK(ret[2].d_type == QType::A); + BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget); + + // Now check the cache + ret.clear(); + res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_REQUIRE_EQUAL(ret.size(), 3); + + BOOST_CHECK_EQUAL(queries, 4); + + BOOST_REQUIRE(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(ret[1].d_name, target); + + BOOST_CHECK(ret[2].d_type == QType::A); + BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget); +} + /* // cerr<<"asyncresolve called to ask "< Date: Wed, 13 Mar 2019 10:43:23 +0100 Subject: [PATCH 036/127] DNAME: find CNAME first --- pdns/syncres.cc | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/pdns/syncres.cc b/pdns/syncres.cc index e7002c20f7a0..fc8dc8d9fe5b 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -944,7 +944,14 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector DNSName foundName; QType foundQT = QType(0); // 0 == QTYPE::ENT - if (qname != g_rootdnsname) { + LOG(prefix<get(d_now.tv_sec, qname, QType(QType::CNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) { + foundName = qname; + foundQT = QType(QType::CNAME); + } + + if (foundName.empty() && qname != g_rootdnsname) { // look for a DNAME cache hit auto labels = qname.getRawLabels(); DNSName dnameName(g_rootdnsname); @@ -964,15 +971,6 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector } while(!labels.empty()); } - if (foundName.empty()) { - LOG(prefix<get(d_now.tv_sec, qname, QType(QType::CNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) { - foundName = qname; - foundQT = QType(QType::CNAME); - } - } - if(!foundName.empty()) { for(auto j=cset.cbegin() ; j != cset.cend() ; ++j) { if (j->d_class != QClass::IN) { From 29ec25aa434db2d57480ac553c2dae7a09dae37d Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Wed, 13 Mar 2019 10:59:54 +0100 Subject: [PATCH 037/127] DNAME: properly remove CNAME from auth answers --- pdns/syncres.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pdns/syncres.cc b/pdns/syncres.cc index fc8dc8d9fe5b..dbb2fd91e9e0 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -2620,6 +2620,15 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co dnameOwner = rec.d_name; dnameTarget = content->getTarget(); dnameTTL = rec.d_ttl; + if (!newtarget.empty()) { // We had a CNAME before, remove it from ret so we don't cache it + ret.erase(std::remove_if( + ret.begin(), + ret.end(), + [&qname](DNSRecord& rr) { + return (rr.d_place == DNSResourceRecord::ANSWER && rr.d_type == QType::CNAME && rr.d_name == qname); + }), + ret.end()); + } try { newtarget = qname.makeRelative(dnameOwner) + dnameTarget; } catch (const std::exception &e) { From 4d76766d0b0158a12c9ca94d047f0015e5f23e23 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 12 Mar 2019 12:09:53 +0100 Subject: [PATCH 038/127] pdnsutil: error on broken IPs in masters field --- pdns/backends/gsql/gsqlbackend.cc | 22 +++++++++++----------- pdns/pdnsutil.cc | 17 ++++++++++++++--- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index 95f850c8116d..29c890a8ab4a 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -292,14 +292,16 @@ bool GSQLBackend::getDomainInfo(const DNSName &domain, DomainInfo &di, bool getS } catch (...) { return false; } + string type=d_result[0][5]; + di.account=d_result[0][6]; + di.kind = DomainInfo::stringToKind(type); + vector masters; stringtok(masters, d_result[0][2], " ,\t"); for(const auto& m : masters) di.masters.emplace_back(m, 53); di.last_check=pdns_stou(d_result[0][3]); di.notified_serial = pdns_stou(d_result[0][4]); - string type=d_result[0][5]; - di.account=d_result[0][6]; di.backend=this; di.serial = 0; @@ -316,8 +318,6 @@ bool GSQLBackend::getDomainInfo(const DNSName &domain, DomainInfo &di, bool getS } } - di.kind = DomainInfo::stringToKind(type); - return true; } @@ -1265,6 +1265,13 @@ void GSQLBackend::getAllDomains(vector *domains, bool include_disabl } catch (...) { continue; } + + if (pdns_iequals(row[3], "MASTER")) + di.kind = DomainInfo::Master; + else if (pdns_iequals(row[3], "SLAVE")) + di.kind = DomainInfo::Slave; + else + di.kind = DomainInfo::Native; if (!row[4].empty()) { vector masters; @@ -1280,13 +1287,6 @@ void GSQLBackend::getAllDomains(vector *domains, bool include_disabl di.last_check = pdns_stou(row[6]); di.account = row[7]; - if (pdns_iequals(row[3], "MASTER")) - di.kind = DomainInfo::Master; - else if (pdns_iequals(row[3], "SLAVE")) - di.kind = DomainInfo::Slave; - else - di.kind = DomainInfo::Native; - di.backend = this; domains->push_back(di); diff --git a/pdns/pdnsutil.cc b/pdns/pdnsutil.cc index cd753330f5d5..e7de035cff2b 100644 --- a/pdns/pdnsutil.cc +++ b/pdns/pdnsutil.cc @@ -239,10 +239,23 @@ bool rectifyAllZones(DNSSECKeeper &dk, bool quiet = false) int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone, const vector* suppliedrecords=0) { + uint64_t numerrors=0, numwarnings=0; + + DomainInfo di; + try { + B.getDomainInfo(zone, di); + } catch(const PDNSException &e) { + if (di.kind == DomainInfo::Slave) { + cout<<"[Error] non-IP address for masters: "< checkKeyErrors; bool validKeys=dk.checkKeys(zone, &checkKeyErrors); - uint64_t numerrors=0, numwarnings=0; - if (haveNSEC3) { if(isSecure && zone.wirelength() > 222) { numerrors++; From 27efa4be12a39dc25a5d5fb768d035ceb864628f Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 12 Mar 2019 12:47:26 +0100 Subject: [PATCH 039/127] auth: report DomainInfo errors in the API --- pdns/ws-auth.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index d2c3f9ae0db3..df59b7eaf1d8 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -620,6 +620,11 @@ static void updateDomainSettingsFromDocument(UeberBackend& B, const DomainInfo& string master = value.string_value(); if (master.empty()) throw ApiException("Master can not be an empty string"); + try { + ComboAddress m(master); + } catch (const PDNSException &e) { + throw ApiException("Master (" + master + ") is not an IP address: " + e.reason); + } zonemaster.push_back(master); } @@ -1689,8 +1694,12 @@ static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp) { UeberBackend B; DomainInfo di; - if (!B.getDomainInfo(zonename, di)) { - throw HttpNotFoundException(); + try { + if (!B.getDomainInfo(zonename, di)) { + throw HttpNotFoundException(); + } + } catch(const PDNSException &e) { + throw HttpInternalServerErrorException("Could not retrieve Domain Info: " + e.reason); } if(req->method == "PUT") { From 2f9dad3733e88b0eb9170560ee3004e444d594b7 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 12 Mar 2019 14:28:49 +0100 Subject: [PATCH 040/127] GSQLBackend::getUnfreshSlaveInfos: log data errors properly --- pdns/backends/gsql/gsqlbackend.cc | 46 +++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index 29c890a8ab4a..9287010f5117 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -341,23 +341,51 @@ void GSQLBackend::getUnfreshSlaveInfos(vector *unfreshDomains) for(const auto& row : d_result) { // id,name,master,last_check DomainInfo sd; - ASSERT_ROW_COLUMNS("info-all-slaves-query", row, 4); + try { + ASSERT_ROW_COLUMNS("info-all-slaves-query", row, 4); + } catch(const PDNSException &e) { + g_log< masters; - stringtok(masters, row[2], ", \t"); - for(const auto& m : masters) + vector masters; + stringtok(masters, row[2], ", \t"); + for(const auto& m : masters) { + try { sd.masters.emplace_back(m, 53); + } catch(const PDNSException &e) { + g_log< Date: Tue, 12 Mar 2019 16:26:13 +0100 Subject: [PATCH 041/127] Webserver: Don't swallow errors from getAllDomains --- pdns/backends/gsql/gsqlbackend.cc | 9 +++++++-- pdns/ws-auth.cc | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index 9287010f5117..b3af298619c7 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -1304,8 +1304,13 @@ void GSQLBackend::getAllDomains(vector *domains, bool include_disabl if (!row[4].empty()) { vector masters; stringtok(masters, row[4], " ,\t"); - for(const auto& m : masters) - di.masters.emplace_back(m, 53); + for(const auto& m : masters) { + try { + di.masters.emplace_back(m, 53); + } catch(const PDNSException &e) { + throw PDNSException("Could not parse master address (" + m + ") for zone '" + di.zone.toLogString() + "': " + e.reason); + } + } } SOAData sd; diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index df59b7eaf1d8..83f9f30b1619 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -1679,7 +1679,11 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) { domains.push_back(di); } } else { - B.getAllDomains(&domains, true); // incl. disabled + try { + B.getAllDomains(&domains, true); // incl. disabled + } catch(const PDNSException &e) { + throw HttpInternalServerErrorException("Could not retrieve all domain information: " + e.reason); + } } Json::array doc; From 51912a5e31553266a95fbaa9e1e442d3316ff3bf Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Fri, 15 Mar 2019 14:05:20 +0100 Subject: [PATCH 042/127] GSQLBackend: fix logging nits --- pdns/backends/gsql/gsqlbackend.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index b3af298619c7..7d6696d021ca 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -350,8 +350,8 @@ void GSQLBackend::getUnfreshSlaveInfos(vector *unfreshDomains) try { sd.zone = DNSName(row[1]); - } catch(const PDNSException &e) { - g_log< *domains, bool include_disabl continue; } - if (pdns_iequals(row[3], "MASTER")) + if (pdns_iequals(row[3], "MASTER")) { di.kind = DomainInfo::Master; - else if (pdns_iequals(row[3], "SLAVE")) + } else if (pdns_iequals(row[3], "SLAVE")) { di.kind = DomainInfo::Slave; - else + } else if (pdns_iequals(row[3], "NATIVE")) { di.kind = DomainInfo::Native; + } else { + g_log< masters; From 08be8a8a36b96ee17611aa7edb3143848f7bc573 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Fri, 15 Mar 2019 14:11:27 +0100 Subject: [PATCH 043/127] GSQLBackend::getAllDomains: don't throw on a broken master address --- pdns/backends/gsql/gsqlbackend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index 7d6696d021ca..412a3768cd1b 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -1312,7 +1312,7 @@ void GSQLBackend::getAllDomains(vector *domains, bool include_disabl try { di.masters.emplace_back(m, 53); } catch(const PDNSException &e) { - throw PDNSException("Could not parse master address (" + m + ") for zone '" + di.zone.toLogString() + "': " + e.reason); + g_log< Date: Fri, 15 Mar 2019 15:29:37 +0100 Subject: [PATCH 044/127] GSQLBackend::getUnfreshSlaveInfos: log row assertion only once --- pdns/backends/gsql/gsqlbackend.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index 412a3768cd1b..691ff810a575 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -339,12 +339,16 @@ void GSQLBackend::getUnfreshSlaveInfos(vector *unfreshDomains) vector allSlaves; + bool loggedAssertRowColumns = false; for(const auto& row : d_result) { // id,name,master,last_check DomainInfo sd; try { ASSERT_ROW_COLUMNS("info-all-slaves-query", row, 4); } catch(const PDNSException &e) { - g_log< Date: Wed, 27 Mar 2019 00:50:25 +0100 Subject: [PATCH 045/127] auth: add referral response tests for DS queries --- regression-tests.rootzone/tests/axfr/expected_result | 5 +++++ .../tests/axfr/expected_result.dnssec | 10 +++++++++- .../tests/axfr/expected_result.nsec3 | 8 ++++++++ .../tests/axfr/expected_result.nsec3-optout | 8 ++++++++ .../tests/ds-at-ent-from-glue/command | 1 + .../tests/ds-at-ent-from-glue/description | 2 ++ .../tests/ds-at-ent-from-glue/expected_result | 5 +++++ .../tests/ds-at-ent-from-glue/expected_result.dnssec | 6 ++++++ regression-tests.rootzone/tests/ds-at-ent/command | 1 + regression-tests.rootzone/tests/ds-at-ent/description | 1 + .../tests/ds-at-ent/expected_result | 5 +++++ .../tests/ds-at-ent/expected_result.dnssec | 6 ++++++ regression-tests.rootzone/tests/ds-at-glue/command | 1 + regression-tests.rootzone/tests/ds-at-glue/description | 1 + .../tests/ds-at-glue/expected_result | 5 +++++ .../tests/ds-at-glue/expected_result.dnssec | 6 ++++++ regression-tests.rootzone/zones/ROOT | 6 ++++++ 17 files changed, 76 insertions(+), 1 deletion(-) create mode 100755 regression-tests.rootzone/tests/ds-at-ent-from-glue/command create mode 100644 regression-tests.rootzone/tests/ds-at-ent-from-glue/description create mode 100644 regression-tests.rootzone/tests/ds-at-ent-from-glue/expected_result create mode 100644 regression-tests.rootzone/tests/ds-at-ent-from-glue/expected_result.dnssec create mode 100755 regression-tests.rootzone/tests/ds-at-ent/command create mode 100644 regression-tests.rootzone/tests/ds-at-ent/description create mode 100644 regression-tests.rootzone/tests/ds-at-ent/expected_result create mode 100644 regression-tests.rootzone/tests/ds-at-ent/expected_result.dnssec create mode 100755 regression-tests.rootzone/tests/ds-at-glue/command create mode 100644 regression-tests.rootzone/tests/ds-at-glue/description create mode 100644 regression-tests.rootzone/tests/ds-at-glue/expected_result create mode 100644 regression-tests.rootzone/tests/ds-at-glue/expected_result.dnssec diff --git a/regression-tests.rootzone/tests/axfr/expected_result b/regression-tests.rootzone/tests/axfr/expected_result index 0b76cb425d40..29b81b58ad28 100644 --- a/regression-tests.rootzone/tests/axfr/expected_result +++ b/regression-tests.rootzone/tests/axfr/expected_result @@ -5,6 +5,11 @@ a.gtld-servers.net. 172800 IN A 192.5.6.30 a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30 a.root-servers.net. 518400 IN A 198.41.0.4 a.root-servers.net. 518400 IN AAAA 2001:503:ba3e::2:30 +barney.advsys.co.uk. 172800 IN A 217.23.160.50 net. 172800 IN NS a.gtld-servers.net. net. 86400 IN DS 35886 8 2 7862b27f5f516ebe19680444d4ce5e762981931842c465f00236401d8bd973ee +nsa.nic.uk. 172800 IN A 156.154.100.3 +nsa.nic.uk. 172800 IN AAAA 2001:502:ad09::3 org. 172800 IN NS a.gtld-servers.net. +uk. 172800 IN NS nsa.nic.uk. +uk. 86400 IN DS 43876 8 2 a107ed2ac1bd14d924173bc7e827a1153582072394f9272ba37e2353bc659603 diff --git a/regression-tests.rootzone/tests/axfr/expected_result.dnssec b/regression-tests.rootzone/tests/axfr/expected_result.dnssec index f89385cf385a..db244622a2bc 100644 --- a/regression-tests.rootzone/tests/axfr/expected_result.dnssec +++ b/regression-tests.rootzone/tests/axfr/expected_result.dnssec @@ -11,11 +11,19 @@ a.gtld-servers.net. 172800 IN A 192.5.6.30 a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30 a.root-servers.net. 518400 IN A 198.41.0.4 a.root-servers.net. 518400 IN AAAA 2001:503:ba3e::2:30 +barney.advsys.co.uk. 172800 IN A 217.23.160.50 net. 172800 IN NS a.gtld-servers.net. net. 86400 IN DS 35886 8 2 7862b27f5f516ebe19680444d4ce5e762981931842c465f00236401d8bd973ee net. 86400 IN NSEC org. NS DS RRSIG NSEC net. 86400 IN RRSIG DS 13 1 86400 [expiry] [inception] [keytag] . ... net. 86400 IN RRSIG NSEC 13 1 86400 [expiry] [inception] [keytag] . ... +nsa.nic.uk. 172800 IN A 156.154.100.3 +nsa.nic.uk. 172800 IN AAAA 2001:502:ad09::3 org. 172800 IN NS a.gtld-servers.net. -org. 86400 IN NSEC . NS RRSIG NSEC +org. 86400 IN NSEC uk. NS RRSIG NSEC org. 86400 IN RRSIG NSEC 13 1 86400 [expiry] [inception] [keytag] . ... +uk. 172800 IN NS nsa.nic.uk. +uk. 86400 IN DS 43876 8 2 a107ed2ac1bd14d924173bc7e827a1153582072394f9272ba37e2353bc659603 +uk. 86400 IN NSEC . NS DS RRSIG NSEC +uk. 86400 IN RRSIG DS 13 1 86400 [expiry] [inception] [keytag] . ... +uk. 86400 IN RRSIG NSEC 13 1 86400 [expiry] [inception] [keytag] . ... diff --git a/regression-tests.rootzone/tests/axfr/expected_result.nsec3 b/regression-tests.rootzone/tests/axfr/expected_result.nsec3 index aa6d56d3c0f9..28e62f8985ad 100644 --- a/regression-tests.rootzone/tests/axfr/expected_result.nsec3 +++ b/regression-tests.rootzone/tests/axfr/expected_result.nsec3 @@ -13,11 +13,19 @@ a.gtld-servers.net. 172800 IN A 192.5.6.30 a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30 a.root-servers.net. 518400 IN A 198.41.0.4 a.root-servers.net. 518400 IN AAAA 2001:503:ba3e::2:30 +barney.advsys.co.uk. 172800 IN A 217.23.160.50 net. 172800 IN NS a.gtld-servers.net. net. 86400 IN DS 35886 8 2 7862b27f5f516ebe19680444d4ce5e762981931842c465f00236401d8bd973ee net. 86400 IN NSEC3 1 0 1 abcd [next owner] NS DS RRSIG net. 86400 IN RRSIG DS 13 1 86400 [expiry] [inception] [keytag] . ... net. 86400 IN RRSIG NSEC3 13 1 86400 [expiry] [inception] [keytag] . ... +nsa.nic.uk. 172800 IN A 156.154.100.3 +nsa.nic.uk. 172800 IN AAAA 2001:502:ad09::3 org. 172800 IN NS a.gtld-servers.net. org. 86400 IN NSEC3 1 0 1 abcd [next owner] NS org. 86400 IN RRSIG NSEC3 13 1 86400 [expiry] [inception] [keytag] . ... +uk. 172800 IN NS nsa.nic.uk. +uk. 86400 IN DS 43876 8 2 a107ed2ac1bd14d924173bc7e827a1153582072394f9272ba37e2353bc659603 +uk. 86400 IN NSEC3 1 0 1 abcd [next owner] NS DS RRSIG +uk. 86400 IN RRSIG DS 13 1 86400 [expiry] [inception] [keytag] . ... +uk. 86400 IN RRSIG NSEC3 13 1 86400 [expiry] [inception] [keytag] . ... diff --git a/regression-tests.rootzone/tests/axfr/expected_result.nsec3-optout b/regression-tests.rootzone/tests/axfr/expected_result.nsec3-optout index ed8d610f2cfe..d86c7120534f 100644 --- a/regression-tests.rootzone/tests/axfr/expected_result.nsec3-optout +++ b/regression-tests.rootzone/tests/axfr/expected_result.nsec3-optout @@ -13,9 +13,17 @@ a.gtld-servers.net. 172800 IN A 192.5.6.30 a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30 a.root-servers.net. 518400 IN A 198.41.0.4 a.root-servers.net. 518400 IN AAAA 2001:503:ba3e::2:30 +barney.advsys.co.uk. 172800 IN A 217.23.160.50 net. 172800 IN NS a.gtld-servers.net. net. 86400 IN DS 35886 8 2 7862b27f5f516ebe19680444d4ce5e762981931842c465f00236401d8bd973ee net. 86400 IN NSEC3 1 1 1 abcd [next owner] NS DS RRSIG net. 86400 IN RRSIG DS 13 1 86400 [expiry] [inception] [keytag] . ... net. 86400 IN RRSIG NSEC3 13 1 86400 [expiry] [inception] [keytag] . ... +nsa.nic.uk. 172800 IN A 156.154.100.3 +nsa.nic.uk. 172800 IN AAAA 2001:502:ad09::3 org. 172800 IN NS a.gtld-servers.net. +uk. 172800 IN NS nsa.nic.uk. +uk. 86400 IN DS 43876 8 2 a107ed2ac1bd14d924173bc7e827a1153582072394f9272ba37e2353bc659603 +uk. 86400 IN NSEC3 1 1 1 abcd [next owner] NS DS RRSIG +uk. 86400 IN RRSIG DS 13 1 86400 [expiry] [inception] [keytag] . ... +uk. 86400 IN RRSIG NSEC3 13 1 86400 [expiry] [inception] [keytag] . ... diff --git a/regression-tests.rootzone/tests/ds-at-ent-from-glue/command b/regression-tests.rootzone/tests/ds-at-ent-from-glue/command new file mode 100755 index 000000000000..5eb6bf5cd5f1 --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-ent-from-glue/command @@ -0,0 +1 @@ +cleandig co.uk DS diff --git a/regression-tests.rootzone/tests/ds-at-ent-from-glue/description b/regression-tests.rootzone/tests/ds-at-ent-from-glue/description new file mode 100644 index 000000000000..f73191c97459 --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-ent-from-glue/description @@ -0,0 +1,2 @@ +A DS query at empty non-terminal (derived from glue) level. +Should result in a referral response. diff --git a/regression-tests.rootzone/tests/ds-at-ent-from-glue/expected_result b/regression-tests.rootzone/tests/ds-at-ent-from-glue/expected_result new file mode 100644 index 000000000000..c6b505054f7d --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-ent-from-glue/expected_result @@ -0,0 +1,5 @@ +1 uk. IN NS 172800 nsa.nic.uk. +2 nsa.nic.uk. IN A 172800 156.154.100.3 +2 nsa.nic.uk. IN AAAA 172800 2001:502:ad09::3 +Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 0, opcode: 0 +Reply to question for qname='co.uk.', qtype=DS diff --git a/regression-tests.rootzone/tests/ds-at-ent-from-glue/expected_result.dnssec b/regression-tests.rootzone/tests/ds-at-ent-from-glue/expected_result.dnssec new file mode 100644 index 000000000000..f714a769c8f3 --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-ent-from-glue/expected_result.dnssec @@ -0,0 +1,6 @@ +1 uk. IN DS 86400 43876 8 2 a107ed2ac1bd14d924173bc7e827a1153582072394f9272ba37e2353bc659603 +1 uk. IN NS 172800 nsa.nic.uk. +2 nsa.nic.uk. IN A 172800 156.154.100.3 +2 nsa.nic.uk. IN AAAA 172800 2001:502:ad09::3 +Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 0, opcode: 0 +Reply to question for qname='co.uk.', qtype=DS diff --git a/regression-tests.rootzone/tests/ds-at-ent/command b/regression-tests.rootzone/tests/ds-at-ent/command new file mode 100755 index 000000000000..5eb6bf5cd5f1 --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-ent/command @@ -0,0 +1 @@ +cleandig co.uk DS diff --git a/regression-tests.rootzone/tests/ds-at-ent/description b/regression-tests.rootzone/tests/ds-at-ent/description new file mode 100644 index 000000000000..77ce502b1966 --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-ent/description @@ -0,0 +1 @@ +A DS query at empty non-terminal level. Should result in a referral response. diff --git a/regression-tests.rootzone/tests/ds-at-ent/expected_result b/regression-tests.rootzone/tests/ds-at-ent/expected_result new file mode 100644 index 000000000000..c6b505054f7d --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-ent/expected_result @@ -0,0 +1,5 @@ +1 uk. IN NS 172800 nsa.nic.uk. +2 nsa.nic.uk. IN A 172800 156.154.100.3 +2 nsa.nic.uk. IN AAAA 172800 2001:502:ad09::3 +Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 0, opcode: 0 +Reply to question for qname='co.uk.', qtype=DS diff --git a/regression-tests.rootzone/tests/ds-at-ent/expected_result.dnssec b/regression-tests.rootzone/tests/ds-at-ent/expected_result.dnssec new file mode 100644 index 000000000000..f714a769c8f3 --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-ent/expected_result.dnssec @@ -0,0 +1,6 @@ +1 uk. IN DS 86400 43876 8 2 a107ed2ac1bd14d924173bc7e827a1153582072394f9272ba37e2353bc659603 +1 uk. IN NS 172800 nsa.nic.uk. +2 nsa.nic.uk. IN A 172800 156.154.100.3 +2 nsa.nic.uk. IN AAAA 172800 2001:502:ad09::3 +Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 0, opcode: 0 +Reply to question for qname='co.uk.', qtype=DS diff --git a/regression-tests.rootzone/tests/ds-at-glue/command b/regression-tests.rootzone/tests/ds-at-glue/command new file mode 100755 index 000000000000..37428bf1605f --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-glue/command @@ -0,0 +1 @@ +cleandig barney.advsys.co.uk DS diff --git a/regression-tests.rootzone/tests/ds-at-glue/description b/regression-tests.rootzone/tests/ds-at-glue/description new file mode 100644 index 000000000000..9ed8708277b2 --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-glue/description @@ -0,0 +1 @@ +A DS query at glue level. Should result in a referral response. diff --git a/regression-tests.rootzone/tests/ds-at-glue/expected_result b/regression-tests.rootzone/tests/ds-at-glue/expected_result new file mode 100644 index 000000000000..8bff8257849d --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-glue/expected_result @@ -0,0 +1,5 @@ +1 uk. IN NS 172800 nsa.nic.uk. +2 nsa.nic.uk. IN A 172800 156.154.100.3 +2 nsa.nic.uk. IN AAAA 172800 2001:502:ad09::3 +Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 0, opcode: 0 +Reply to question for qname='barney.advsys.co.uk.', qtype=DS diff --git a/regression-tests.rootzone/tests/ds-at-glue/expected_result.dnssec b/regression-tests.rootzone/tests/ds-at-glue/expected_result.dnssec new file mode 100644 index 000000000000..07271b403738 --- /dev/null +++ b/regression-tests.rootzone/tests/ds-at-glue/expected_result.dnssec @@ -0,0 +1,6 @@ +1 uk. IN DS 86400 43876 8 2 a107ed2ac1bd14d924173bc7e827a1153582072394f9272ba37e2353bc659603 +1 uk. IN NS 172800 nsa.nic.uk. +2 nsa.nic.uk. IN A 172800 156.154.100.3 +2 nsa.nic.uk. IN AAAA 172800 2001:502:ad09::3 +Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 0, opcode: 0 +Reply to question for qname='barney.advsys.co.uk.', qtype=DS diff --git a/regression-tests.rootzone/zones/ROOT b/regression-tests.rootzone/zones/ROOT index b183bdd63f01..8fd586e68b11 100644 --- a/regression-tests.rootzone/zones/ROOT +++ b/regression-tests.rootzone/zones/ROOT @@ -11,3 +11,9 @@ org. 172800 IN NS a.gtld-servers.net. a.gtld-servers.net. 172800 IN A 192.5.6.30 a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e:0:0:0:2:30 + +uk. 86400 IN DS 43876 8 2 A107ED2AC1BD14D924173BC7E827A1153582072394F9272BA37E2353BC659603 +uk. 172800 IN NS nsa.nic.uk. +nsa.nic.uk. 172800 IN AAAA 2001:502:ad09::3 +nsa.nic.uk. 172800 IN A 156.154.100.3 +barney.advsys.co.uk. 172800 IN A 217.23.160.50 From fd65c85dc9ae30fb8734cf6b5d6a81f6136e1778 Mon Sep 17 00:00:00 2001 From: Kees Monshouwer Date: Wed, 27 Mar 2019 00:51:57 +0100 Subject: [PATCH 046/127] auth: fix referral response for DS queries --- pdns/packethandler.cc | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/pdns/packethandler.cc b/pdns/packethandler.cc index 72888474af8c..40ae41871d56 100644 --- a/pdns/packethandler.cc +++ b/pdns/packethandler.cc @@ -1448,19 +1448,38 @@ DNSPacket *PacketHandler::doQuestion(DNSPacket *p) return 0; } - if(rrset.empty()) { - DLOG(g_log<<"checking if qtype is DS"<qtype.getCode() == QType::DS) - { + + // referral for DS query + if(p->qtype.getCode() == QType::DS) { + DLOG(g_log<<"Qtype is DS"< Date: Wed, 3 Apr 2019 14:10:22 +0200 Subject: [PATCH 047/127] A way to fix https://github.com/PowerDNS/pdns/issues/7646. It might not be the right place, though, but it prevents fatal exception on unparseable A (or AAAA) addresss for nameserver addresses needed to send notifies. --- pdns/communicator.hh | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/pdns/communicator.hh b/pdns/communicator.hh index 5413e4dacf23..2cb0f70feb3c 100644 --- a/pdns/communicator.hh +++ b/pdns/communicator.hh @@ -259,10 +259,25 @@ public: if(b) { b->lookup(QType(QType::ANY),name); - DNSZoneRecord rr; - while(b->get(rr)) - if(rr.dr.d_type == QType::A || rr.dr.d_type==QType::AAAA) - addresses.push_back(rr.dr.d_content->getZoneRepresentation()); // SOL if you have a CNAME for an NS + bool ok; + do { + DNSZoneRecord rr; + try { + ok = b->get(rr); + } + catch (PDNSException &ae) { + g_log << Logger::Error << "Skipping record: " << ae.reason << endl; + continue; + } + catch (std::exception &e) { + g_log << Logger::Error << "Skipping record: " << e.what() << endl; + continue; + } + if (ok) { + if (rr.dr.d_type == QType::A || rr.dr.d_type == QType::AAAA) + addresses.push_back(rr.dr.d_content->getZoneRepresentation()); // SOL if you have a CNAME for an NS + } + } while (ok); } return addresses; } From cb22b82e6e97374e0eadcc03c56dd8e7f1328bf3 Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Wed, 3 Apr 2019 16:00:12 +0200 Subject: [PATCH 048/127] Rearrange; to avoid uninitialized var and bail out after exception, b might be inconsistent in that case. --- pdns/communicator.hh | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/pdns/communicator.hh b/pdns/communicator.hh index 2cb0f70feb3c..831b2cecf534 100644 --- a/pdns/communicator.hh +++ b/pdns/communicator.hh @@ -258,26 +258,25 @@ public: this->resolve_name(&addresses, name); if(b) { - b->lookup(QType(QType::ANY),name); - bool ok; - do { + b->lookup(QType(QType::ANY),name); + while (true) { + try { DNSZoneRecord rr; - try { - ok = b->get(rr); - } - catch (PDNSException &ae) { - g_log << Logger::Error << "Skipping record: " << ae.reason << endl; - continue; - } - catch (std::exception &e) { - g_log << Logger::Error << "Skipping record: " << e.what() << endl; - continue; - } - if (ok) { - if (rr.dr.d_type == QType::A || rr.dr.d_type == QType::AAAA) - addresses.push_back(rr.dr.d_content->getZoneRepresentation()); // SOL if you have a CNAME for an NS - } - } while (ok); + if (!b->get(rr)) + break; + if (rr.dr.d_type == QType::A || rr.dr.d_type == QType::AAAA) + addresses.push_back(rr.dr.d_content->getZoneRepresentation()); // SOL if you have a CNAME for an NS + } + // After an exception, b can be inconsistent so break + catch (PDNSException &ae) { + g_log << Logger::Error << "Skipping record(s): " << ae.reason << endl; + break; + } + catch (std::exception &e) { + g_log << Logger::Error << "Skipping record(s): " << e.what() << endl; + break; + } + } } return addresses; } From c6e6b0559cfcff8537ee95c4f3d7c02477fd81a3 Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Wed, 3 Apr 2019 16:24:09 +0200 Subject: [PATCH 049/127] Better logging, so the operator knows where to look. --- pdns/communicator.hh | 6 +++--- pdns/mastercommunicator.cc | 2 +- pdns/tcpreceiver.cc | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pdns/communicator.hh b/pdns/communicator.hh index 831b2cecf534..c63534dc232b 100644 --- a/pdns/communicator.hh +++ b/pdns/communicator.hh @@ -251,7 +251,7 @@ private: class FindNS { public: - vector lookup(const DNSName &name, UeberBackend *b) + vector lookup(const DNSName &name, UeberBackend *b, const DNSName& zone) { vector addresses; @@ -269,11 +269,11 @@ public: } // After an exception, b can be inconsistent so break catch (PDNSException &ae) { - g_log << Logger::Error << "Skipping record(s): " << ae.reason << endl; + g_log << Logger::Error << "Could not lookup address for nameserver " << name << " in zone " << zone << ", cannot notify: " << ae.reason << endl; break; } catch (std::exception &e) { - g_log << Logger::Error << "Skipping record(s): " << e.what() << endl; + g_log << Logger::Error << "Could not lookup address for nameserver " << name << " in zone " << zone << ", cannot notify: " << e.what() << endl; break; } } diff --git a/pdns/mastercommunicator.cc b/pdns/mastercommunicator.cc index b5841132cfad..04803474994a 100644 --- a/pdns/mastercommunicator.cc +++ b/pdns/mastercommunicator.cc @@ -56,7 +56,7 @@ void CommunicatorClass::queueNotifyDomain(const DomainInfo& di, UeberBackend* B) nsset.insert(getRR(rr.dr)->getNS().toString()); for(set::const_iterator j=nsset.begin();j!=nsset.end();++j) { - vector nsips=fns.lookup(DNSName(*j), B); + vector nsips=fns.lookup(DNSName(*j), B, di.zone); if(nsips.empty()) g_log< q) while(B->get(rr)) nsset.insert(DNSName(rr.content)); for(const auto & j: nsset) { - vector nsips=fns.lookup(j, s_P->getBackend()); + vector nsips=fns.lookup(j, s_P->getBackend(),q->qdomain); for(vector::const_iterator k=nsips.begin();k!=nsips.end();++k) { // cerr<<"got "<<*k<<" from AUTO-NS"<getRemote().toString()) From 18e5a5ba7281ab7dd0397ec9189ce4fb9749dc35 Mon Sep 17 00:00:00 2001 From: phonedph1 Date: Wed, 3 Apr 2019 18:53:40 +0000 Subject: [PATCH 050/127] start to document the jsonstat --- .../docs/http-api/endpoint-jsonstat.rst | 60 +++++++++++++++++++ pdns/recursordist/docs/http-api/index.rst | 1 + 2 files changed, 61 insertions(+) create mode 100644 pdns/recursordist/docs/http-api/endpoint-jsonstat.rst diff --git a/pdns/recursordist/docs/http-api/endpoint-jsonstat.rst b/pdns/recursordist/docs/http-api/endpoint-jsonstat.rst new file mode 100644 index 000000000000..088afc168c19 --- /dev/null +++ b/pdns/recursordist/docs/http-api/endpoint-jsonstat.rst @@ -0,0 +1,60 @@ +jsonstat endpoint +================= + +.. http:get:: /jsonstat + + Get statistics from recursor in JSON format. + The ``Accept`` request header is ignored. + This endpoint accepts a ``command`` and ``name`` query for different statistics: + + * ``get-query-ring``: Retrieve statistics from the query subsection. ``name`` can be ``servfail-queries`` or ``queries``. + * ``get-remote-ring``: Retrieve statistics from the remotes subsection. ``name`` can be ``remotes``, ``bogus-remotes``, ``large-answer-remotes``, or ``timeouts``. + + **Example request**: + + .. sourcecode:: http + + GET /jsonstat?command=get-query-ring&name=servfail-queries HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + **Example request**: + + .. sourcecode:: http + + GET /jsonstat?command=get-query-ring&name=queries HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + **Example request**: + + .. sourcecode:: http + + GET /jsonstat?command=get-remote-ring&name=remotes HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + **Example request**: + + .. sourcecode:: http + + GET /jsonstat?command=get-remote-ring&name=bogus-remotes HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + **Example request**: + + .. sourcecode:: http + + GET /jsonstat?command=get-remote-ring&name=large-answer-remotes HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + **Example request**: + + .. sourcecode:: http + + GET /jsonstat?command=get-remote-ring&name=timeouts HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + diff --git a/pdns/recursordist/docs/http-api/index.rst b/pdns/recursordist/docs/http-api/index.rst index 41e6f6bce121..9fd799638717 100644 --- a/pdns/recursordist/docs/http-api/index.rst +++ b/pdns/recursordist/docs/http-api/index.rst @@ -64,3 +64,4 @@ All API endpoints for the PowerDNS Recursor are documented here: endpoint-cache endpoint-failure endpoint-rpz-stats + endpoint-jsonstat From 65ae96092bece0be8122ce922e61f96fbdd02b54 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 2 Apr 2019 16:16:25 +0200 Subject: [PATCH 051/127] dumresp: Add TCP support --- pdns/dumresp.cc | 160 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 134 insertions(+), 26 deletions(-) diff --git a/pdns/dumresp.cc b/pdns/dumresp.cc index 8d12c5121f25..354eba7461bb 100644 --- a/pdns/dumresp.cc +++ b/pdns/dumresp.cc @@ -30,9 +30,9 @@ #include StatBag S; -std::atomic* g_counter; +static std::atomic* g_counter; -void printStatus() +static void printStatus() { auto prev= g_counter->load(); for(;;) { @@ -42,47 +42,151 @@ void printStatus() } } -void usage() { - cerr<<"Syntax: dumresp LOCAL-ADDRESS LOCAL-PORT NUMBER-OF-PROCESSES"<qr=1; + dh->ad=0; +} + +static void tcpConnectionHandler(int sock) +try +{ + char buffer[1500]; + auto dh = reinterpret_cast(buffer); + + for (;;) { + uint16_t len = 0; + ssize_t got = read(sock, &len, sizeof(len)); + + if (got == 0) { + break; + } + + if (got != sizeof(len)) + unixDie("read 1"); + + len = ntohs(len); + + if (len < sizeof(dnsheader)) + unixDie("too small"); + + if (len > sizeof(buffer)) + unixDie("too large"); + + got = read(sock, buffer, len); + if (got != len) + unixDie("read 2: " + std::to_string(got) + " / " + std::to_string(len)); + + if (dh->qr) + continue; + + turnQueryIntoResponse(dh); + + uint16_t wirelen = htons(len); + if (write(sock, &wirelen, sizeof(wirelen)) != sizeof(wirelen)) + unixDie("send 1"); + + if (write(sock, buffer, len) < 0) + unixDie("send 2"); + } + + close(sock); +} +catch(const std::exception& e) { + cerr<<"TCP connection handler got an exception: "<(&rem), &socklen); + if (sock == -1) { + continue; + } + + std::thread connectionHandler(tcpConnectionHandler, sock); + connectionHandler.detach(); + } } int main(int argc, char** argv) try { + bool tcp = false; + for(int i = 1; i < argc; i++) { - if((string) argv[i] == "--help"){ + if(std::string(argv[i]) == "--help"){ usage(); return(EXIT_SUCCESS); } - if((string) argv[i] == "--version"){ + if(std::string(argv[i]) == "--version"){ cerr<<"dumresp "<), PROT_READ | PROT_WRITE, + auto ptr = mmap(nullptr, sizeof(std::atomic), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); g_counter = new(ptr) std::atomic(); - + + int numberOfListeners = atoi(argv[3]); + ComboAddress local(argv[1], atoi(argv[2])); + int i=1; - for(; i < atoi(argv[3]); ++i) { + for(; i < numberOfListeners; ++i) { if(!fork()) break; } - if(i==1) { + + if (i==1) { std::thread t(printStatus); t.detach(); + + if (tcp) { + for (int j = 0; j < numberOfListeners; j++) { + cout<<"Listening to TCP "<(buffer); + for(;;) { - len=recvfrom(s.getHandle(), buffer, sizeof(buffer), 0, (struct sockaddr*)&rem, &socklen); - (*g_counter)++; + uint16_t len = recvfrom(s.getHandle(), buffer, sizeof(buffer), 0, reinterpret_cast(&rem), &socklen); + if(len < 0) unixDie("recvfrom"); + if (len < sizeof(dnsheader)) + unixDie("too small " + std::to_string(len)); + if(dh->qr) continue; - dh->qr=1; - dh->ad=0; - if(sendto(s.getHandle(), buffer, len, 0, (struct sockaddr*)&rem, socklen) < 0) - unixDie("sendto"); + turnQueryIntoResponse(dh); + + if(sendto(s.getHandle(), buffer, len, 0, reinterpret_cast(&rem), socklen) < 0) + unixDie("sendto"); } } -catch(std::exception& e) +catch(const std::exception& e) { cerr<<"Fatal error: "< Date: Fri, 5 Apr 2019 14:37:19 +0200 Subject: [PATCH 052/127] dumresp: Document the new 'tcp' option --- docs/manpages/dumresp.1.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/manpages/dumresp.1.rst b/docs/manpages/dumresp.1.rst index 510d33828331..8a836a404086 100644 --- a/docs/manpages/dumresp.1.rst +++ b/docs/manpages/dumresp.1.rst @@ -4,7 +4,7 @@ dumresp Synopsis -------- -**dumresp** *LOCAL-ADDRESS* *LOCAL-PORT* *NUMBER-OF-PROCESSES* +**dumresp** *LOCAL-ADDRESS* *LOCAL-PORT* *NUMBER-OF-PROCESSES* [tcp] Description ----------- @@ -18,7 +18,8 @@ the port. Options ------- -None +tcp: Whether to listen and accept TCP connections in addition to +UDP packets. Defaults to false. See also -------- From 982e54af4a2decd6f1606ce4e0f3aa783cccbf3f Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:21 +0200 Subject: [PATCH 053/127] docs: Improve "BIND-mode operation" for DNSSEC Also: * Mention that not just keys are part of this database, but also DNSSEC domain metadata. * Link to the pdns.conf setting. --- docs/dnssec/modes-of-operation.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/dnssec/modes-of-operation.rst b/docs/dnssec/modes-of-operation.rst index f6679dafbd3f..25525eb05453 100644 --- a/docs/dnssec/modes-of-operation.rst +++ b/docs/dnssec/modes-of-operation.rst @@ -162,13 +162,14 @@ The signing and hashing algorithms are described in :ref:`dnssec-online-signing` BIND-mode operation ------------------- -The :doc:`bindbackend <../backends/bind>` can manage keys in an -SQLite3 database without launching a separate gsqlite3 backend. - -To use this mode, add -``bind-dnssec-db=/var/db/bind-dnssec-db.sqlite3`` to pdns.conf, and run -``pdnsutil create-bind-db /var/db/bind-dnssec-db.sqlite3``. Then, -restart PowerDNS. +The :doc:`bindbackend <../backends/bind>` can manage keys and other +DNSSEC-related :doc:`domain metadata <../domainmetadata>` in an SQLite3 +database without launching a separate gsqlite3 backend. + +To use this mode, run +``pdnsutil create-bind-db /var/db/bind-dnssec-db.sqlite3`` and set +:ref:`setting-bind-dnssec-db` in pdns.conf to the path of the created +database. Then, restart PowerDNS. .. note:: This SQLite database is different from the database used for the regular :doc:`SQLite 3 backend <../backends/generic-sqlite3>`. From 14b74d7a4b5b00d195e5642979571d8c997d04da Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:21 +0200 Subject: [PATCH 054/127] docs: align meta-data -> metadata This occurrence was the only one spelled like 'meta-data', all others are spelled as 'metadata'. --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index f288312e4911..ca5a8e7dd872 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,7 +6,7 @@ supports a large number of backends. These backends can either be plain zone files or be more dynamic in nature. PowerDNS has the concepts of 'backends'. A backend is a datastore that -the server will consult that contains DNS records (and some meta-data). +the server will consult that contains DNS records (and some metadata). The backends range from database backends (:doc:`MySQL `, :doc:`PostgreSQL `, :doc:`Oracle `) and :doc:`Bind-zonefiles ` to :doc:`co-processes ` and :doc:`JSON API's `. From f404c4c680c20e6455e1e9b5b72f3e7a14a67920 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:21 +0200 Subject: [PATCH 055/127] docs: remove unintentional blockquotes in HTML Having leading spaces in either: * Lists * RST directives, such as .. toctree:: will lead to the listing being wrapped in an HTML
element with also the styling as such. This is probably unintentional and at least inconsistent with other occurrences. --- docs/backends/lua.rst | 20 ++++----- docs/backends/remote.rst | 6 +-- docs/changelog/4.1.rst | 2 +- docs/common/security-policy.rst | 7 ++-- docs/index.rst | 12 +++--- docs/lua-records/functions.rst | 46 ++++++++++----------- docs/lua-records/index.rst | 2 +- docs/lua-records/reference/comboaddress.rst | 16 +++---- docs/lua-records/reference/index.rst | 2 +- docs/lua-records/reference/misc.rst | 20 ++++----- docs/modes-of-operation.rst | 12 +++--- docs/running.rst | 2 +- 12 files changed, 73 insertions(+), 74 deletions(-) diff --git a/docs/backends/lua.rst b/docs/backends/lua.rst index aef67350c5ed..610f3f5c64bb 100644 --- a/docs/backends/lua.rst +++ b/docs/backends/lua.rst @@ -47,16 +47,16 @@ There is a couple of new functions for you to use in Lua: All these ``log_facilities`` is available: - * ``log_all`` - * ``log_ntlog`` - * ``log_alert`` - * ``log_critical`` - * ``log_error`` - * ``log_warning`` - * ``log_notice,`` - * ``log_info`` - * ``log_debug`` - * ``log_none`` +* ``log_all`` +* ``log_ntlog`` +* ``log_alert`` +* ``log_critical`` +* ``log_error`` +* ``log_warning`` +* ``log_notice,`` +* ``log_info`` +* ``log_debug`` +* ``log_none`` ``dnspacket()`` ~~~~~~~~~~~~~~~ diff --git a/docs/backends/remote.rst b/docs/backends/remote.rst index 61d794ca3403..6a54b933d01d 100644 --- a/docs/backends/remote.rst +++ b/docs/backends/remote.rst @@ -326,9 +326,9 @@ Returns the value(s) for variable kind for zone name. You **must** always return something, if there are no values, you shall return empty set or false. - * Mandatory: No - * Parameters: name - * Reply: hash of key to array of strings +* Mandatory: No +* Parameters: name +* Reply: hash of key to array of strings Example JSON/RPC '''''''''''''''' diff --git a/docs/changelog/4.1.rst b/docs/changelog/4.1.rst index 0b37c2614c98..94cfe8f649bb 100644 --- a/docs/changelog/4.1.rst +++ b/docs/changelog/4.1.rst @@ -922,7 +922,7 @@ Changelogs for 4.1.x :tags: Tools, Improvements :pullreq: 4584 - Allow setting the account of a zone via pdnsutil (Tuxis Internet Engineering). + Allow setting the account of a zone via pdnsutil (Tuxis Internet Engineering). .. change:: :tags: Internals, New Features diff --git a/docs/common/security-policy.rst b/docs/common/security-policy.rst index 4c2b36460940..6114a3656080 100644 --- a/docs/common/security-policy.rst +++ b/docs/common/security-policy.rst @@ -19,7 +19,6 @@ Do note that only the PowerDNS software is in scope for the HackerOne program, n Disclosure Policy ^^^^^^^^^^^^^^^^^ - - Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue. - - Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. - - We will always credit researchers in our :doc:`../security-advisories/index`. - +- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue. +- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. +- We will always credit researchers in our :doc:`../security-advisories/index`. diff --git a/docs/index.rst b/docs/index.rst index ca5a8e7dd872..7ad7ee8dac53 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,9 +20,9 @@ This documentation is also available as a `PDF document ` - * :doc:`Configure the Server ` - * :doc:`Configure the backend(s) ` +* :doc:`Install the Authoritative Server ` +* :doc:`Configure the Server ` +* :doc:`Configure the backend(s) ` Getting Support --------------- @@ -31,9 +31,9 @@ You may also help others (please do). Public support is available via several different channels: - * This documentation - * `The mailing list `_ - * ``#powerdns`` on `irc.oftc.net `_ +* This documentation +* `The mailing list `_ +* ``#powerdns`` on `irc.oftc.net `_ The PowerDNS company can provide help or support you in private as well. For first class and rapid support, please contact powerdns.support@powerdns.com, or see the `.com website `__. diff --git a/docs/lua-records/functions.rst b/docs/lua-records/functions.rst index d180c76b5230..3b30327dab42 100644 --- a/docs/lua-records/functions.rst +++ b/docs/lua-records/functions.rst @@ -188,17 +188,17 @@ Reverse DNS functions **Formatting options:** - - ``%1%`` to ``%4%`` are individual octets - - Example record query: ``1.0.0.127.in-addr.arpa`` - - ``%1%`` = 127 - - ``%2%`` = 0 - - ``%3%`` = 0 - - ``%4%`` = 1 - - ``%5%`` joins the four decimal octets together with dashes - - Example: ``%5%.static.example.com`` is equivalent to ``%1%-%2%-%3%-%4%.static.example.com`` - - ``%6%`` converts each octet from decimal to hexadecimal and joins them together - - Example: A query for ``15.0.0.127.in-addr.arpa`` - - ``%6`` would be ``7f00000f`` (127 is 7f, and 15 is 0f in hexadecimal) + - ``%1%`` to ``%4%`` are individual octets + - Example record query: ``1.0.0.127.in-addr.arpa`` + - ``%1%`` = 127 + - ``%2%`` = 0 + - ``%3%`` = 0 + - ``%4%`` = 1 + - ``%5%`` joins the four decimal octets together with dashes + - Example: ``%5%.static.example.com`` is equivalent to ``%1%-%2%-%3%-%4%.static.example.com`` + - ``%6%`` converts each octet from decimal to hexadecimal and joins them together + - Example: A query for ``15.0.0.127.in-addr.arpa`` + - ``%6`` would be ``7f00000f`` (127 is 7f, and 15 is 0f in hexadecimal) **NOTE:** At the current time, only forward dotted format works with :func:`createForward` (i.e. ``127.0.0.1.static.example.com``) @@ -255,18 +255,18 @@ Reverse DNS functions Formatting options: - - ``%1%`` to ``%32%`` are individual characters (nibbles) - - **Example PTR record query:** ``a.0.0.0.1.0.0.2.ip6.arpa`` - - ``%1%`` = 2 - - ``%2%`` = 0 - - ``%3%`` = 0 - - ``%4%`` = 1 - - ``%33%`` converts the compressed address format into a dashed format, e.g. ``2001:a::1`` to ``2001-a--1`` - - ``%34%`` to ``%41%`` represent the 8 uncompressed 2-byte chunks - - **Example:** PTR query for ``2001:a:b::123`` - - ``%34%`` - returns ``2001`` (chunk 1) - - ``%35%`` - returns ``000a`` (chunk 2) - - ``%41%`` - returns ``0123`` (chunk 8) + - ``%1%`` to ``%32%`` are individual characters (nibbles) + - **Example PTR record query:** ``a.0.0.0.1.0.0.2.ip6.arpa`` + - ``%1%`` = 2 + - ``%2%`` = 0 + - ``%3%`` = 0 + - ``%4%`` = 1 + - ``%33%`` converts the compressed address format into a dashed format, e.g. ``2001:a::1`` to ``2001-a--1`` + - ``%34%`` to ``%41%`` represent the 8 uncompressed 2-byte chunks + - **Example:** PTR query for ``2001:a:b::123`` + - ``%34%`` - returns ``2001`` (chunk 1) + - ``%35%`` - returns ``000a`` (chunk 2) + - ``%41%`` - returns ``0123`` (chunk 8) **NOTE:** At the current time, only dashed compressed format works for this function (i.e. ``2001-a-b--1.static6.example.com``) diff --git a/docs/lua-records/index.rst b/docs/lua-records/index.rst index 3c2a9b0864ac..d44dbe1a0b03 100644 --- a/docs/lua-records/index.rst +++ b/docs/lua-records/index.rst @@ -200,7 +200,7 @@ explicitly, either globally (``enable-lua-records``) or per zone Reference --------- - .. toctree:: +.. toctree:: :maxdepth: 2 functions diff --git a/docs/lua-records/reference/comboaddress.rst b/docs/lua-records/reference/comboaddress.rst index 56156e269769..7d79eeec03a2 100644 --- a/docs/lua-records/reference/comboaddress.rst +++ b/docs/lua-records/reference/comboaddress.rst @@ -73,14 +73,14 @@ ComboAddressSet objects We provide a convenient object class that can store unique ComboAddresses in no particular order and allows fast retrieval of individual elements based on their values - .. code-block:: lua - - addr = newCA("1.2.3.4") - myset = newCAS() - myset:add(addr) - if myset:check(addr) then -- prints "found!" - print('found!') - end +.. code-block:: lua + + addr = newCA("1.2.3.4") + myset = newCAS() + myset:add(addr) + if myset:check(addr) then -- prints "found!" + print('found!') + end Functions and methods of a ``ComboAddressSet`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/lua-records/reference/index.rst b/docs/lua-records/reference/index.rst index dbf23422a453..2ab827e1d879 100644 --- a/docs/lua-records/reference/index.rst +++ b/docs/lua-records/reference/index.rst @@ -1,7 +1,7 @@ LUA Reference ------------- - .. toctree:: +.. toctree:: :maxdepth: 2 comboaddress diff --git a/docs/lua-records/reference/misc.rst b/docs/lua-records/reference/misc.rst index 89e1f0f6ec52..c9f28d7f5b36 100644 --- a/docs/lua-records/reference/misc.rst +++ b/docs/lua-records/reference/misc.rst @@ -10,16 +10,16 @@ Other functions :param string message: The message to log :param int loglevel: The urgency level of the message. Defaults to `pdns.loglevels.Warning` - You can use the following constants as log levels : - - - `pdns.loglevels.Alert` - - `pdns.loglevels.Critical` - - `pdns.loglevels.Debug` - - `pdns.loglevels.Emergency` - - `pdns.loglevels.Info` - - `pdns.loglevels.Notice` - - `pdns.loglevels.Warning` - - `pdns.loglevels.Error` + You can use the following constants as log levels : + + - `pdns.loglevels.Alert` + - `pdns.loglevels.Critical` + - `pdns.loglevels.Debug` + - `pdns.loglevels.Emergency` + - `pdns.loglevels.Info` + - `pdns.loglevels.Notice` + - `pdns.loglevels.Warning` + - `pdns.loglevels.Error` .. function:: pdnsrandom([maximum]) diff --git a/docs/modes-of-operation.rst b/docs/modes-of-operation.rst index bf76cf28e970..15af055bedcc 100644 --- a/docs/modes-of-operation.rst +++ b/docs/modes-of-operation.rst @@ -201,12 +201,12 @@ itself as a slave for that zone. Before a supermaster notification succeeds, the following conditions must be met: - - :ref:`setting-supermaster` support must be enabled - - The supermaster must carry a SOA record for the notified domain - - The supermaster IP must be present in the 'supermaster' table - - The set of NS records for the domain, as retrieved by the slave from the supermaster, must include the name that goes with the IP address in the supermaster table - - If your master sends signed NOTIFY it will mark that TSIG key as the TSIG key used for retrieval as well - - If you turn off :ref:`setting-allow-unsigned-supermaster`, then your supermaster(s) are required to sign their notifications. +- :ref:`setting-supermaster` support must be enabled +- The supermaster must carry a SOA record for the notified domain +- The supermaster IP must be present in the 'supermaster' table +- The set of NS records for the domain, as retrieved by the slave from the supermaster, must include the name that goes with the IP address in the supermaster table +- If your master sends signed NOTIFY it will mark that TSIG key as the TSIG key used for retrieval as well +- If you turn off :ref:`setting-allow-unsigned-supermaster`, then your supermaster(s) are required to sign their notifications. .. warning:: If you use another PowerDNS server as master and have diff --git a/docs/running.rst b/docs/running.rst index 42b1ed8322a5..9900b9673662 100644 --- a/docs/running.rst +++ b/docs/running.rst @@ -138,7 +138,7 @@ commands: - ``mrtg``: Dump statistics in mrtg format. See the performance :ref:`counters` documentation. - .. note:: +.. note:: Packages provided by Operating System vendors might support different or less commands. From c21bf56cf55c94e1c000bbe160e270fcc11b4e23 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:21 +0200 Subject: [PATCH 056/127] docs: fix formatting of 'PKCS#11 support' page Also: * slightly reword some things where necessary * Ubuntu 12.nothing/14.nothing do not exist, added assumed '.04'. --- docs/dnssec/pkcs11.rst | 175 ++++++++++++++++++----------------------- 1 file changed, 77 insertions(+), 98 deletions(-) diff --git a/docs/dnssec/pkcs11.rst b/docs/dnssec/pkcs11.rst index 89f77246bc02..2325d0955377 100644 --- a/docs/dnssec/pkcs11.rst +++ b/docs/dnssec/pkcs11.rst @@ -27,38 +27,32 @@ To test this feature, a software HSM can be used. It is **not recommended** to use this in production. Instructions on how to setup SoftHSM to work with the feature after -compilation on ubuntu/debian (tested with Ubuntu 12 and 14). - -``apt-get install softhsm p11-kit opensc`` - create directory -/etc/pkcs11/modules - Add file called 'softhsm' there with (on newer -versions, use softhsm.module) -``module: /home/cmouse/softhsm/lib/softhsm/libsofthsm.so managed: yes`` -- Verify it works: ``p11-kit -l`` - Create at least two tokens (ksk and -zsk) with (slot-number starts from 0) +compilation on Ubuntu/Debian (tested with Ubuntu 12.04 and 14.04). -:: +- ``apt-get install softhsm p11-kit opensc`` +- create directory ``/etc/pkcs11/modules`` +- create a file ``softhsm`` (``softhsm.module`` on newer versions), + with contents::: - ``` - sudo softhsm --init-token --slot slot-number --label zone-ksk|zone-zsk --pin some-pin --so-pin another-pin - ``` - -- Using pkcs11-tool, initialize your new keys. + module: /home/cmouse/softhsm/lib/softhsm/libsofthsm.so managed: yes - :: +- Verify that it works: ``p11-kit -l`` +- Create at least two tokens (ksk and zsk) with (slot-number starts from 0):: - sudo pkcs11-tool --module=/home/cmouse/softhsm/lib/softhsm/libsofthsm.so -l -p some-pin -k --key-type RSA:2048 -a zone-ksk|zone-zsk --slot-index slot-number + sudo softhsm --init-token --slot slot-number --label zone-ksk|zone-zsk --pin some-pin --so-pin another-pin -- Assign the keys using (note that token label is not necessarily same - as object label, see p11-kit -l) +- Using pkcs11-tool, initialize your new keys.:: - :: + sudo pkcs11-tool --module=/home/cmouse/softhsm/lib/softhsm/libsofthsm.so -l -p some-pin -k --key-type RSA:2048 -a zone-ksk|zone-zsk --slot-index slot-number - pdnsutil hsm assign zone rsasha256 ksk|zsk softhsm token-label pin zone-ksk|zsk +- Assign the keys using (note that token label is not necessarily same + as object label, see p11-kit -l):: -- Verify that everything worked, you should see valid data there + pdnsutil hsm assign zone rsasha256 ksk|zsk softhsm token-label pin zone-ksk|zsk - :: +- Verify that everything worked, you should see valid data there:: - pdnsutil show-zone zone + pdnsutil show-zone zone - SoftHSM signatures are fast enough to be used in live environment. @@ -66,84 +60,69 @@ Using CryptAS ------------- Instructions on how to use CryptAS -```Athena IDProtect Key USB Token V2J`` `__ -Smart Card token on Ubuntu 14. - install the manufacturer\`s support -software on your system and initialize the Smart Card token as per -instructions (do not use PIV). - apt-get install p11-kit opensc - create -directory /etc/pkcs11/modules - Add file called 'athena.module' with -content +`Athena IDProtect Key USB Token V2J `_ +Smart Card token on Ubuntu 14.04. -:: +- Install the manufacturer's support software on your system and initialize + the Smart Card token as per instructions (do not use PIV). +- ``apt-get install p11-kit opensc`` +- Create directory ``/etc/pkcs11/modules``. +- Create file named ``athena.module`` with contents:: - ``` module: /lib64/libASEP11.so managed: yes - ``` - -- Verify it worked, it should resemble output below. do not continue if - this does not show up. - - :: - - $ p11-kit -l - athena: /lib64/libASEP11.so - library-description: ASE Cryptoki - library-manufacturer: Athena Smartcard Solutions - library-version: 3.1 - token: IDProtect#0A50123456789 - manufacturer: Athena Smartcard Solutions - model: IDProtect - serial-number: 0A50123456789 - hardware-version: 1.0 - firmware-version: 1.0 - flags: - rng - login-required - user-pin-initialized - token-initialized - -- Using pkcs11-tool, initialize your new keys. After this IDProtect - Manager no longer can show your token certificates and keys, at least - on version v6.23.04. - - :: - - pkcs11-tool --module=/home/cmouse/softhsm/lib/softhsm/libsofthsm.so -l -p some-pin -k --key-type RSA:2048 -a zone-ksk - pkcs11-tool --module=/home/cmouse/softhsm/lib/softhsm/libsofthsm.so -l -p some-pin -k --key-type RSA:2048 -a zone-zsk - -- Verify that keys are there. - - :: - - $ pkcs11-tool --module=/lib64/libASEP11.so -l -p some-pin -O - Using slot 0 with a present token (0x0) - Public Key Object; RSA 2048 bits - label: zone-ksk - Usage: encrypt, verify, wrap - Public Key Object; RSA 2048 bits - label: zone-zsk - Usage: encrypt, verify, wrap - Private Key Object; RSA - label: zone-ksk - Usage: decrypt, sign, unwrap - Private Key Object; RSA - label: zone-zsk - Usage: decrypt, sign, unwrap - -- Assign the keys using - - :: - - pdnsutil hsm assign zone rsasha256 ksk|zsk athena IDProtect#0A50123456789 pin zone-ksk|zsk - -- Verify that everything worked, you should see valid data there. - - :: - - pdnsutil show-zone zone - -- Note that the physical token is pretty slow, so you have to use it as - hidden master. It has been observed to produce about - 1.5signatures/second. - +- Verify it worked, it should resemble output below. Do not continue if + this does not show up. :: + + $ p11-kit -l + athena: /lib64/libASEP11.so + library-description: ASE Cryptoki + library-manufacturer: Athena Smartcard Solutions + library-version: 3.1 + token: IDProtect#0A50123456789 + manufacturer: Athena Smartcard Solutions + model: IDProtect + serial-number: 0A50123456789 + hardware-version: 1.0 + firmware-version: 1.0 + flags: + rng + login-required + user-pin-initialized + token-initialized + +- Using pkcs11-tool, initialize your new keys. After this IDProtect + Manager no longer can show your token certificates and keys, at least + on version v6.23.04. :: + + pkcs11-tool --module=/home/cmouse/softhsm/lib/softhsm/libsofthsm.so -l -p some-pin -k --key-type RSA:2048 -a zone-ksk + pkcs11-tool --module=/home/cmouse/softhsm/lib/softhsm/libsofthsm.so -l -p some-pin -k --key-type RSA:2048 -a zone-zsk + +- Verify that keys are there:: + + $ pkcs11-tool --module=/lib64/libASEP11.so -l -p some-pin -O + Using slot 0 with a present token (0x0) + Public Key Object; RSA 2048 bits + label: zone-ksk + Usage: encrypt, verify, wrap + Public Key Object; RSA 2048 bits + label: zone-zsk + Usage: encrypt, verify, wrap + Private Key Object; RSA + label: zone-ksk + Usage: decrypt, sign, unwrap + Private Key Object; RSA + label: zone-zsk + Usage: decrypt, sign, unwrap + +- Assign the keys using:: + + pdnsutil hsm assign zone rsasha256 ksk|zsk athena IDProtect#0A50123456789 pin zone-ksk|zsk + +- Verify that everything worked, you should see valid data there. :: + + pdnsutil show-zone zone + +- Note that the physical token is pretty slow, so you have to use it as + hidden master. It has been observed to produce about 1.5 signatures/second. From 69e1e56a881295b2c51121dee14e0a422452932c Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:21 +0200 Subject: [PATCH 057/127] docs: Add 'hidden master' approach in DNSSEC security This approach is referred to in the public domain as well as once in the PowerDNS changelog, but not described in any way before this change. --- docs/dnssec/modes-of-operation.rst | 7 +++++-- docs/dnssec/operational.rst | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/dnssec/modes-of-operation.rst b/docs/dnssec/modes-of-operation.rst index 25525eb05453..a92abbaf956a 100644 --- a/docs/dnssec/modes-of-operation.rst +++ b/docs/dnssec/modes-of-operation.rst @@ -122,12 +122,15 @@ PowerDNS also serves the DNSKEY records in live-signing mode. Their TTL is derived from the SOA records *minimum* field. When using NSEC3, the TTL of the NSEC3PARAM record is also derived from that field. +.. _dnssec_presigned_records: + Pre-signed records ------------------ In this mode, PowerDNS serves zones that already contain DNSSEC records. -Such zones can either be slaved from a remote master, or can be signed -using tools like OpenDNSSEC, ldns-signzone, and dnssec-signzone. +Such zones can either be slaved from a remote master in online signing +mode, or can be pre-signed using tools like OpenDNSSEC, ldns-signzone, +and dnssec-signzone. Even in this mode, PowerDNS will synthesize NSEC(3) records itself because of its architecture. RRSIGs of these NSEC(3) will still need to diff --git a/docs/dnssec/operational.rst b/docs/dnssec/operational.rst index a0687bcfc646..f3cfce1ce7db 100644 --- a/docs/dnssec/operational.rst +++ b/docs/dnssec/operational.rst @@ -203,6 +203,16 @@ In some settings, having such (private) keying material available online is considered undesirable. In this case, consider running in pre-signed mode. +A slightly more complex approach is running a *hidden* master in simple +online signing mode, but on a highly secured system unreachable for the +public. Internet-connected slaves can then transfer the zones pre-signed +from this master over a secure private network. This topology offers +substantial security benefits with regards to key material while +maintaining ease of daily operation by PowerDNS's features in online +mode. + +See also :ref:`dnssec_presigned_records`. + Performance ----------- From 633489be02df94ece6e837cbdcb984ee0ddcd343 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:21 +0200 Subject: [PATCH 058/127] docs: Fix formatting of some code blocks --- docs/appendices/backend-writers-guide.rst | 4 +- docs/backends/generic-mysql.rst | 1 + docs/backends/generic-odbc.rst | 3 +- docs/backends/generic-oracle.rst | 1 + docs/backends/generic-postgresql.rst | 1 + docs/backends/generic-sql.rst | 20 ++- docs/backends/generic-sqlite3.rst | 7 +- docs/backends/ldap.rst | 10 +- docs/backends/lua.rst | 12 +- docs/backends/opendbx.rst | 22 +-- docs/backends/oracle.rst | 60 +++---- docs/backends/remote.rst | 150 +++++++++--------- docs/changelog/pre-4.0.rst | 4 +- docs/dnssec/index.rst | 2 +- docs/dnssec/migration.rst | 8 +- docs/dnssec/operational.rst | 8 +- docs/dnsupdate.rst | 22 +-- docs/domainmetadata.rst | 12 +- docs/guides/alias.rst | 2 +- docs/guides/basic-database.rst | 7 +- docs/guides/kskroll.rst | 6 +- docs/guides/recursion.rst | 10 +- docs/guides/virtual-instances.rst | 4 +- docs/guides/zskroll.rst | 6 +- docs/installation.rst | 14 +- docs/migration.rst | 8 +- docs/modes-of-operation.rst | 4 +- .../powerdns-advisory-2012-01.rst | 4 +- docs/settings.rst | 8 +- docs/tsig.rst | 14 +- 30 files changed, 220 insertions(+), 214 deletions(-) diff --git a/docs/appendices/backend-writers-guide.rst b/docs/appendices/backend-writers-guide.rst index 4766771f77d6..d47a35dc1841 100644 --- a/docs/appendices/backend-writers-guide.rst +++ b/docs/appendices/backend-writers-guide.rst @@ -643,9 +643,9 @@ Supermaster/Superslave capability A backend that wants to act as a 'superslave' for a master should implement the following method: -:: +.. code-block:: cpp - class DNSBackend + class DNSBackend { virtual bool superMasterBackend(const string &ip, const string &domain, const vector&nsset, string *account, DNSBackend **db) }; diff --git a/docs/backends/generic-mysql.rst b/docs/backends/generic-mysql.rst index edc0ed9580f8..0e125ea7c1ad 100644 --- a/docs/backends/generic-mysql.rst +++ b/docs/backends/generic-mysql.rst @@ -33,6 +33,7 @@ material, and other information upon deletion of a domain from the domains table. The following SQL does the job: .. literalinclude:: ../../modules/gmysqlbackend/enable-foreign-keys.mysql.sql + :language: SQL Using MySQL replication ----------------------- diff --git a/docs/backends/generic-odbc.rst b/docs/backends/generic-odbc.rst index 69fa1b5f43a8..6c6b3997a773 100644 --- a/docs/backends/generic-odbc.rst +++ b/docs/backends/generic-odbc.rst @@ -114,6 +114,7 @@ This schema can also be found in the PowerDNS source as This is the schema for 4.2. For 4.1, please find `the 4.1 schema on GitHub `_. .. literalinclude:: ../../modules/godbcbackend/schema.mssql.sql + :language: SQL Load this into the database as follows: @@ -133,7 +134,7 @@ Configuring PowerDNS Add the options required to your ``pdns.conf``: -:: +.. code-block:: ini launch=godbc godbc-datasource=pdns1 diff --git a/docs/backends/generic-oracle.rst b/docs/backends/generic-oracle.rst index 31f08c2e2fd4..59fa2e74cadf 100644 --- a/docs/backends/generic-oracle.rst +++ b/docs/backends/generic-oracle.rst @@ -27,6 +27,7 @@ need or want to add ``namespace`` statements. Below, you will find the schema for 4.2. If you are using 4.1 or earlier, please find `the 4.1 schema on GitHub `_. .. literalinclude:: ../../modules/goraclebackend/schema.goracle.sql + :language: SQL This schema contains all elements needed for master, slave and superslave operation. diff --git a/docs/backends/generic-postgresql.rst b/docs/backends/generic-postgresql.rst index 824233413d12..41738ec9ca55 100644 --- a/docs/backends/generic-postgresql.rst +++ b/docs/backends/generic-postgresql.rst @@ -97,3 +97,4 @@ Default schema This is the 4.2 schema. Please find `the 4.1 schema on GitHub `_. .. literalinclude:: ../../modules/gpgsqlbackend/schema.pgsql.sql + :language: SQL diff --git a/docs/backends/generic-sql.rst b/docs/backends/generic-sql.rst index b1e2027f1179..b98eb04bb08d 100644 --- a/docs/backends/generic-sql.rst +++ b/docs/backends/generic-sql.rst @@ -48,17 +48,15 @@ And wait a while for PowerDNS to pick up the addition - which happens within one minute (this is determined by the :ref:`setting-slave-cycle-interval` setting). There is no need to inform PowerDNS that a new domain was -added. Typical output is: - -.. code-block:: SQL - - Apr 09 13:34:29 All slave domains are fresh - Apr 09 13:35:29 1 slave domain needs checking - Apr 09 13:35:29 Domain example.com is stale, master serial 1, our serial 0 - Apr 09 13:35:30 [gPgSQLBackend] Connected to database - Apr 09 13:35:30 AXFR started for 'example.com' - Apr 09 13:35:30 AXFR done for 'example.com' - Apr 09 13:35:30 [gPgSQLBackend] Closing connection +added. Typical output is:: + + Apr 09 13:34:29 All slave domains are fresh + Apr 09 13:35:29 1 slave domain needs checking + Apr 09 13:35:29 Domain example.com is stale, master serial 1, our serial 0 + Apr 09 13:35:30 [gPgSQLBackend] Connected to database + Apr 09 13:35:30 AXFR started for 'example.com' + Apr 09 13:35:30 AXFR done for 'example.com' + Apr 09 13:35:30 [gPgSQLBackend] Closing connection From now on, PowerDNS is authoritative for the 'example.com' zone and will respond accordingly for queries within that zone. diff --git a/docs/backends/generic-sqlite3.rst b/docs/backends/generic-sqlite3.rst index 31066a92135d..b0e723aceea8 100644 --- a/docs/backends/generic-sqlite3.rst +++ b/docs/backends/generic-sqlite3.rst @@ -93,11 +93,10 @@ Using the SQLite backend ------------------------ The last thing you need to do is telling PowerDNS to use the SQLite -backend. +backend in pdns.conf: -:: +.. code-block:: ini - # in pdns.conf launch=gsqlite3 gsqlite3-database= @@ -105,7 +104,7 @@ Then you can start PowerDNS and it should notify you that a connection to the database was made. Compiling the SQLite backend ------------------------------ +---------------------------- Before you can begin compiling PowerDNS with the SQLite backend you need to have the SQLite utility and library installed on your system. You can diff --git a/docs/backends/ldap.rst b/docs/backends/ldap.rst index b2f58c9bb9bb..76111e2b65a6 100644 --- a/docs/backends/ldap.rst +++ b/docs/backends/ldap.rst @@ -65,7 +65,7 @@ Add them to the ``pdns.conf`` file. To launch the ldap backend: -:: +.. code-block:: ini launch=ldap @@ -450,7 +450,7 @@ standard compliant LDAP server. ``zone2ldap`` needs the BIND ``named.conf`` (usually located in /etc) as input and writes the dns record entries in ldif format to stdout: -:: +.. code-block:: shell zone2ldap --basedn=YOUR_BASE_DN \ @@ -460,7 +460,7 @@ record entries in ldif format to stdout: Alternatively zone2ldap can be used to convert only single zone files instead all zones: -:: +.. code-block:: shell zone2ldap --basedn=YOUR_BASE_DN \ @@ -487,7 +487,7 @@ creates a file in LDIF format with the necessary LDAP updates including the "associatedDomain" and "dc" attributes. The utility is executed on the command line by: -:: +.. code-block:: shell ./bind2pdns-ldap --host=HOSTNAME_OR_IP \ @@ -525,7 +525,7 @@ into a file and call ``zone2ldap`` with the file name as option to the which can be imported into the LDAP tree. The bash script except below automates this: -:: +.. code-block:: shell DNSSERVER=127.0.0.1 DOMAINS="example.com 10.10.in-addr.arpa" diff --git a/docs/backends/lua.rst b/docs/backends/lua.rst index 610f3f5c64bb..b7d39dcb2b99 100644 --- a/docs/backends/lua.rst +++ b/docs/backends/lua.rst @@ -102,9 +102,9 @@ The following script can be used to test the server: This will yield the following result: -:: +.. code-block:: shell - $dig any www.test.com @127.0.0.1 -p5300 +multiline + $ dig any www.test.com @127.0.0.1 -p5300 +multiline ; <<>> DiG 9.7.3 <<>> any www.test.com @127.0.0.1 -p5300 +multiline ;; global options: +cmd ;; Got answer: @@ -152,7 +152,9 @@ luafunctions if you want. For example: .. _setting-lua-f_lookup: -``lua-f_lookup = mynewfunction`` +.. code-block:: ini + + lua-f_lookup = mynewfunction will call the function ``mynewfunction`` for the lookup-routine. @@ -168,7 +170,9 @@ You can have an error function in Lua when Lua gives back a error. First make your error function then you put this in ``pdns.conf``: -``lua-f_exec_error = YOUR_METHOD`` +.. code-block:: ini + + lua-f_exec_error = YOUR_METHOD DNSSEC ------ diff --git a/docs/backends/opendbx.rst b/docs/backends/opendbx.rst index 81a19fc8e0cd..768bc2836714 100644 --- a/docs/backends/opendbx.rst +++ b/docs/backends/opendbx.rst @@ -293,7 +293,7 @@ Supported without changes since OpenDBX 1.0.0 but requires to set (including the trailing slash or backslash, depending on your operating system) and opendbx-database to the name of the file. -.. code-block:: SQL +.. code-block:: ini opendbx-host-read = /path/to/file/ opendbx-host-write = /path/to/file/ @@ -302,7 +302,7 @@ system) and opendbx-database to the name of the file. SQLite Schema ~~~~~~~~~~~~~ -:: +.. code-block:: SQL CREATE TABLE "domains" ( "id" INTEGER NOT NULL PRIMARY KEY, @@ -370,7 +370,7 @@ SQLite Schema SQLite3 Schema ~~~~~~~~~~~~~~ -:: +.. code-block:: SQL CREATE TABLE "domains" ( "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, @@ -442,7 +442,7 @@ Requires :ref:`setting-opendbx-database` set to the path of the database file and doesn't support the default statement for starting transactions. Please add the following lines to your pdns.conf: -:: +.. code-block:: ini opendbx-database = /var/lib/firebird2/data/powerdns.gdb opendbx-sql-transactbegin = SET TRANSACTION @@ -452,7 +452,7 @@ tool with the parameter ``-page 4096``. Otherwise, you will get an error (key size exceeds implementation restriction for index "pdns\_unq\_domains\_name") when creating the tables. -:: +.. code-block:: SQL CREATE TABLE "domains" ( "id" INTEGER NOT NULL, @@ -560,13 +560,13 @@ configuration file of the dblib client library) and doesn't support the default statement for starting transactions. Please add the following lines to your pdns.conf: -:: +.. code-block:: ini opendbx-host-read = MSSQL2k opendbx-host-write = MSSQL2k opendbx-sql-transactbegin = BEGIN TRANSACTION -:: +.. code-block:: SQL SET quoted_identifier ON; @@ -650,13 +650,13 @@ section in the configuration file of the ctlib client library) and doesn't support the default statement for starting transactions. Please add the following lines to your pdns.conf: -:: +.. code-block:: ini opendbx-host-read = SYBASE opendbx-host-write = SYBASE opendbx-sql-transactbegin = BEGIN TRANSACTION -:: +.. code-block:: SQL SET quoted_identifier ON; @@ -736,11 +736,11 @@ Oracle Uses a different syntax for transactions and requires the following additional line in your pdns.conf: -:: +.. code-block:: ini opendbx-sql-transactbegin = SET TRANSACTION NAME 'AXFR' -:: +.. code-block:: SQL CREATE TABLE "domains" ( "id" INTEGER NOT NULL, diff --git a/docs/backends/oracle.rst b/docs/backends/oracle.rst index 8445e694ec1e..ba9b7c34bd70 100644 --- a/docs/backends/oracle.rst +++ b/docs/backends/oracle.rst @@ -352,7 +352,7 @@ oracle-basic-query Looking for records based on owner name and type. Default: -:: +.. code-block:: SQL SELECT fqdn, ttl, type, content, zone_id, last_change, auth FROM Records @@ -363,7 +363,7 @@ oracle-basic-id-query Looking for records from one zone based on owner name and type. Default: -:: +.. code-block:: SQL SELECT fqdn, ttl, type, content, zone_id, last_change, auth FROM Records @@ -374,7 +374,7 @@ oracle-any-query Looking for records based on owner name. Default: -:: +.. code-block:: SQL SELECT fqdn, ttl, type, content, zone_id, last_change, auth FROM Records @@ -387,7 +387,7 @@ oracle-any-id-query Looking for records from one zone based on owner name. Default: -:: +.. code-block:: SQL SELECT fqdn, ttl, type, content, zone_id, last_change, auth FROM Records @@ -401,7 +401,7 @@ oracle-list-query Looking for all records from one zone. Default: -:: +.. code-block:: SQL SELECT fqdn, ttl, type, content, zone_id, last_change, auth FROM Records @@ -418,7 +418,7 @@ oracle-get-zone-metadata-query Fetch the content of the metadata entries of type ':kind' for the zone called ':name', in their original order. Default: -:: +.. code-block:: SQL SELECT md.meta_content FROM Zones z JOIN ZoneMetadata md ON z.id = md.zone_id @@ -432,7 +432,7 @@ Delete all metadata entries of type ':kind' for the zone called ':name'. You can skip this if you do not plan to manage zones with the ``pdnsutil`` tool. Default: -:: +.. code-block:: SQL DELETE FROM ZoneMetadata md WHERE zone_id = (SELECT id FROM Zones z WHERE z.name = lower(:name)) @@ -444,7 +444,7 @@ oracle-set-zone-metadata-query Create a metadata entry. You can skip this if you do not plan to manage zones with the ``pdnsutil`` tool. Default: -:: +.. code-block:: SQL INSERT INTO ZoneMetadata (zone_id, meta_type, meta_ind, meta_content) VALUES ( @@ -457,7 +457,7 @@ oracle-get-tsig-key-query Retrieved the TSIG key specified by ':name'. Default: -:: +.. code-block:: SQL SELECT algorithm, secret FROM TSIGKeys @@ -471,7 +471,7 @@ oracle-get-zone-keys-query Retrieve the DNSSEC signing keys for a zone. Default: -:: +.. code-block:: SQL SELECT k.id, k.flags, k.active, k.keydata FROM ZoneDNSKeys k JOIN Zones z ON z.id = k.zone_id @@ -483,7 +483,7 @@ oracle-del-zone-key-query Delete a DNSSEC signing key. You can skip this if you do not plan to manage zones with the ``pdnsutil`` tool. Default: -:: +.. code-block:: SQL DELETE FROM ZoneDNSKeys WHERE id = :keyid @@ -493,9 +493,9 @@ oracle-add-zone-key-query Add a DNSSEC signing key. You can skip this if you do not plan to manage zones with the ``pdnsutil`` tool. Default: -:: +.. code-block:: SQL - INSERT INTO ZoneDNSKeys (id, zone_id, flags, active, keydata) " + INSERT INTO ZoneDNSKeys (id, zone_id, flags, active, keydata) VALUES ( zonednskeys_id_seq.NEXTVAL, (SELECT id FROM Zones WHERE name = lower(:name)), @@ -510,7 +510,7 @@ oracle-set-zone-key-state-query Enable or disable a DNSSEC signing key. You can skip this if you do not plan to manage zones with the **pdnsutil** tool. Default: -:: +.. code-block:: SQL UPDATE ZoneDNSKeys SET active = :active WHERE id = :keyid @@ -526,7 +526,7 @@ variables, not a query. Default: -:: +.. code-block:: SQL BEGIN get_canonical_prev_next(:zoneid, :name, :prev, :next); @@ -539,7 +539,7 @@ Given an NSEC3 hash, this call needs to return its predecessor and successor in NSEC3 zone ordering into ``:prev`` and ``:next``, and the FQDN of the predecessor into ``:unhashed``. Default: -:: +.. code-block:: SQL BEGIN get_hashed_prev_next(:zoneid, :hash, :unhashed, :prev, :next); @@ -554,7 +554,7 @@ oracle-zone-info-query Get some basic information about the named zone before doing master/slave things. Default: -:: +.. code-block:: SQL SELECT id, name, type, last_check, serial, notified_serial FROM Zones @@ -567,7 +567,7 @@ Delete all records for a zone in preparation for an incoming zone transfer. This happens inside a transaction, so if the transfer fails, the old zone content will still be there. Default: -:: +.. code-block:: SQL DELETE FROM Records WHERE zone_id = :zoneid @@ -578,7 +578,7 @@ Insert a record into the zone during an incoming zone transfer. This happens inside the same transaction as delete-zone, so we will not end up with a partially transferred zone. Default: -:: +.. code-block:: SQL INSERT INTO Records (id, fqdn, zone_id, ttl, type, content) VALUES (records_id_seq.NEXTVAL, lower(:name), :zoneid, :ttl, :type, :content) @@ -592,7 +592,7 @@ empty non-terminals, set the ``auth`` bit and NSEC3 hashes, and generally do any post-processing your schema requires. The do-nothing default: -:: +.. code-block:: SQL DECLARE zone_id INTEGER := :zoneid; @@ -610,7 +610,7 @@ Return a list of zones that need to be checked and their master servers. Return multiple rows, identical except for the master address, for zones with more than one master. Default: -:: +.. code-block:: SQL SELECT z.id, z.name, z.last_check, z.serial, zm.master FROM Zones z JOIN Zonemasters zm ON z.id = zm.zone_id @@ -623,7 +623,7 @@ oracle-zone-set-last-check-query Set the last check timestamp after a successful check. Default: -:: +.. code-block:: SQL UPDATE Zones SET last_check = :lastcheck WHERE id = :zoneid @@ -633,7 +633,7 @@ oracle-updated-masters-query Return a list of zones that need to have ``NOTIFY`` packets sent out. Default: -:: +.. code-block:: SQL SELECT id, name, serial, notified_serial FROM Zones @@ -645,7 +645,7 @@ oracle-zone-set-notified-serial-query Set the last notified serial after packets have been sent. Default: -:: +.. code-block:: SQL UPDATE Zones SET notified_serial = :serial WHERE id = :zoneid @@ -656,7 +656,7 @@ Return a list of hosts that should be notified, in addition to any nameservers in the NS records, when sending ``NOTIFY`` packets for the named zone. Default: -:: +.. code-block:: SQL SELECT an.hostaddr FROM Zones z JOIN ZoneAlsoNotify an ON z.id = an.zone_id @@ -667,7 +667,7 @@ oracle-zone-masters-query Return a list of masters for the zone specified by id. Default: -:: +.. code-block:: SQL SELECT master FROM Zonemasters @@ -679,7 +679,7 @@ oracle-is-zone-master-query Return a row if the specified host is a registered master for the named zone. Default: -:: +.. code-block:: SQL SELECT zm.master FROM Zones z JOIN Zonemasters zm ON z.id = zm.zone_id @@ -694,7 +694,7 @@ oracle-accept-supernotification-query If a supernotification should be accepted from ':ip', for the master nameserver ':ns', return a label for this supermaster. Default: -:: +.. code-block:: SQL SELECT name FROM Supermasters @@ -706,7 +706,7 @@ oracle-insert-slave-query A supernotification has just been accepted, and we need to create an entry for the new zone. Default: -:: +.. code-block:: SQL INSERT INTO Zones (id, name, type) VALUES (zones_id_seq.NEXTVAL, lower(:zone), 'SLAVE') @@ -718,7 +718,7 @@ oracle-insert-master-query We need to register the first master server for the newly created zone. Default: -:: +.. code-block:: SQL INSERT INTO Zonemasters (zone_id, master) VALUES (:zoneid, :ip) diff --git a/docs/backends/remote.rst b/docs/backends/remote.rst index 6a54b933d01d..49e74b4afc04 100644 --- a/docs/backends/remote.rst +++ b/docs/backends/remote.rst @@ -50,7 +50,7 @@ Usage The only configuration options for backend are remote-connection-string and remote-dnssec. -:: +.. code-block:: ini remote-connection-string=:=,=... @@ -63,7 +63,7 @@ Unix connector parameters: path, timeout (default 2000ms) -:: +.. code-block:: ini remote-connection-string=unix:path=/path/to/socket @@ -72,7 +72,7 @@ Pipe connector parameters: command,timeout (default 2000ms) -:: +.. code-block:: ini remote-connection-string=pipe:command=/path/to/executable,timeout=2000 @@ -81,7 +81,7 @@ HTTP connector parameters: url, url-suffix, post, post_json, timeout (default 2000ms) -:: +.. code-block:: ini remote-connection-string=http:url=http://localhost:63636/dns,url-suffix=.php @@ -107,7 +107,7 @@ ZeroMQ connector parameters: endpoint, timeout (default 2000ms) -:: +.. code-block:: ini remote-connection-string=zeromq:endpoint=ipc:///tmp/tmp.sock @@ -159,13 +159,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"initialize", "parameters":{"command":"/path/to/something", "timeout":"2000", "something":"else"}} Response: -:: +.. code-block:: json {"result":true} @@ -191,13 +191,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"lookup", "parameters":{"qtype":"ANY", "qname":"www.example.com.", "remote":"192.0.2.24", "local":"192.0.2.1", "real-remote":"192.0.2.24", "zone-id":-1}} Response: -:: +.. code-block:: json {"result":[{"qtype":"A", "qname":"www.example.com", "content":"203.0.113.2", "ttl": 60}]} @@ -241,13 +241,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"list", "parameters":{"zonename":"example.com.","domain_id":-1}} Response (split into lines for ease of reading) -:: +.. code-block:: json {"result":[ {"qtype":"SOA", "qname":"example.com", "content":"dns1.icann.org. hostmaster.icann.org. 2012081600 7200 3600 1209600 3600", "ttl": 3600}, @@ -294,15 +294,15 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"getbeforeandafternamesabsolute", "params":{"id":0,"qname":"www.example.com"}} Response: -:: +.. code-block:: json - {”result":{"before":"ns1","after":""}} + {"result":{"before":"ns1","after":""}} Example HTTP/RPC '''''''''''''''' @@ -315,9 +315,9 @@ Query: Response: -:: +.. code-block:: json - {”result":{"before":"ns1","after":""}} + {"result":{"before":"ns1","after":""}} ``getAllDomainMetadata`` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -335,13 +335,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"getalldomainmetadata", "parameters":{"name":"example.com"}} Response: -:: +.. code-block:: json {"result":{"PRESIGNED":["0"]}} @@ -380,13 +380,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"getdomainmetadata", "parameters":{"name":"example.com.","kind":"PRESIGNED"}} Response: -:: +.. code-block:: json {"result":["0"]} @@ -424,13 +424,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"setdomainmetadata","parameters":{"name":"example.com","kind":"PRESIGNED","value":["YES"]}} Response: -:: +.. code-block:: json {"result":true} @@ -476,13 +476,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"getdomainkeys","parameters":{"name":"example.com."}} Response: -:: +.. code-block:: json {"result":[{"id":1,"flags":256,"active":true,"content":"Private-key-format: v1.2 Algorithm: 8 (RSASHA256) @@ -538,7 +538,7 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"adddomainkey", "parameters":{"key":{"id":1,"flags":256,"active":true,"content":"Private-key-format: v1.2 Algorithm: 8 (RSASHA256) @@ -553,7 +553,7 @@ Query: Response: -:: +.. code-block:: json {"result":true} @@ -602,13 +602,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json - {"method":"removedomainkey","parameters":"{"name":"example.com","id":1}} + {"method":"removedomainkey","parameters":{"name":"example.com","id":1}} Response: -:: +.. code-block:: json {"result":true} @@ -644,13 +644,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"activatedomainkey","parameters":{"name":"example.com","id":1}} Response: -:: +.. code-block:: json {"result":true} @@ -686,13 +686,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"deactivatedomainkey","parameters":{"name":"example.com","id":1}} Response: -:: +.. code-block:: json {"result": true} @@ -728,15 +728,15 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"gettsigkey","parameters":{"name":"example.com."}} Response: -:: +.. code-block:: json - {"result":{"algorithm":"hmac-md5","content:"kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys="}} + {"result":{"algorithm":"hmac-md5","content":"kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys="}} Example HTTP/RPC '''''''''''''''' @@ -776,15 +776,15 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"getdomaininfo","parameters":{"name":"example.com"}} Response: -:: +.. code-block:: json - {"result":{id:1,"zone":"example.com","kind":"NATIVE","serial":2002010100}} + {"result":{"id":1,"zone":"example.com","kind":"NATIVE","serial":2002010100}} Example HTTP/RPC '''''''''''''''' @@ -818,13 +818,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"setnotified","parameters":{"id":1,"serial":2002010100}} Response: -:: +.. code-block:: json {"result":true} @@ -864,13 +864,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"isMaster","parameters":{"name":"example.com","ip":"198.51.100.0.1"}} Response: -:: +.. code-block:: json {"result":true} @@ -909,19 +909,19 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"superMasterBackend","parameters":{"ip":"198.51.100.0.1","domain":"example.com","nsset":[{"qtype":"NS","qname":"example.com","qclass":1,"content":"ns1.example.com","ttl":300,"auth":true},{"qtype":"NS","qname":"example.com","qclass":1,"content":"ns2.example.com","ttl":300,"auth":true}]}} Response: -:: +.. code-block:: json {"result":true} Alternative response: -:: +.. code-block:: json {"result":{"account":"my account","nameserver":"ns2.example.com"}} @@ -970,13 +970,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"createSlaveDomain","parameters":{"ip":"198.51.100.0.1","domain":"pirate.example.net"}} Response: -:: +.. code-block:: json {"result":true} @@ -1015,13 +1015,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"replaceRRSet","parameters":{"domain_id":2,"qname":"replace.example.com","qtype":"A","trxid":1370416133,"rrset":[{"qtype":"A","qname":"replace.example.com","qclass":1,"content":"1.1.1.1","ttl":300,"auth":true}]}} Response: -:: +.. code-block:: json {"result":true} @@ -1062,13 +1062,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"feedRecord","parameters":{"rr":{"qtype":"A","qname":"replace.example.com","qclass":1,"content":"127.0.0.1","ttl":300,"auth":true},"trxid":1370416133}} Response: -:: +.. code-block:: json {"result":true} @@ -1114,13 +1114,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"feedEnts","parameters":{"domain_id":2,"trxid":1370416133,"nonterm":["_sip._udp","_udp"]}} Response: -:: +.. code-block:: json {"result":true} @@ -1161,13 +1161,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"feedEnts3","parameters":{"domain_id":2,"domain":"example.com","times":1,"salt":"9642","narrow":false,"trxid":1370416356,"nonterm":["_sip._udp","_udp"]}} Response: -:: +.. code-block:: json {"result":true} @@ -1208,13 +1208,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"startTransaction","parameters":{"trxid":1234,"domain_id":1,"domain":"example.com"}} Response: -:: +.. code-block:: json {"result":true} @@ -1255,13 +1255,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"commitTransaction","parameters":{"trxid":1234}} Response: -:: +.. code-block:: json {"result":true} @@ -1299,13 +1299,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"abortTransaction","parameters":{"trxid":1234}} Response: -:: +.. code-block:: json {"result":true} @@ -1344,13 +1344,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"calculateSOASerial","parameters":{"domain":"unit.test","sd":{"qname":"unit.test","nameserver":"ns.unit.test","hostmaster":"hostmaster.unit.test","ttl":300,"serial":1,"refresh":2,"retry":3,"expire":4,"default_ttl":5,"domain_id":-1,"scopeMask":0}}} Response: -:: +.. code-block:: json {"result":2013060501} @@ -1391,13 +1391,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"directBackendCmd","parameters":{"query":"PING"}} Response: -:: +.. code-block:: json {"result":"PONG"} @@ -1437,13 +1437,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method": "getAllDomains", "parameters": {"include_disabled": true}} Response: -:: +.. code-block:: json {"result":[{"id":1,"zone":"unit.test.","masters":["10.0.0.1"],"notified_serial":2,"serial":2,"last_check":1464693331,"kind":"native"}]} @@ -1480,13 +1480,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method":"searchRecords","parameters":{"pattern":"www.example*","maxResults":100}} Response: -:: +.. code-block:: json {"result":[{"qtype":"A", "qname":"www.example.com", "content":"203.0.113.2", "ttl": 60}]} @@ -1523,13 +1523,13 @@ Example JSON/RPC Query: -:: +.. code-block:: json {"method": "getUpdatedMasters", "parameters": {}} Response: -:: +.. code-block:: json {"result":[{"id":1,"zone":"unit.test.","masters":["10.0.0.1"],"notified_serial":2,"serial":2,"last_check":1464693331,"kind":"master"}]} @@ -1561,7 +1561,7 @@ Scenario: SOA lookup via pipe, unix or zeromq connector Query: -:: +.. code-block:: json { "method": "lookup", @@ -1574,7 +1574,7 @@ Query: Reply: -:: +.. code-block:: json { "result": @@ -1599,7 +1599,7 @@ Query: Reply: -:: +.. code-block:: json { "result": diff --git a/docs/changelog/pre-4.0.rst b/docs/changelog/pre-4.0.rst index a6246ea2db43..2c16c085a46e 100644 --- a/docs/changelog/pre-4.0.rst +++ b/docs/changelog/pre-4.0.rst @@ -5042,7 +5042,7 @@ After the SOA of example.org was raised If however our slaves would ignore us, as some are prone to do, we can send some additional notifications -:: +.. code-block:: shell $ sudo pdns_control notify example.org Added to queue @@ -5055,7 +5055,7 @@ send some additional notifications Conversely, if PowerDNS needs to be reminded to retrieve a zone from a master, a command is provided -:: +.. code-block:: shell $ sudo pdns_control retrieve forfun.net Added retrieval request for 'forfun.net' from master 212.187.98.67 diff --git a/docs/dnssec/index.rst b/docs/dnssec/index.rst index dad7dcd01a1b..85fa195e60ff 100644 --- a/docs/dnssec/index.rst +++ b/docs/dnssec/index.rst @@ -19,7 +19,7 @@ automatically. As an example, securing an existing zone can be as simple as: -:: +.. code-block:: shell $ pdnsutil secure-zone powerdnssec.org diff --git a/docs/dnssec/migration.rst b/docs/dnssec/migration.rst index 53d6a412e56c..0ba6656c8716 100644 --- a/docs/dnssec/migration.rst +++ b/docs/dnssec/migration.rst @@ -24,21 +24,21 @@ all the changes in database schemas as shown in the :doc:`upgrade documentation To deliver a correctly signed zone with the :ref:`dnssec-pdnsutil-dnssec-defaults`, invoke: -:: +.. code-block:: shell pdnsutil secure-zone ZONE To view the DS records for this zone (to transfer to the parent zone), run -:: +.. code-block:: shell pdnsutil show-zone ZONE For a more traditional setup with a KSK and a ZSK, use the following sequence of commands: -:: +.. code-block:: shell pdnsutil add-zone-key ZONE ksk 2048 active rsasha256 pdnsutil add-zone-key ZONE zsk 1024 active rsasha256 @@ -85,7 +85,7 @@ The ``pdnsutil`` tool features the option to import zone keys in the industry standard private key format, version 1.2. To import an existing KSK, use -:: +.. code-block:: shell pdnsutil import-zone-key ZONE FILENAME ksk diff --git a/docs/dnssec/operational.rst b/docs/dnssec/operational.rst index f3cfce1ce7db..ce3db3e05ecd 100644 --- a/docs/dnssec/operational.rst +++ b/docs/dnssec/operational.rst @@ -19,7 +19,7 @@ zone. Going insecure -------------- -:: +.. code-block:: shell pdnsutil disable-dnssec ZONE @@ -34,13 +34,13 @@ Setting the NSEC modes and parameters As stated earlier, PowerDNS uses NSEC by default. If you want to use NSEC3 instead, issue: -:: +.. code-block:: shell pdnsutil set-nsec3 ZONE [PARAMETERS] e.g. -:: +.. code-block:: shell pdnsutil set-nsec3 example.net '1 0 1 ab' @@ -56,7 +56,7 @@ The quoted part is the content of the NSEC3PARAM records, as defined in To convert a zone from NSEC3 to NSEC operations, run: -:: +.. code-block:: shell pdnsutil unset-nsec3 ZONE diff --git a/docs/dnsupdate.rst b/docs/dnsupdate.rst index 94ed0b0fc06f..c65eae1eb3ca 100644 --- a/docs/dnsupdate.rst +++ b/docs/dnsupdate.rst @@ -120,19 +120,19 @@ This setting allows you to set the TSIG key required to do an DNS update. If you have GSS-TSIG enabled, you can use Kerberos principals here. An example, using :program:`pdnsutil` to create the key: -:: +.. code-block:: shell - pdnsutil generate-tsig-key test hmac-md5 + $ pdnsutil generate-tsig-key test hmac-md5 Create new TSIG key test hmac-md5 kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys= - + +:: + sql> insert into tsigkeys (name, algorithm, secret) values ('test', 'hmac-md5', 'kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys='); sql> select id from domains where name='example.org'; 5 sql> insert into domainmetadata (domain_id, kind, content) values (5, 'TSIG-ALLOW-DNSUPDATE', 'test'); -An example of how to use a TSIG key with the :program:`nsupdate` command: - -:: +An example of how to use a TSIG key with the :program:`nsupdate` command:: nsupdate < @@ -252,14 +252,14 @@ Setting up dhcpd We're going to use a TSIG key for security. We're going to generate a key using the following command: -:: +.. code-block:: shell dnssec-keygen -a hmac-md5 -b 128 -n USER dhcpdupdate This generates two files (Kdhcpdupdate.*.key and Kdhcpdupdate.*.private). You're interested in the .key file: -:: +.. code-block:: shell # ls -l Kdhcp* -rw------- 1 root root 53 Aug 26 19:29 Kdhcpdupdate.+157+20493.key @@ -338,7 +338,7 @@ dynamic updates from **dhcpd**. Enabled DNS update (:rfc:`2136`) support functionality in PowerDNS by adding the following to the PowerDNS configuration file (pdns.conf). -:: +.. code-block:: ini dnsupdate=yes allow-dnsupdate-from= @@ -467,7 +467,7 @@ There are many same things available as in recursor Lua scripts, but there is also resolve(qname, qtype) which returns array of records. Example: -:: +.. code-block:: lua resolve("www.google.com", pdns.A) @@ -477,7 +477,7 @@ resolve does not perform local lookup. Simple example script: -.. code:: lua +.. code-block:: lua --- This script is not suitable for production use diff --git a/docs/domainmetadata.rst b/docs/domainmetadata.rst index c3c6ece7fc2a..926628037ff7 100644 --- a/docs/domainmetadata.rst +++ b/docs/domainmetadata.rst @@ -30,7 +30,7 @@ that tries to allow all potential slaves in. Example: -:: +.. code-block:: shell pdnsutil set-meta powerdns.org ALLOW-AXFR-FROM AUTO-NS 2001:db8::/48 @@ -38,10 +38,10 @@ Each ACL has its own row in the database: :: - select id from domains where name='example.com'; + sql> select id from domains where name='example.com'; 7 - insert into domainmetadata (domain_id, kind, content) values (7,'ALLOW-AXFR-FROM','AUTO-NS'); - insert into domainmetadata (domain_id, kind, content) values (7,'ALLOW-AXFR-FROM','2001:db8::/48'); + sql> insert into domainmetadata (domain_id, kind, content) values (7,'ALLOW-AXFR-FROM','AUTO-NS'); + sql> insert into domainmetadata (domain_id, kind, content) values (7,'ALLOW-AXFR-FROM','2001:db8::/48'); To disallow all IP's, except those explicitly allowed by domainmetadata records, add ``allow-axfr-ips=`` to ``pdns.conf``. @@ -82,14 +82,14 @@ When notifying this domain, also notify this nameserver (can occur multiple times). The nameserver may have contain an optional port number. e.g.: -:: +.. code-block:: shell pdnsutil set-meta powerdns.org ALSO-NOTIFY 192.0.2.1:5300 pdnsutil set-meta powerdns.org ALLOW-AXFR-FROM 2001:db8:53::1 Or in SQL: -:: +.. code-block:: SQL insert into domainmetadata (domain_id, kind, content) values (7,'ALSO-NOTIFY','192.0.2.1:5300'); insert into domainmetadata (domain_id, kind, content) values (7,'ALLOW-AXFR-FROM','2001:db8:53::1'); diff --git a/docs/guides/alias.rst b/docs/guides/alias.rst index 19e2e7946d66..419d32e0d4c7 100644 --- a/docs/guides/alias.rst +++ b/docs/guides/alias.rst @@ -9,7 +9,7 @@ Server 4.1.0 or higher, set the :ref:`setting-resolver` setting to an existing resolver and enable :ref:`setting-expand-alias`: -:: +.. code-block:: ini resolver=[::1]:5300 expand-alias=yes diff --git a/docs/guides/basic-database.rst b/docs/guides/basic-database.rst index 0a832e37c739..2dac35834f67 100644 --- a/docs/guides/basic-database.rst +++ b/docs/guides/basic-database.rst @@ -6,7 +6,7 @@ is called 'gmysql', and needs to be configured in ``pdns.conf``. Add the following lines, adjusted for your local setup (specifically, you may not want to use the 'root' user): -:: +.. code-block:: ini launch=gmysql gmysql-host=127.0.0.1 @@ -49,6 +49,7 @@ Connect to MySQL as a user with sufficient privileges and issue the following commands: .. literalinclude:: ../../modules/gmysqlbackend/schema.mysql.sql + :language: SQL Now we have a database and an empty table. PowerDNS should now be able to launch in monitor mode and display no errors: @@ -66,7 +67,7 @@ to launch in monitor mode and display no errors: In a different shell, a sample query sent to the server should now return quickly without data: -:: +.. code-block:: shell $ dig +short www.example.com @127.0.0.1 $ @@ -110,7 +111,7 @@ Now we need to add some records to our database (in a separate shell): If we now requery our database, ``www.example.com`` should be present: -:: +.. code-block:: shell $ dig +short www.example.com @127.0.0.1 192.0.2.10 diff --git a/docs/guides/kskroll.rst b/docs/guides/kskroll.rst index f50ccc0728b3..ee48f23549a4 100644 --- a/docs/guides/kskroll.rst +++ b/docs/guides/kskroll.rst @@ -13,7 +13,7 @@ both a KSK and a CSK. To start the rollover, add an **active** new KSK to the zone (example.net in this case): -:: +.. code-block:: shell pdnsutil add-zone-key example.net ksk active @@ -24,7 +24,7 @@ If this zone is of the type 'MASTER', increase the SOA serial. The rollover is now in the "New KSK" stage. Retrieve the DS record(s) for the new KSK: -:: +.. code-block:: shell pdnsutil show-zone example.net @@ -38,7 +38,7 @@ rollover is now in the "DS Change" state and can continue to the The key-id for the old KSK is shown in the output of ``pdnsutil show-zone example.net``. -:: +.. code-block:: shell pdnsutil remove-zone-key example.net KEY-ID diff --git a/docs/guides/recursion.rst b/docs/guides/recursion.rst index c43536add733..bca561d6aeb0 100644 --- a/docs/guides/recursion.rst +++ b/docs/guides/recursion.rst @@ -50,7 +50,7 @@ should be removed: To make the authoritative server listen on the local loopback address and port 5300 change the following in ``pdns.conf``: -:: +.. code-block:: ini local-ipv6= local-address=127.0.0.1 @@ -87,7 +87,7 @@ Authoritative Server. This is done using the ``recursor.conf``. The domains should be forwarded to 127.0.0.1:5300 (the new address and port of the Authoritative Server): -:: +.. code-block:: ini forward-zones=private.example.com=127.0.0.1:5300 forward-zones+=another.example.com=127.0.0.1:5300 @@ -133,7 +133,7 @@ should be removed: To make the authoritative server listen on the local loopback address and port 5300 change the following in ``pdns.conf``: -:: +.. code-block:: ini local-ipv6= local-address=127.0.0.1 @@ -153,7 +153,7 @@ Configure the recursor to listen on the local loopback interface on a different port than the Authoritative Server. Set the following in ``recursor.conf``: -:: +.. code-block:: ini local-address=127.0.0.1 local-port=5301 @@ -164,7 +164,7 @@ Authoritative Server. This is done using the ``recursor.conf``. The domains should be forwarded to 127.0.0.1:5300 (the new address and port of the Authoritative Server): -:: +.. code-block:: ini forward-zones=private.example.com=127.0.0.1:5300 forward-zones+=another.example.com=127.0.0.1:5300 diff --git a/docs/guides/virtual-instances.rst b/docs/guides/virtual-instances.rst index 7794a77f58d1..87251ceefca0 100644 --- a/docs/guides/virtual-instances.rst +++ b/docs/guides/virtual-instances.rst @@ -40,13 +40,13 @@ Assuming your instance is called ``myinstance`` and ``pdns-myinstance.conf`` exists in the configuration directory, the following command will start the service: -:: +.. code-block:: shell systemctl start pdns@myinstance.service Similarly you can enable it at boot: -:: +.. code-block:: shell systemctl enable pdns@myinstance.service diff --git a/docs/guides/zskroll.rst b/docs/guides/zskroll.rst index a139bd319629..aa7631a07ccf 100644 --- a/docs/guides/zskroll.rst +++ b/docs/guides/zskroll.rst @@ -12,7 +12,7 @@ First, create a new inactive ZSK for the zone (if one already exists, you can skip this step), we add an ECDSA 256 bit key (algorithm 13) here: -:: +.. code-block:: shell pdnsutil add-zone-key example.net zsk inactive ecdsa256 @@ -23,7 +23,7 @@ database and wait for the slaves to pickup the zone change. To change the RRSIGs on your records, the new key must be made active. Note: you can get the key-ids with ``pdnsutil show-zone example.net``: -:: +.. code-block:: shell pdnsutil activate-zone-key example.net new-key-id pdnsutil deactivate-zone-key example.net previous-key-id @@ -33,7 +33,7 @@ the "new RRSIGs" stage of the roll over. The last step is to remove the old key from the completely: -:: +.. code-block:: shell pdnsutil remove-zone-key example.net previous-key-id diff --git a/docs/installation.rst b/docs/installation.rst index 6e1753d8eca4..0d6bd6da9e11 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -17,7 +17,7 @@ Debian-based Systems PowerDNS Authoritative Server is available through the `apt `__ system. -:: +.. code-block:: shell # apt-get install pdns-server @@ -25,7 +25,7 @@ Debian splits the backends into `several different packages `__, install the required backend as follows: -:: +.. code-block:: shell # apt-get install pdns-backend-$backend @@ -39,13 +39,13 @@ or from `the PowerDNS repositories `__: Add either to your list of repositories and install PowerDNS by issuing: -:: +.. code-block:: shell # yum install pdns The different backends can be installed using -:: +.. code-block:: shell # yum install pdns-backend-$backend @@ -57,13 +57,13 @@ PowerDNS Authoritative Server is available through the For the package: -:: +.. code-block:: shell # pkg install dns/powerdns To have your system build the port: -:: +.. code-block:: shell cd /usr/ports/dns/powerdns/ && make install clean @@ -72,7 +72,7 @@ Mac OS X PowerDNS Authoritative Server is available through Homebrew: -:: +.. code-block:: shell $ brew install pdns diff --git a/docs/migration.rst b/docs/migration.rst index bfcab43a164f..0a81af1d51fe 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -28,7 +28,7 @@ In order to migrate to a Generic SQL backend, add all your domains to the 'domains' table with the IP of your current master. On your current master, make sure that this master allows AXFRs to this new slave. -:: +.. code-block:: SQL INSERT INTO domains (name,type,master) VALUES ('example.net', 'SLAVE', '198.51.100.101'); @@ -36,7 +36,7 @@ Then start PowerDNS and wait for all the zones to be transferred. If this server is the new :ref:`master `, change the type of domain in the database: -:: +.. code-block:: SQL UPDATE domains set type='MASTER' where type='SLAVE'; @@ -45,7 +45,7 @@ and restart PowerDNS. Or, if you want to use :ref:`native `: -:: +.. code-block:: SQL UPDATE domains set type='NATIVE' where type='SLAVE'; @@ -116,7 +116,7 @@ See `its manpage ` for more information. An example call to ``zone2sql`` could be: -:: +.. code-block:: shell zone2sql --named-conf=/path/to/named.conf --gmysql | mysql -u pdns -p pdns-db diff --git a/docs/modes-of-operation.rst b/docs/modes-of-operation.rst index 15af055bedcc..d8d79aef5516 100644 --- a/docs/modes-of-operation.rst +++ b/docs/modes-of-operation.rst @@ -246,7 +246,7 @@ the ``domainmetadata`` table for the domain. Supposing the domain we want has an ``id`` of 3, the following SQL statement will enable the Lua script ``my.lua`` for that domain: -:: +.. code-block:: SQL INSERT INTO domainmetadata (domain_id, kind, content) VALUES (3, "LUA-AXFR-SCRIPT", "/lua/my.lua"); @@ -270,7 +270,7 @@ incoming record as normal. Consider the following simple example: -:: +.. code-block:: lua function axfrfilter(remoteip, zone, record) diff --git a/docs/security-advisories/powerdns-advisory-2012-01.rst b/docs/security-advisories/powerdns-advisory-2012-01.rst index 0c82fd2349be..cd4165284919 100644 --- a/docs/security-advisories/powerdns-advisory-2012-01.rst +++ b/docs/security-advisories/powerdns-advisory-2012-01.rst @@ -37,7 +37,7 @@ Alternatively, on Linux systems with a working iptables setup, 'responses' sent to the PowerDNS Authoritative Server 'question' address can be blocked by issuing: -:: +.. code-block:: shell iptables -I INPUT -p udp --dst $AUTHIP --dport 53 \! -f -m u32 --u32 "0>>22&0x3C@8>>15&0x01=1" -j DROP @@ -57,7 +57,7 @@ announcement. For those running custom PowerDNS versions, just applying this patch may be easier: -:: +.. code-block:: diff --- pdns/common_startup.cc (revision 2326) +++ pdns/common_startup.cc (working copy) diff --git a/docs/settings.rst b/docs/settings.rst index eab1244d2700..49d6371c53bd 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -682,7 +682,7 @@ Which backends to launch and order to query them in. Launches backends. In its most simple form, supply all backends that need to be launched. e.g. -:: +.. code-block:: ini launch=bind,gmysql,remote @@ -690,7 +690,7 @@ If you find that you need to query a backend multiple times with different configuration, you can specify a name for later instantiations. e.g.: -:: +.. code-block:: ini launch=gmysql,gmysql:server2 @@ -1153,7 +1153,9 @@ To notify all IP addresses apart from the 192.168.0.0/24 subnet use the followin Otherwise there will be error trying to resolve address. For example, slaves support both IPv4 and IPv6, but PowerDNS master have only IPv4, - so allow only IPv4 with ``only-notify``:: + so allow only IPv4 with ``only-notify``: + + .. code-block:: ini only-notify=0.0.0.0/0 diff --git a/docs/tsig.rst b/docs/tsig.rst index f4074b2596f4..df97df2e74eb 100644 --- a/docs/tsig.rst +++ b/docs/tsig.rst @@ -34,7 +34,9 @@ with the key name in the content field. For example:: $ dig -t axfr powerdnssec.org @127.0.0.1 -y 'test:kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys=' Another of importing and activating TSIG keys into the database is using -:doc:`pdnsutil `:: +:doc:`pdnsutil `: + +.. code-block:: shell pdnsutil import-tsig-key test hmac-md5 'kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys=' pdnsutil activate-tsig-key powerdnssec.org test master @@ -70,9 +72,7 @@ The actual TSIG key must also be provisioned, as outlined in the previous section. For the Generic SQL backends, configuring the use of TSIG for AXFR -requests could be achieved as follows: - -:: +requests could be achieved as follows:: insert into tsigkeys (name, algorithm, secret) values ('test', 'hmac-md5', 'kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys='); select id from domains where name='powerdnssec.org'; @@ -82,7 +82,7 @@ requests could be achieved as follows: This can also be done using :doc:`/manpages/pdnsutil.1`: -:: +.. code-block:: shell pdnsutil import-tsig-key test hmac-md5 'kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys=' pdnsutil activate-tsig-key powerdnssec.org test slave @@ -91,9 +91,7 @@ This setup corresponds to the ``TSIG-ALLOW-AXFR`` access rule defined in the previous section. In the interest of interoperability, the configuration above is (not -quite) similar to the following BIND statements: - -:: +quite) similar to the following BIND statements:: key test. { algorithm hmac-md5; From 36d10f0f6c47635d0665d9063c7bce4eb1429a55 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:21 +0200 Subject: [PATCH 059/127] docs: reword sentence in DNSSEC intro "confirmation can be gotten" sounds weird to me. --- docs/dnssec/intro.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dnssec/intro.rst b/docs/dnssec/intro.rst index 09517b6439dc..239b67460a8f 100644 --- a/docs/dnssec/intro.rst +++ b/docs/dnssec/intro.rst @@ -14,7 +14,7 @@ is used for verification. The private part is used for signing and is never published. To make sure that the internet knows that the key that is used for -signing is the authentic key, confirmation can be gotten from the parent +signing is the authentic key, confirmation can be obtained from the parent zone. This means that to become operational, a zone operator will have to publish a representation of the signing key to the parent zone, often a ccTLD or a gTLD. This representation is called a DS record, and is a From 76a678b6757f5e8a4787ab89fd8451dc2e2b4fac Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:21 +0200 Subject: [PATCH 060/127] docs: fix link in Lua backend Two problems with the existing link: * target was non-existent (anchor is different in Sphinx) * nested markup for monospaced text on link is not possible, see https://stackoverflow.com/q/4743845/1254292 --- docs/backends/lua.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/backends/lua.rst b/docs/backends/lua.rst index b7d39dcb2b99..c95702114bea 100644 --- a/docs/backends/lua.rst +++ b/docs/backends/lua.rst @@ -66,6 +66,8 @@ This will give you back three parameters with ``remote_ip``, Can only be used in the functions ``list()`` and ``getsoa()``. +.. _backends_lua_fun_getarg: + ``getarg("PARAMETER")`` ~~~~~~~~~~~~~~~~~~~~~~~ @@ -75,8 +77,8 @@ pdns.conf file. ``mustdo("PARAMETER")`` ~~~~~~~~~~~~~~~~~~~~~~~ -This is the same as ```getarg()`` <#getarg>`__ but return a boolean -instead of a string. +This is the same as :ref:`getarg() `, but returns +a boolean instead of a string. You also have all the different QTypes in a table called 'QTypes'. From 3c0f81fcae5ba743adda17b71a09166f83cbb98b Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:21 +0200 Subject: [PATCH 061/127] docs: Fix formatting of Lua2 backend API Without a blank line between the paragraphs in the descriptions of RST description lists [1], the list items will not render as list items, but as a long single paragraph. [1]: http://docutils.sourceforge.net/docs/user/rst/quickref.html#definition-lists --- docs/backends/lua2.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/backends/lua2.rst b/docs/backends/lua2.rst index 7b079a0d4e68..e12c1441e1d3 100644 --- a/docs/backends/lua2.rst +++ b/docs/backends/lua2.rst @@ -46,6 +46,7 @@ INPUT: OUTPUT: Expects a array which has tables with following keys: + - DNSName name - resource record name (can also be string) - string type - type of resource record (can also be QType or valid integer) - string content - resource record content @@ -85,6 +86,7 @@ INPUT: OUTPUT: Return false if not supported or found, otherwise expects a table with keys: + - string account - Associated account of this domain (default: ) - string kind - Domain kind (NATIVE,MASTER,SLAVE) (default: NATIVE) - int id - Associated domain ID (default: -1) @@ -147,6 +149,7 @@ INPUT: OUTPUT: Return false if not found or supported, otherwise expects array of tables with keys: + - int id - Key ID - int flags - Key flags - bool active - Is key active @@ -165,6 +168,7 @@ INPUT: OUTPUT: Table with keys: + - unhashed - DNSName of the unhashed relative to domain - before - (hashed) name of previous record relative to domain - after - (hashed) name of next record relative to domain From 274d02444b9ed6a8fb9763960c69f38e2e8d19ec Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 062/127] docs: Fix link in Lua2 backend --- docs/backends/lua2.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/backends/lua2.rst b/docs/backends/lua2.rst index e12c1441e1d3..f08815de2f36 100644 --- a/docs/backends/lua2.rst +++ b/docs/backends/lua2.rst @@ -76,6 +76,8 @@ OUTPUT: NOTES: This function is **optional**. + +.. _backends_lua2_dns_get_domaininfo: ``dns_get_domaininfo(domain)`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -108,7 +110,8 @@ NOTES: Get domain information for all domains. OUTPUT: - Return false if not supported or found, otherwise return a table of string, domaininfo. See ``dns_get_domaininfo```. + Return false if not supported or found, otherwise return a table of string, + domaininfo. See :ref:`dns_get_domaininfo() `. NOTES: This function is **optional**, except if you need master functionality. From da7a5d84fbbbaf08560d82f681201ab1c4c18619 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 063/127] docs: Other minor fixes * Make sentence actually part of the 'versionchanged' directive on the settings page. Fixes sphinx-build warning. * LDAP backend formatting changes so that ldap:// URIs don't get linkified. * Fix sphinx-build warning about under and over underlined sections. --- docs/backends/generic-postgresql.rst | 2 +- docs/backends/ldap.rst | 6 +++--- docs/settings.rst | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/backends/generic-postgresql.rst b/docs/backends/generic-postgresql.rst index 41738ec9ca55..c77187683413 100644 --- a/docs/backends/generic-postgresql.rst +++ b/docs/backends/generic-postgresql.rst @@ -82,7 +82,7 @@ Enable DNSSEC processing for this backend. Default: no. .. _setting-gpgsql-extra-connection-parameters: ``gpgsql-extra-connection-parameters`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Extra connection parameters to forward to postgres. If you want to pin a specific certificate for the connection you should set this to diff --git a/docs/backends/ldap.rst b/docs/backends/ldap.rst index 76111e2b65a6..266be52bba7b 100644 --- a/docs/backends/ldap.rst +++ b/docs/backends/ldap.rst @@ -74,7 +74,7 @@ To launch the ldap backend: ``ldap-host`` ^^^^^^^^^^^^^ -(default "ldap://127.0.0.1:389/") : The values assigned to this +(default "``ldap://127.0.0.1:389/``") : The values assigned to this parameter can be LDAP URIs (e.g. ``ldap://127.0.0.1/`` or ``ldaps://127.0.0.1/``) describing the connection to the LDAP server. There can be multiple LDAP URIs specified for load balancing and high @@ -89,7 +89,7 @@ followed by a colon and the port). ^^^^^^^^^^^^^^^^^ (default "no") : Use TLS encrypted connections to the LDAP server. This -is only allowed if ldap-host is a ldap:// URI or a host name / IP +is only allowed if ldap-host is an ``ldap://`` URI or a host name / IP address. .. _setting-ldap-timeout: @@ -629,4 +629,4 @@ SASL support ^^^^^^^^^^^^ Support for more authentication methods would be handy. Anyone -interested may `contribute `__.? +interested may `contribute `__. diff --git a/docs/settings.rst b/docs/settings.rst index 49d6371c53bd..e8647faa2898 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -155,7 +155,7 @@ Static pre-shared authentication key for access to the REST API. .. versionadded:: 4.0.0 .. versionchanged:: 4.2.0 -This setting has been removed in 4.2.0. + This setting has been removed in 4.2.0. Disallow data modification through the REST API when set. @@ -324,7 +324,7 @@ The value of :ref:`metadata-api-rectify` if it is not set on the zone. .. _setting-default-ksk-algorithm: ``default-ksk-algorithm`` --------------------------- +------------------------- - String - Default: ecdsa256 From 5a311890aa24f5aeeb1fe299876b022fc55b97a3 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 064/127] docs: HTTP API tsigkeys example shows wrong body Two errors fixed: * The JSON body parameter key 'key' was provided twice. I think it should have been the 'name' key. * One value wasn't terminated with a double quote. The HTTP code block parser warned about this in the sphinx-build output. --- docs/http-api/tsigkey.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/http-api/tsigkey.rst b/docs/http-api/tsigkey.rst index 83b3db55f195..5509e21d569e 100644 --- a/docs/http-api/tsigkey.rst +++ b/docs/http-api/tsigkey.rst @@ -36,7 +36,7 @@ Modifying the key material X-Api-Key: secret Content-Type: application/json - {"key": "mytsigkey, "key": "GQNyFy1QagMUarHmiSgsIJajghdTGJGVcN5TRVwgbclzxGyhQR1uYLCOyJ/uj9uj12jyeLwzJuW12wCI9PYv7Q=="} + {"name": "mytsigkey", "key": "GQNyFy1QagMUarHmiSgsIJajghdTGJGVcN5TRVwgbclzxGyhQR1uYLCOyJ/uj9uj12jyeLwzJuW12wCI9PYv7Q=="} .. code-block:: http From a771f67f26211ac78a82516351fb3c9572050da7 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 065/127] docs: formatting fixes for 'Dynamic DNS Update' page * Fix a broken link to the domain metadata. * Prettify method listing paragraph. --- docs/dnsupdate.rst | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/dnsupdate.rst b/docs/dnsupdate.rst index c65eae1eb3ca..8c6842eaef2f 100644 --- a/docs/dnsupdate.rst +++ b/docs/dnsupdate.rst @@ -90,7 +90,7 @@ Per zone settings ----------------- For permissions, a number of per zone settings are available via the -:doc:`domain metadata ``. +:doc:`domain metadata `. .. _metadata-allow-dnsupdate-from: @@ -454,17 +454,18 @@ each record at a time and you can approve or reject any or all. The object has following methods available: -- DNSName getQName() - name to update -- DNSName getZonename() - zone name -- int getQType() - record type, it can be 255(ANY) for delete. -- ComboAddress getLocal() - local socket address -- ComboAddress getRemote() - remote socket address -- Netmask getRealRemote() - real remote address (or netmask if EDNS Subnet is used) -- DNSName getTsigName() - TSIG **key** name (you can assume it is validated here) -- string getPeerPrincipal() - Return peer principal name (user@DOMAIN, service/machine.name@DOMAIN, host/MACHINE$@DOMAIN) +- ``DNSName getQName()`` - name to update +- ``DNSName getZonename()`` - zone name +- ``int getQType()`` - record type, it can be 255(ANY) for delete. +- ``ComboAddress getLocal()`` - local socket address +- ``ComboAddress getRemote()`` - remote socket address +- ``Netmask getRealRemote()`` - real remote address (or netmask if EDNS Subnet is used) +- ``DNSName getTsigName()`` - TSIG **key** name (you can assume it is validated here) +- ``string getPeerPrincipal()`` - Return peer principal name (``user@DOMAIN``, + ``service/machine.name@DOMAIN``, ``host/MACHINE$@DOMAIN``) There are many same things available as in recursor Lua scripts, but -there is also resolve(qname, qtype) which returns array of records. +there is also ``resolve(qname, qtype)`` which returns array of records. Example: .. code-block:: lua From 3ee1b9c9376f6fcb5e7f56e5938d8132196660b4 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 066/127] docs: Use 'sudo' to install packages This also fixes the rendering of the shell command - was highlighted as comment. --- docs/installation.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 0d6bd6da9e11..2b25b06f492a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -19,7 +19,7 @@ PowerDNS Authoritative Server is available through the .. code-block:: shell - # apt-get install pdns-server + $ sudo apt-get install pdns-server Debian splits the backends into `several different packages `__, install the @@ -27,7 +27,7 @@ required backend as follows: .. code-block:: shell - # apt-get install pdns-backend-$backend + $ sudo apt-get install pdns-backend-$backend Redhat-based Systems ~~~~~~~~~~~~~~~~~~~~ @@ -41,13 +41,13 @@ Add either to your list of repositories and install PowerDNS by issuing: .. code-block:: shell - # yum install pdns + $ sudo yum install pdns The different backends can be installed using .. code-block:: shell - # yum install pdns-backend-$backend + $ sudo yum install pdns-backend-$backend FreeBSD ~~~~~~~ @@ -59,7 +59,7 @@ For the package: .. code-block:: shell - # pkg install dns/powerdns + $ sudo pkg install dns/powerdns To have your system build the port: From 9ae11fa4d899e79a29b19438774728eff285ae52 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 067/127] docs: Emphasize no data should return in example The line with just a '$' confused be until I've read the sentence above it again. --- docs/guides/basic-database.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/guides/basic-database.rst b/docs/guides/basic-database.rst index 2dac35834f67..8396096ef02b 100644 --- a/docs/guides/basic-database.rst +++ b/docs/guides/basic-database.rst @@ -65,12 +65,11 @@ to launch in monitor mode and display no errors: 15:39:55 [gMySQLbackend] MySQL connection succeeded In a different shell, a sample query sent to the server should now -return quickly without data: +return quickly *without* data: .. code-block:: shell - $ dig +short www.example.com @127.0.0.1 - $ + $ dig +short www.example.com @127.0.0.1 # should print nothing .. warning:: When debugging DNS problems, don't use ``host``. Please use From 74be66a8b30d54a2732afe107f3388a93884d6cf Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 068/127] docs: fix links in 'Adding new DNS record types' nested markup for monospaced text on link is not possible, see https://stackoverflow.com/q/4743845/1254292 --- docs/guides/addingrecords.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/addingrecords.rst b/docs/guides/addingrecords.rst index de3f630674fd..37a3c1c36d77 100644 --- a/docs/guides/addingrecords.rst +++ b/docs/guides/addingrecords.rst @@ -47,10 +47,10 @@ Next, it defines that the rest of the record is the actual certificate methods are supplied for all DNS data types in use. Now add ``TLSARecordContent::report()`` to -```reportOtherTypes()`` `__. +`reportOtherTypes() `__. And that's it. For completeness, add TLSA and 52 to the QType enum in -```qtype.hh`` `__, +`qtype.hh `__, which makes it easier to refer to the TLSA record in code if so required. From 12388955907a0209e66a952e096948d823236f20 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 069/127] docs: Hide the toctree on 'Backends' index page Effectively, all the backends are listed in the nicely formatted table already. --- docs/backends/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/backends/index.rst b/docs/backends/index.rst index caa25276aa2a..b4149d056748 100644 --- a/docs/backends/index.rst +++ b/docs/backends/index.rst @@ -44,6 +44,7 @@ These backends have :doc:`features unique ` to the generic SQL back .. toctree:: :maxdepth: 1 + :hidden: bind generic-sql From d5eff893d1ac98de63c54252a5c6a48ce1b8f9b5 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 070/127] docs: Change occurrences of "note" to admonition --- docs/backends/bind.rst | 10 +++++++--- docs/changelog/4.0.rst | 7 ++++--- docs/settings.rst | 9 +++++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/backends/bind.rst b/docs/backends/bind.rst index 0dc1bf9efb00..4ee4cb13a26f 100644 --- a/docs/backends/bind.rst +++ b/docs/backends/bind.rst @@ -22,7 +22,11 @@ information about zones from it. It makes no attempt to honour other configuration flags, which you should configure (when available) using the PowerDNS native configuration. -**note**: Because this backend retrieves its configuration from a text file and not a database, the HTTP API is unable to process changes for this backend. This effectively makes the API read-only for zones hosted by the BIND backend. +.. note:: + Because this backend retrieves its configuration from a text file and + not a database, the HTTP API is unable to process changes for this + backend. This effectively makes the API read-only for zones hosted by + the BIND backend. Configuration Parameters ------------------------ @@ -195,5 +199,5 @@ are picked up in the same way as master and slave zones, see Native zones in the BIND backend are supported since version 4.1.0 of the PowerDNS Authoritative Server. -**note**: Any zone with no ``type`` set (an error in BIND) is assumed to -be native. +.. note:: + Any zone with no ``type`` set (an error in BIND) is assumed to be native. diff --git a/docs/changelog/4.0.rst b/docs/changelog/4.0.rst index d23088680914..587831457013 100644 --- a/docs/changelog/4.0.rst +++ b/docs/changelog/4.0.rst @@ -442,9 +442,10 @@ PowerDNS Authoritative Server 4.0.0-rc2 Released June 29th 2016 -**note**: rc1 was tagged in git but never officially released. Kees -Monshouwer discovered an issue in the gmysql backend that would -terminate the daemon on a connection error, this fixed in rc2. +.. note:: + rc1 was tagged in git but never officially released. Kees + Monshouwer discovered an issue in the gmysql backend that would + terminate the daemon on a connection error, this fixed in rc2. This Release Candidate adds IXFR consumption and fixes some issues with prepared statements: diff --git a/docs/settings.rst b/docs/settings.rst index e8647faa2898..4f3028eb0148 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -620,11 +620,12 @@ A/AAAA). If this is disabled (the default), ALIAS records will not expanded and the server will will return NODATA for A/AAAA queries for such names. -**note**: :ref:`setting-resolver` must also be set for ALIAS -expansion to work! +.. note:: + :ref:`setting-resolver` must also be set for ALIAS expansion to work! -**note**: In PowerDNS Authoritative Server 4.0.x, this setting did not -exist and ALIAS was always expanded. +.. note:: + In PowerDNS Authoritative Server 4.0.x, this setting did not exist and + ALIAS was always expanded. .. _setting-forward-dnsupdate: From 81b050b3be7b3c0fe920671f96d06f5816313d94 Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 071/127] docs: fix several typos --- docs/appendices/compiling.rst | 2 +- docs/settings.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/appendices/compiling.rst b/docs/appendices/compiling.rst index 8972ecf6f2ee..a29d768bd7ce 100644 --- a/docs/appendices/compiling.rst +++ b/docs/appendices/compiling.rst @@ -17,7 +17,7 @@ Each backend has a module name that you look up in this table. To compile a module for inclusion at runtime, which is great if you are a unix vendor, use ``--with-dynmodules='mod1 mod2 mod3'``. These modules then end up as .so files in the compiled ``libdir``. -By default, the :doc:`bind <../../backends/bind>`, :doc:`mysql <../../backends/generic-mysql>` and :doc:`random <../../backends/random>` are compiled into the binary. The :doc:`pipe <../../backends/pipe>` is, by default, compiled as a runtime loadable module. +By default, the :doc:`bind <../../backends/bind>`, :doc:`mysql <../../backends/generic-mysql>` and :doc:`random <../../backends/random>` modules are compiled into the binary. The :doc:`pipe <../../backends/pipe>` is, by default, compiled as a runtime loadable module. Getting the sources ------------------- diff --git a/docs/settings.rst b/docs/settings.rst index 4f3028eb0148..974d518da7d6 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -617,7 +617,7 @@ Entropy source file to use. If this is enabled, ALIAS records are expanded (synthesised to their A/AAAA). -If this is disabled (the default), ALIAS records will not expanded and +If this is disabled (the default), ALIAS records will not be expanded and the server will will return NODATA for A/AAAA queries for such names. .. note:: @@ -1149,7 +1149,7 @@ To notify all IP addresses apart from the 192.168.0.0/24 subnet use the followin :ref:`metadata-also-notify` domain metadata to avoid this potential bottleneck. .. note:: - If your slaves support Internet Protocol version, which your master does not, + If your slaves support an Internet Protocol version, which your master does not, then set ``only-notify`` to include only supported protocol version. Otherwise there will be error trying to resolve address. @@ -1369,7 +1369,7 @@ Turn on slave support. See :ref:`slave-operation`. - Integer - 60 -On a master, this is the amounts of seconds between the master checking +On a master, this is the amount of seconds between the master checking the SOA serials in its database to determine to send out NOTIFYs to the slaves. On slaves, this is the number of seconds between the slave checking for updates to zones. From 78dd03ecd04bd2ff627ae6d95b316d8478ebd1cb Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 072/127] docs: Add '(or zone transfer)' in sentence on BIND backend If zones would never be reloaded without a regular non-AXFR DNS request, the BIND backend would not be usable in a hidden master setup for example. Als remove superfluous period after link to setting. --- docs/backends/bind.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/backends/bind.rst b/docs/backends/bind.rst index 4ee4cb13a26f..277b293d658c 100644 --- a/docs/backends/bind.rst +++ b/docs/backends/bind.rst @@ -96,8 +96,8 @@ available for serving, as they are parsed. So a ``named.conf`` with zones will already be available. While a domain is being loaded, it is not yet available, to prevent incomplete answers. -Reloading is currently done only when a request for a zone comes in, and -then only after :ref:`setting-bind-check-interval`. +Reloading is currently done only when a request (or zone transfer) for a +zone comes in, and then only after :ref:`setting-bind-check-interval` seconds have passed after the last check. If a change occurred, access to the zone is disabled, the file is reloaded, access is restored, and the question is answered. For regular zones, reloading is fast enough to From 3948ea81b9f877610b7cd6b5eebed03ee544b46b Mon Sep 17 00:00:00 2001 From: Gert van Dijk Date: Sun, 31 Mar 2019 19:25:22 +0200 Subject: [PATCH 073/127] docs: BIND backend - improve formatting of output status Before this change it was hard to distinguish the three possible statuses due to the styling of the inline-monospaced text without contrast in the background for the current Sphinx theme. --- docs/backends/bind.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/backends/bind.rst b/docs/backends/bind.rst index 277b293d658c..4a18f255f33b 100644 --- a/docs/backends/bind.rst +++ b/docs/backends/bind.rst @@ -122,9 +122,11 @@ will be loaded at first request. ``bind-domain-status [domain]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Output status of domain or domains. Can be one of -``seen in named.conf, not parsed``, ``parsed successfully at