From f43646f5156f93f710671fbaeadaee4102dfb02d Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Fri, 7 Jun 2019 16:29:37 +0200 Subject: [PATCH] auth API, pdnsutil: improve backend transaction correctness --- pdns/backends/gsql/gsqlbackend.cc | 11 +++++++++++ pdns/pdnsutil.cc | 19 ++++++++++++++----- pdns/ws-auth.cc | 14 ++++++++------ 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index 7dc46b588..0f13175cb 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -1349,6 +1349,10 @@ bool GSQLBackend::replaceRRSet(uint32_t domain_id, const DNSName& qname, const Q try { reconnectIfNeeded(); + if (!d_inTransaction) { + throw PDNSException("replaceRRSet called outside of transaction"); + } + if (qt != QType::ANY) { d_DeleteRRSetQuery_stmt-> bind("domain_id", domain_id)-> @@ -1495,6 +1499,9 @@ bool GSQLBackend::startTransaction(const DNSName &domain, int domain_id) try { reconnectIfNeeded(); + if (inTransaction()) { + throw PDNSException("Attempted to start transaction while one was already active (domain '" + domain.toLogString() + "')"); + } d_db->startTransaction(); d_inTransaction = true; if(domain_id >= 0) { @@ -1611,6 +1618,10 @@ bool GSQLBackend::replaceComments(const uint32_t domain_id, const DNSName& qname try { reconnectIfNeeded(); + if (!d_inTransaction) { + throw PDNSException("replaceComments called outside of transaction"); + } + d_DeleteCommentRRsetQuery_stmt-> bind("domain_id",domain_id)-> bind("qname", qname)-> diff --git a/pdns/pdnsutil.cc b/pdns/pdnsutil.cc index a1b0995a5..b2045b9d4 100644 --- a/pdns/pdnsutil.cc +++ b/pdns/pdnsutil.cc @@ -157,11 +157,11 @@ void loadMainConfig(const std::string& configdir) UeberBackend::go(); } -bool rectifyZone(DNSSECKeeper& dk, const DNSName& zone, bool quiet = false) +bool rectifyZone(DNSSECKeeper& dk, const DNSName& zone, bool quiet = false, bool rectifyTransaction = true) { string output; string error; - bool ret = dk.rectifyZone(zone, error, output, true); + bool ret = dk.rectifyZone(zone, error, output, rectifyTransaction); if (!quiet || !ret) { // When quiet, only print output if there was an error if (!output.empty()) { @@ -859,9 +859,10 @@ int clearZone(DNSSECKeeper& dk, const DNSName &zone) { return EXIT_SUCCESS; } -int editZone(DNSSECKeeper& dk, const DNSName &zone) { +int editZone(const DNSName &zone) { UeberBackend B; DomainInfo di; + DNSSECKeeper dk(&B); if (! B.getDomainInfo(zone, di)) { cerr<<"Domain '"<startTransaction(zone, -1); for(const auto& change : changed) { vector vrr; for(const DNSRecord& rr : grouped[change.first]) { @@ -1013,7 +1015,8 @@ int editZone(DNSSECKeeper& dk, const DNSName &zone) { } di.backend->replaceRRSet(di.id, change.first.first, QType(change.first.second), vrr); } - rectifyZone(dk, zone); + rectifyZone(dk, zone, false, false); + di.backend->commitTransaction(); return EXIT_SUCCESS; } @@ -1190,6 +1193,9 @@ int addOrReplaceRecord(bool addOrReplace, const vector& cmds) { rr.domain_id = di.id; rr.qname = name; DNSResourceRecord oldrr; + + di.backend->startTransaction(zone, -1); + if(addOrReplace) { // the 'add' case di.backend->lookup(rr.qtype, rr.qname, 0, di.id); @@ -1245,6 +1251,7 @@ int addOrReplaceRecord(bool addOrReplace, const vector& cmds) { di.backend->replaceRRSet(di.id, name, rr.qtype, newrrs); // need to be explicit to bypass the ueberbackend cache! di.backend->lookup(rr.qtype, name, 0, di.id); + di.backend->commitTransaction(); cout<<"New rrset:"<get(rr)) { cout<startTransaction(zone, -1); di.backend->replaceRRSet(di.id, name, qt, vector()); + di.backend->commitTransaction(); return EXIT_SUCCESS; } @@ -2388,7 +2397,7 @@ try if(cmds[1]==".") cmds[1].clear(); - exit(editZone(dk, DNSName(cmds[1]))); + exit(editZone(DNSName(cmds[1]))); } else if(cmds[0] == "clear-zone") { if(cmds.size() != 2) { diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index 349876b74..61d9cb538 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -618,7 +618,7 @@ static void throwUnableToSecure(const DNSName& zonename) { + "capable backends are loaded, or because the backends have DNSSEC disabled. Check your configuration."); } -static void updateDomainSettingsFromDocument(UeberBackend& B, const DomainInfo& di, const DNSName& zonename, const Json document) { +static void updateDomainSettingsFromDocument(UeberBackend& B, const DomainInfo& di, const DNSName& zonename, const Json document, bool rectifyTransaction=true) { vector zonemaster; bool shouldRectify = false; for(auto value : document["masters"].array_items()) { @@ -743,7 +743,7 @@ static void updateDomainSettingsFromDocument(UeberBackend& B, const DomainInfo& if (api_rectify == "1") { string info; string error_msg; - if (!dk.rectifyZone(zonename, error_msg, info, true)) { + if (!dk.rectifyZone(zonename, error_msg, info, rectifyTransaction)) { throw ApiException("Failed to rectify '" + zonename.toString() + "' " + error_msg); } } @@ -1642,13 +1642,13 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) { if(!B.getDomainInfo(zonename, di)) throw ApiException("Creating domain '"+zonename.toString()+"' failed: lookup of domain ID failed"); + di.backend->startTransaction(zonename, di.id); + // updateDomainSettingsFromDocument does NOT fill out the default we've established above. if (!soa_edit_api_kind.empty()) { di.backend->setDomainMetadataOne(zonename, "SOA-EDIT-API", soa_edit_api_kind); } - di.backend->startTransaction(zonename, di.id); - for(auto rr : new_records) { rr.domain_id = di.id; di.backend->feedRecord(rr, DNSName()); @@ -1658,7 +1658,7 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) { di.backend->feedComment(c); } - updateDomainSettingsFromDocument(B, di, zonename, document); + updateDomainSettingsFromDocument(B, di, zonename, document, false); di.backend->commitTransaction(); @@ -1714,7 +1714,9 @@ static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp) { if(req->method == "PUT") { // update domain settings - updateDomainSettingsFromDocument(B, di, zonename, req->json()); + di.backend->startTransaction(zonename, -1); + updateDomainSettingsFromDocument(B, di, zonename, req->json(), false); + di.backend->commitTransaction(); resp->body = ""; resp->status = 204; // No Content, but indicate success -- 2.40.0