Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP ZONEVERSION (RFC9660) support #14818

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,8 @@ common_sources += files(
src_dir / 'ednsoptions.hh',
src_dir / 'ednssubnet.cc',
src_dir / 'ednssubnet.hh',
src_dir / 'ednszoneversion.cc',
src_dir / 'ednszoneversion.hh',
src_dir / 'gss_context.cc',
src_dir / 'gss_context.hh',
src_dir / 'histogram.hh',
Expand Down
1 change: 1 addition & 0 deletions modules/remotebackend/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ libtestremotebackend_la_SOURCES = \
../../pdns/ednscookies.cc \
../../pdns/ednsoptions.cc ../../pdns/ednsoptions.hh \
../../pdns/ednssubnet.cc \
../../pdns/ednszoneversion.cc \
../../pdns/gss_context.cc ../../pdns/gss_context.hh \
../../pdns/iputils.cc \
../../pdns/json.hh ../../pdns/json.cc \
Expand Down
5 changes: 5 additions & 0 deletions pdns/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ pdns_server_SOURCES = \
ednscookies.cc ednscookies.hh \
ednsoptions.cc ednsoptions.hh \
ednssubnet.cc ednssubnet.hh \
ednszoneversion.cc ednszoneversion.hh \
gettime.cc gettime.hh \
gss_context.cc gss_context.hh \
histogram.hh \
Expand Down Expand Up @@ -370,6 +371,7 @@ pdnsutil_SOURCES = \
ednscookies.cc ednscookies.hh \
ednsoptions.cc ednsoptions.hh \
ednssubnet.cc \
ednszoneversion.cc ednszoneversion.hh \
gettime.cc gettime.hh \
gss_context.cc gss_context.hh \
ipcipher.cc ipcipher.hh \
Expand Down Expand Up @@ -556,6 +558,7 @@ sdig_SOURCES = \
dolog.hh \
ednsextendederror.cc ednsextendederror.hh \
ednssubnet.cc iputils.cc \
ednszoneversion.cc ednszoneversion.hh \
libssl.cc libssl.hh \
logger.cc \
misc.cc misc.hh \
Expand Down Expand Up @@ -737,6 +740,7 @@ ixfrdist_SOURCES = \
ednsextendederror.cc ednsextendederror.hh \
ednsoptions.cc ednsoptions.hh \
ednssubnet.cc ednssubnet.hh \
ednszoneversion.cc ednszoneversion.hh \
gss_context.cc gss_context.hh \
iputils.hh iputils.cc \
ixfr.cc ixfr.hh \
Expand Down Expand Up @@ -1370,6 +1374,7 @@ testrunner_SOURCES = \
ednscookies.cc ednscookies.hh \
ednsoptions.cc ednsoptions.hh \
ednssubnet.cc \
ednszoneversion.cc ednszoneversion.hh \
gettime.cc gettime.hh \
gss_context.cc gss_context.hh \
histogram.hh \
Expand Down
30 changes: 30 additions & 0 deletions pdns/dnspacket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
#include "ednszoneversion.hh"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Expand Down Expand Up @@ -334,6 +335,10 @@
}
}

if (! d_auth_serials.empty()) {
optsize += 2 * (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + EDNSZoneVersion::EDNSZoneVersionOptSize);
}

if (d_trc.d_algoName.countLabels())
{
// TSIG is not OPT, but we count it in optsize anyway
Expand Down Expand Up @@ -383,6 +388,21 @@
opts.emplace_back(EDNSOptionCode::COOKIE, d_eco.makeOptString());
}

