Skip to content

Commit

Permalink
Rework the QType class
Browse files Browse the repository at this point in the history
This adds QType::fromString() which does error checking and throws exceptions when invalid
input is provided.

Cleans up the anti-pattern that was previously there where you could only construct a
QType object from a string by assigning to a dummy one. Also removes access to internals
like chartocode which was being used all around in place of that anti-pattern.
  • Loading branch information
fredmorcos committed Jul 3, 2024
1 parent 53bcb9f commit 02dac74
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 106 deletions.
96 changes: 49 additions & 47 deletions pdns/qtype.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "dns.hh"
#include <iostream>

#include <cstdint>
#include <stdexcept>
#include <string>
#include <vector>
#include <utility>
#include <sstream>
#include "qtype.hh"
#include "misc.hh"

Expand Down Expand Up @@ -100,73 +99,76 @@ const map<const string, uint16_t> QType::names = {
#endif
};

static map<uint16_t, const string> swapElements(const map<const string, uint16_t>& names) {
map<uint16_t, const string> ret;

for (const auto& n : names) {
ret.emplace(n.second, n.first);
}
return ret;
}

const map<uint16_t, const string> QType::numbers = swapElements(names);


bool QType::isSupportedType() const
static auto parsingError(const string& name) -> std::runtime_error
{
return numbers.count(code) == 1;
string fullErrorMessage{"Unknown or invalid record type `"};
fullErrorMessage += name;
fullErrorMessage += "`";
return std::runtime_error{fullErrorMessage};
}

bool QType::isMetadataType() const
static auto invalidEmptyRecordType() -> std::runtime_error
{
// rfc6895 section 3.1, note ANY is 255 and falls outside the range
if (code == QType::OPT || (code >= rfc6895MetaLowerBound && code <= rfc6895MetaUpperBound)) {
return true;
}
return false;
string fullErrorMessage{"Invalid empty record type"};
return std::runtime_error{fullErrorMessage};
}