for (auto &auth_serial : d_auth_serials) {
const auto& name = auth_serial.first;
auto& unedited_serial = auth_serial.second.first;
auto& edited_serial = auth_serial.second.second;

uint8_t labelcount = name.countLabels();
EDNSZoneVersion unedited = {labelcount, 0 /* FIXME enum */, unedited_serial};

Check notice

Code scanning / CodeQL

FIXME comment Note

FIXME comment: enum
EDNSZoneVersion edited = {labelcount, 246 /* FIXME enum and wrong number */, edited_serial};

Check notice

Code scanning / CodeQL

FIXME comment Note

FIXME comment: enum and wrong number

string opt = makeEDNSZoneVersionString(unedited);
opts.emplace_back(EDNSOptionCode::ZONEVERSION, opt);

opt = makeEDNSZoneVersionString(edited);
opts.emplace_back(EDNSOptionCode::ZONEVERSION, opt);
}
if(!opts.empty() || d_haveednssection || d_dnssecOk)
{
pw.addOpt(s_udpTruncationThreshold, d_ednsrcode, d_dnssecOk ? EDNSOpts::DNSSECOK : 0, opts);
Expand Down Expand Up @@ -447,6 +467,7 @@
r->d_eso = d_eso;
r->d_eco = d_eco;
r->d_haveednssubnet = d_haveednssubnet;
r->d_wantszoneversion = d_wantszoneversion;
r->d_haveednssection = d_haveednssection;
r->d_haveednscookie = d_haveednscookie;
r->d_ednsversion = 0;
Expand Down Expand Up @@ -600,6 +621,7 @@
d_haveednssection = false;
d_haveednscookie = false;
d_ednscookievalid = false;
d_wantszoneversion = false;

if(getEDNSOpts(mdp, &edo)) {
d_haveednssection=true;
Expand Down Expand Up @@ -627,6 +649,9 @@
d_eco.makeFromString(option.second);
d_ednscookievalid = d_eco.isValid(s_EDNSCookieKey, d_remote);
}
else if (option.first == EDNSOptionCode::ZONEVERSION) {
d_wantszoneversion = true;
}
else {
// cerr<<"Have an option #"<<iter->first<<": "<<makeHexDump(iter->second)<<endl;
}
Expand Down Expand Up @@ -689,6 +714,11 @@
return d_haveednssubnet;
}

bool DNSPacket::wantsEDNSZoneVersion() const
{
return d_wantszoneversion;
}

bool DNSPacket::hasEDNS() const
{
return d_haveednssection;
Expand Down
4 changes: 4 additions & 0 deletions pdns/dnspacket.hh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "ednssubnet.hh"
#include "ednscookies.hh"
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include <sys/socket.h>
#include <netinet/in.h>
Expand Down Expand Up @@ -121,6 +122,7 @@ public:

bool couldBeCached() const; //!< returns 0 if this query should bypass the packet cache
bool hasEDNSSubnet() const;
bool wantsEDNSZoneVersion() const;
bool hasEDNS() const;
bool hasEDNSCookie() const;
bool hasWellFormedEDNSCookie() const;
Expand Down Expand Up @@ -172,6 +174,7 @@ public:
static bool s_doEDNSCookieProcessing;
static string s_EDNSCookieKey;
EDNSSubnetOpts d_eso;
std::unordered_map<DNSName, std::pair<uint32_t, uint32_t> > d_auth_serials; // unedited, edited

#ifdef ENABLE_GSS_TSIG
void cleanupGSS(int rcode);
Expand Down Expand Up @@ -201,6 +204,7 @@ private:
bool d_tsigtimersonly{false};
bool d_wantsnsid{false};
bool d_haveednssubnet{false};
bool d_wantszoneversion{false};
bool d_haveednscookie{false};
bool d_ednscookievalid{false};
bool d_haveednssection{false};
Expand Down
2 changes: 1 addition & 1 deletion pdns/ednsoptions.hh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

struct EDNSOptionCode
{
enum EDNSOptionCodeEnum {NSID=3, DAU=5, DHU=6, N3U=7, ECS=8, EXPIRE=9, COOKIE=10, TCPKEEPALIVE=11, PADDING=12, CHAIN=13, KEYTAG=14, EXTENDEDERROR=15};
enum EDNSOptionCodeEnum {NSID=3, DAU=5, DHU=6, N3U=7, ECS=8, EXPIRE=9, COOKIE=10, TCPKEEPALIVE=11, PADDING=12, CHAIN=13, KEYTAG=14, EXTENDEDERROR=15, ZONEVERSION=19};
};

/* extract the position (relative to the optRR pointer!) and size of a specific EDNS0 option from a pointer on the beginning rdLen of the OPT RR */
Expand Down
78 changes: 78 additions & 0 deletions pdns/ednszoneversion.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* This file is part of PowerDNS or dnsdist.
* Copyright -- PowerDNS.COM B.V. and its contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* In addition, for the avoidance of any doubt, permission is granted to
* link this program with OpenSSL and to (re)distribute the binaries
* produced as the result of such linking.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ednszoneversion.hh"
#include "dns.hh"

namespace
{
struct EDNSZoneVersionWire
{
uint8_t labelcount;
uint8_t type;
char version[256]; // FIXME they can be bigger

Check notice

Code scanning / CodeQL

FIXME comment Note

FIXME comment: they can be bigger
} GCCPACKATTRIBUTE; // BRRRRR

}

bool getEDNSZoneVersionFromString(const string& options, EDNSZoneVersion& zoneversion)
{
// cerr<<"options.size:"<<options.size()<<endl;

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.
return getEDNSZoneVersionFromString(options.c_str(), options.length(), zoneversion);
}

bool getEDNSZoneVersionFromString(const char* options, unsigned int len, EDNSZoneVersion& zoneversion)
{
EDNSZoneVersionWire zoneversionw{};
// static_assert(sizeof(zoneversionw) == 4, "sizeof(EDNSSubnetOptsWire) must be 4 bytes");

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.
if (len > sizeof(zoneversionw)) {
return false; // FIXME this silently breaks on >256 bytes of version

Check notice

Code scanning / CodeQL

FIXME comment Note

FIXME comment: this silently breaks on >256 bytes of version
}
if (len < (1 + 1 + 4)) {
return false; // does not contain labelcount + type + uint32_t version
}
memcpy(&zoneversionw, options, len);
zoneversion.labelcount = zoneversionw.labelcount;
zoneversion.type = zoneversionw.type;

memcpy(&zoneversion.version, zoneversionw.version, sizeof(zoneversion.version));
zoneversion.version = ntohl(zoneversion.version);

return true;
}

string makeEDNSZoneVersionString(const EDNSZoneVersion& zoneversion)
{
string ret;
EDNSZoneVersionWire zoneversionw{};
zoneversionw.labelcount = zoneversion.labelcount;
zoneversionw.type = zoneversion.type;

uint32_t version = htonl(zoneversion.version);
memcpy(&zoneversionw.version, &version, sizeof(zoneversion.version));

ret.assign((const char*)&zoneversionw, 1 + 1 + 4);

return ret;
}
38 changes: 38 additions & 0 deletions pdns/ednszoneversion.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* This file is part of PowerDNS or dnsdist.
* Copyright -- PowerDNS.COM B.V. and its contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* In addition, for the avoidance of any doubt, permission is granted to
* link this program with OpenSSL and to (re)distribute the binaries
* produced as the result of such linking.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 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 "namespaces.hh"
#include "iputils.hh"
#include "dnsname.hh"

struct EDNSZoneVersion
{
static const size_t EDNSZoneVersionOptSize = 1 + 1 + 4; // used for upper bound size calculation in dnspacket, assumes versions are 4 bytes

uint8_t labelcount;
uint8_t type;
uint32_t version; // this assumes all versions fit in uint32_t. RFC9660 does not promise that.
};

bool getEDNSZoneVersionFromString(const string& options, EDNSZoneVersion& zoneversion);
bool getEDNSZoneVersionFromString(const char* options, unsigned int len, EDNSZoneVersion& zoneversion);
string makeEDNSZoneVersionString(const EDNSZoneVersion& zoneversion);
6 changes: 6 additions & 0 deletions pdns/packethandler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,12 @@ std::unique_ptr<DNSPacket> PacketHandler::doQuestion(DNSPacket& p)
goto sendit;
}
DLOG(g_log<<Logger::Error<<"We have authority, zone='"<<d_sd.qname<<"', id="<<d_sd.domain_id<<endl);
g_log<<Logger::Error<<"We have authority, zone='"<<d_sd.qname<<"', id="<<d_sd.domain_id<<", serial="<<d_sd.serial<<endl;
if (r->wantsEDNSZoneVersion()) {
auto edited_serial = calculateEditSOA(d_sd.serial, d_dk, d_sd.qname);

r->d_auth_serials[d_sd.qname] = {edited_serial, d_sd.serial};
}

authSet.insert(d_sd.qname);
d_dnssec=(p.d_dnssecOk && d_dk.isSecuredZone(d_sd.qname));
Expand Down
Loading