const string QType::toString() const
auto QType::fromString(const string& name) -> QType
{
const auto& name = numbers.find(code);
if (name != numbers.cend()) {
return name->second;
if (name.empty()) {
throw invalidEmptyRecordType();
}
return "TYPE" + std::to_string(code);
}

uint16_t QType::chartocode(const char *p)
{
string P = toUpper(p);
const auto& upperName = toUpper(name);

const auto& num = names.find(P);
const auto& num = names.find(upperName);
if (num != names.cend()) {
return num->second;
}
if (*p == '#') {
return static_cast<uint16_t>(atoi(p + 1));

if (name.at(0) == '#') {
return pdns::checked_stoi<uint16_t>(&name[1]);
}

if (boost::starts_with(P, "TYPE")) {
return static_cast<uint16_t>(atoi(p + 4));
if (boost::starts_with(upperName, "TYPE")) {
return pdns::checked_stoi<uint16_t>(&name[4]);
}

return 0;
throw parsingError(name);
}

string QType::toString() const
{
const auto& name = numbers.find(qtype);
if (name != numbers.cend()) {
return name->second;
}
return "TYPE" + std::to_string(qtype);
}

static map<uint16_t, const string> swapElements(const map<const string, uint16_t>& names)
{
map<uint16_t, const string> ret;
for (const auto& name : names) {
ret.emplace(name.second, name.first);
}
return ret;
}

QType &QType::operator=(const char *p)
const map<uint16_t, const string> QType::numbers = swapElements(names);

bool QType::isSupportedType() const
{
code = chartocode(p);
return *this;
return numbers.count(qtype) == 1;
}

QType &QType::operator=(const string &s)
bool QType::isMetadataType() const
{
code = chartocode(s.c_str());
return *this;
// rfc6895 section 3.1, note ANY is 255 and falls outside the range
return qtype == QType::OPT || (qtype >= rfc6895MetaLowerBound && qtype <= rfc6895MetaUpperBound);
}

const std::string QClass::toString() const
std::string QClass::toString() const
{
switch (qclass) {
case IN:
Expand All @@ -177,7 +179,7 @@ const std::string QClass::toString() const
return "NONE";
case ANY:
return "ANY";
default :
default:
return "CLASS" + std::to_string(qclass);
}
}
132 changes: 73 additions & 59 deletions pdns/qtype.hh
Original file line number Diff line number Diff line change
Expand Up @@ -20,41 +20,47 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "namespaces.hh"
#include <cstdint>

/** The QType class is meant to deal easily with the different kind of resource types, like 'A', 'NS',
* 'CNAME' etcetera. These types have both a name and a number. This class can seamlessly move between
* them. Use it like this:
/**
* The QType class is meant to be used to easily deal with the different kinds of resource
* types like 'A', 'NS' and 'CNAME'. These types have both a name and a number. This class
* can seamlessly convert between them. Use it like this:
\code
QType t;
t="CNAME";
cout<<t.getCode()<<endl; // prints '5'
t=6;
cout<<t.toString()<<endl; // prints 'SOA'
QType qtype = QType::fromString("CNAME");
cout << qtype.getCode() << endl; // prints '5'
qtype = QType(6);
cout << qtype.toString() << endl; // prints 'SOA'
\endcode
*/

class QType
{
public:
QType(uint16_t qtype = 0) : code(qtype) {}
QType &operator=(const char *);
QType &operator=(const string &);
constexpr QType(uint16_t code = 0) :
qtype(code) {}

static auto fromString(const string&) -> QType;

operator uint16_t() const {
return code;
operator uint16_t() const
{
return qtype;
}

const string toString() const;
uint16_t getCode() const
auto operator=(std::string& value) -> QType&
{
return code;
*this = QType::fromString(value);
return *this;
}

[[nodiscard]] string toString() const;

[[nodiscard]] uint16_t getCode() const
{
return qtype;
}

/**
Expand All @@ -63,45 +69,45 @@ public:
* This does not presume that we have an implemented a content representation for this type,
* for that please see DNSRecordContent::isRegisteredType().
*/
bool isSupportedType() const;
[[nodiscard]] bool isSupportedType() const;

/**
* \brief Whether the type is either a QTYPE or Meta-Type as defined by rfc6895 section 3.1.
*
* Note that ANY is 255 and falls outside the range.
*/
bool isMetadataType() const;
[[nodiscard]] bool isMetadataType() const;

static uint16_t chartocode(const char* p);

enum typeenum : uint16_t {
enum QTypeEnum : uint16_t
{
ENT = 0,
A = 1,
NS = 2,
A = 1, // NOLINT(readability-identifier-length)
NS = 2, // NOLINT(readability-identifier-length)
CNAME = 5,
SOA = 6,
MB = 7,
MG = 8,
MR = 9,
MB = 7, // NOLINT(readability-identifier-length)
MG = 8, // NOLINT(readability-identifier-length)
MR = 9, // NOLINT(readability-identifier-length)
PTR = 12,
HINFO = 13,
MINFO = 14,
MX = 15,
MX = 15, // NOLINT(readability-identifier-length)
TXT = 16,
RP = 17,
RP = 17, // NOLINT(readability-identifier-length)
AFSDB = 18,
SIG = 24,
KEY = 25,
AAAA = 28,
LOC = 29,
SRV = 33,
NAPTR = 35,
KX = 36,
KX = 36, // NOLINT(readability-identifier-length)
CERT = 37,
A6 = 38,
A6 = 38, // NOLINT(readability-identifier-length)
DNAME = 39,
OPT = 41,
APL = 42,
DS = 43,
DS = 43, // NOLINT(readability-identifier-length)
SSHFP = 44,
IPSECKEY = 45,
RRSIG = 46,
Expand All @@ -124,7 +130,7 @@ public:
NID = 104,
L32 = 105,
L64 = 106,
LP = 107,
LP = 107, // NOLINT(readability-identifier-length)
EUI48 = 108,
EUI64 = 109,
TKEY = 249,
Expand All @@ -140,29 +146,32 @@ public:
ADDR = 65400,
#if !defined(RECURSOR)
ALIAS = 65401,
LUA = 65402
LUA = 65402,
#endif
};

const static uint16_t rfc6895MetaLowerBound = 128;
const static uint16_t rfc6895MetaUpperBound = 254; // Note 255: ANY is not included
const static uint16_t rfc6895Reserved = 65535;
static const uint16_t rfc6895MetaLowerBound = 128;
static const uint16_t rfc6895MetaUpperBound = 254; // Note 255: ANY is not included
static const uint16_t rfc6895Reserved = 65535;

const static map<const string, uint16_t> names;
const static map<uint16_t, const string> numbers;
static const map<const string, uint16_t> names;
static const map<uint16_t, const string> numbers;

private:

uint16_t code;
uint16_t qtype;
};

// Define hash function on QType. See https://en.cppreference.com/w/cpp/utility/hash
namespace std {
template<> struct hash<QType> {
std::size_t operator()(QType qtype) const noexcept {
return std::hash<uint16_t>{}(qtype.getCode());
}
};
namespace std
{
template <>
struct hash<QType>
{
std::size_t operator()(QType qtype) const noexcept
{
return std::hash<uint16_t>{}(qtype.getCode());
}
};
}

inline std::ostream& operator<<(std::ostream& stream, const QType& qtype)
Expand All @@ -171,24 +180,29 @@ inline std::ostream& operator<<(std::ostream& stream, const QType& qtype)
}

// Used by e.g. boost multi-index
inline size_t hash_value(const QType qtype) {
inline size_t hash_value(const QType qtype)
{
return qtype.getCode();
}

struct QClass
{
constexpr QClass(uint16_t code = 0) : qclass(code) {}
constexpr QClass(uint16_t code = 0) :
qclass(code) {}

constexpr operator uint16_t() const {
constexpr operator uint16_t() const
{
return qclass;
}
constexpr uint16_t getCode() const

[[nodiscard]] constexpr uint16_t getCode() const
{
return qclass;
}
const std::string toString() const;

static const QClass IN;
[[nodiscard]] std::string toString() const;

static const QClass IN; // NOLINT(readability-identifier-length)
static const QClass CHAOS;
static const QClass NONE;
static const QClass ANY;
Expand All @@ -197,12 +211,12 @@ private:
uint16_t qclass;
};

constexpr QClass QClass::IN(1);
constexpr QClass QClass::IN(1); // NOLINT(readability-identifier-length)
constexpr QClass QClass::CHAOS(3);
constexpr QClass QClass::NONE(254);
constexpr QClass QClass::ANY(255);

inline std::ostream& operator<<(std::ostream& s, QClass qclass)
inline std::ostream& operator<<(std::ostream& stream, QClass qclass)
{
return s << qclass.toString();
return stream << qclass.toString();
}

0 comments on commit 02dac74

Please sign in to comment.