return ENOENT;
}
+namespace dnsdist {
/* extract the start of the OPT RR in a QUERY packet if any */
-int getEDNSOptionsStart(const PacketBuffer& packet, const size_t offset, uint16_t* optRDPosition, size_t* remaining)
+int getEDNSOptionsStart(const PacketBuffer& packet, const size_t qnameWireLength, uint16_t* optRDPosition, size_t* remaining)
{
if (optRDPosition == nullptr || remaining == nullptr) {
throw std::runtime_error("Invalid values passed to getEDNSOptionsStart");
const dnsheader_aligned dnsHeader(packet.data());
- if (offset >= packet.size()) {
+ if (qnameWireLength >= packet.size()) {
return ENOENT;
}
return ENOENT;
}
- size_t pos = sizeof(dnsheader) + offset;
+ size_t pos = sizeof(dnsheader) + qnameWireLength;
pos += DNS_TYPE_SIZE + DNS_CLASS_SIZE;
if (pos >= packet.size()) {
return 0;
}
+}
void generateECSOption(const ComboAddress& source, string& res, uint16_t ECSPrefixLength)
{
size_t remaining = 0;
uint16_t optRDPosition{};
- int res = getEDNSOptionsStart(dnsQuestion.getData(), dnsQuestion.ids.qname.wirelength(), &optRDPosition, &remaining);
+ int res = dnsdist::getEDNSOptionsStart(dnsQuestion.getData(), dnsQuestion.ids.qname.wirelength(), &optRDPosition, &remaining);
if (res == 0) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
uint16_t optRDPosition = 0;
size_t remaining = 0;
- int res = getEDNSOptionsStart(packet, qnameWireLength, &optRDPosition, &remaining);
+ int res = dnsdist::getEDNSOptionsStart(packet, qnameWireLength, &optRDPosition, &remaining);
if (res != 0) {
/* no EDNS but there might be another record in additional (TSIG?) */
size_t remaining = 0;
auto& packet = dnsQuestion.getMutableData();
- int res = getEDNSOptionsStart(packet, dnsQuestion.ids.qname.wirelength(), &optRDPosition, &remaining);
+ int res = dnsdist::getEDNSOptionsStart(packet, dnsQuestion.ids.qname.wirelength(), &optRDPosition, &remaining);
if (res != 0) {
/* if the initial query did not have EDNS0, we are done */
return true;
}
+namespace dnsdist {
+static std::optional<size_t> getEDNSRecordPosition(const DNSQuestion& dnsQuestion)
+{
+ try {
+ const auto& packet = dnsQuestion.getData();
+ if (packet.size() <= sizeof(dnsheader)) {
+ return std::nullopt;
+ }
+
+ uint16_t optRDPosition = 0;
+ size_t remaining = 0;
+ auto res = getEDNSOptionsStart(packet, dnsQuestion.ids.qname.wirelength(), &optRDPosition, &remaining);
+ if (res != 0) {
+ return std::nullopt;
+ }
+
+ if (optRDPosition < DNS_TTL_SIZE) {
+ return std::nullopt;
+ }
+
+ return optRDPosition - DNS_TTL_SIZE;
+ }
+ catch (...) {
+ return std::nullopt;
+ }
+}
+
// goal in life - if you send us a reasonably normal packet, we'll get Z for you, otherwise 0
int getEDNSZ(const DNSQuestion& dnsQuestion)
{
try {
- const auto& dnsHeader = dnsQuestion.getHeader();
- if (ntohs(dnsHeader->qdcount) != 1 || dnsHeader->ancount != 0 || ntohs(dnsHeader->arcount) != 1 || dnsHeader->nscount != 0) {
+ auto position = getEDNSRecordPosition(dnsQuestion);
+
+ if (!position) {
return 0;
}
- if (dnsQuestion.getData().size() <= sizeof(dnsheader)) {
+ const auto& packet = dnsQuestion.getData();
+ if ((*position + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE + 1) >= packet.size()) {
return 0;
}
- size_t pos = sizeof(dnsheader) + dnsQuestion.ids.qname.wirelength() + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
+ return 0x100 * packet.at(*position + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE) + packet.at(*position + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE + 1);
+ }
+ catch (...) {
+ return 0;
+ }
+}
+
+std::optional<uint8_t> getEDNSVersion(const DNSQuestion& dnsQuestion)
+{
+ try {
+ auto position = getEDNSRecordPosition(dnsQuestion);
- if (dnsQuestion.getData().size() <= (pos + /* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE)) {
- return 0;
+ if (!position) {
+ return std::nullopt;
}
const auto& packet = dnsQuestion.getData();
- if (packet.at(pos) != 0) {
- /* not root, so not a valid OPT record */
- return 0;
+ if ((*position + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE) >= packet.size()) {
+ return std::nullopt;
}
- pos++;
+ return packet.at(*position + EDNS_EXTENDED_RCODE_SIZE);
+ }
+ catch (...) {
+ return std::nullopt;
+ }
+}
+
+std::optional<uint8_t> getEDNSExtendedRCode(const DNSQuestion& dnsQuestion)
+{
+ try {
+ auto position = getEDNSRecordPosition(dnsQuestion);
- uint16_t qtype = packet.at(pos) * 256 + packet.at(pos + 1);
- pos += DNS_TYPE_SIZE;
- pos += DNS_CLASS_SIZE;
+ if (!position) {
+ return std::nullopt;
+ }
- if (qtype != QType::OPT || (pos + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE + 1) >= packet.size()) {
- return 0;
+ const auto& packet = dnsQuestion.getData();
+ if ((*position + EDNS_EXTENDED_RCODE_SIZE) >= packet.size()) {
+ return std::nullopt;
}
- return 0x100 * packet.at(pos + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE) + packet.at(pos + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE + 1);
+ return packet.at(*position);
}
catch (...) {
- return 0;
+ return std::nullopt;
}
}
+}
+
bool queryHasEDNS(const DNSQuestion& dnsQuestion)
{
uint16_t optRDPosition = 0;
size_t ecsRemaining = 0;
- int res = getEDNSOptionsStart(dnsQuestion.getData(), dnsQuestion.ids.qname.wirelength(), &optRDPosition, &ecsRemaining);
+ int res = dnsdist::getEDNSOptionsStart(dnsQuestion.getData(), dnsQuestion.ids.qname.wirelength(), &optRDPosition, &ecsRemaining);
return res == 0;
}
auto dnsQuestion = DNSQuestion(ids, query);
- return getEDNSZ(dnsQuestion);
+ return dnsdist::getEDNSZ(dnsQuestion);
}
BOOST_AUTO_TEST_CASE(test_getEDNSZ)
}
}
+BOOST_AUTO_TEST_CASE(test_getEDNSVersion)
+{
+ const DNSName qname("www.powerdns.com.");
+ const uint16_t qtype = QType::A;
+ const uint16_t qclass = QClass::IN;
+ const GenericDNSPacketWriter<PacketBuffer>::optvect_t opts;
+
+ auto getVersion = [&qname, qtype, qclass](PacketBuffer& query) {
+ InternalQueryState ids;
+ ids.protocol = dnsdist::Protocol::DoUDP;
+ ids.qname = qname;
+ ids.qtype = qtype;
+ ids.qclass = qclass;
+ ids.origDest = ComboAddress("127.0.0.1");
+ ids.origRemote = ComboAddress("127.0.0.1");
+ ids.queryRealTime.start();
+
+ auto dnsQuestion = DNSQuestion(ids, query);
+
+ return dnsdist::getEDNSVersion(dnsQuestion);
+ };
+
+ {
+ /* no EDNS */
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(query, qname, qtype, qclass, 0);
+ packetWriter.commit();
+
+ BOOST_CHECK(getVersion(query) == std::nullopt);
+ }
+
+ {
+ /* truncated EDNS */
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(query, qname, qtype, qclass, 0);
+ packetWriter.addOpt(512, 0, EDNS_HEADER_FLAG_DO);
+ packetWriter.commit();
+
+ query.resize(query.size() - (/* RDLEN */ sizeof(uint16_t) + /* TTL */ 2));
+ BOOST_CHECK(getVersion(query) == std::nullopt);
+ }
+
+ {
+ /* valid EDNS, no options */
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(query, qname, qtype, qclass, 0);
+ packetWriter.addOpt(512, 0, 0);
+ packetWriter.commit();
+
+ BOOST_CHECK_EQUAL(*getVersion(query), 0U);
+ }
+
+ {
+ /* EDNS version 255 */
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(query, qname, qtype, qclass, 0);
+ packetWriter.addOpt(512, 0, EDNS_HEADER_FLAG_DO, opts, 255U);
+ packetWriter.commit();
+
+ BOOST_CHECK_EQUAL(*getVersion(query), 255U);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_getEDNSExtendedRCode)
+{
+ const DNSName qname("www.powerdns.com.");
+ const uint16_t qtype = QType::A;
+ const uint16_t qclass = QClass::IN;
+
+ auto getExtendedRCode = [&qname, qtype, qclass](PacketBuffer& query) {
+ InternalQueryState ids;
+ ids.protocol = dnsdist::Protocol::DoUDP;
+ ids.qname = qname;
+ ids.qtype = qtype;
+ ids.qclass = qclass;
+ ids.origDest = ComboAddress("127.0.0.1");
+ ids.origRemote = ComboAddress("127.0.0.1");
+ ids.queryRealTime.start();
+
+ auto dnsQuestion = DNSQuestion(ids, query);
+
+ return dnsdist::getEDNSExtendedRCode(dnsQuestion);
+ };
+
+ {
+ /* no EDNS */
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(query, qname, qtype, qclass, 0);
+ packetWriter.commit();
+
+ BOOST_CHECK(getExtendedRCode(query) == std::nullopt);
+ }
+
+ {
+ /* truncated EDNS */
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(query, qname, qtype, qclass, 0);
+ packetWriter.addOpt(512, 0, EDNS_HEADER_FLAG_DO);
+ packetWriter.commit();
+
+ query.resize(query.size() - (/* RDLEN */ sizeof(uint16_t) + /* TTL */ 2));
+ BOOST_CHECK(getExtendedRCode(query) == std::nullopt);
+ }
+
+ {
+ /* valid EDNS, no options */
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(query, qname, qtype, qclass, 0);
+ packetWriter.addOpt(512, 0, 0);
+ packetWriter.commit();
+
+ BOOST_CHECK_EQUAL(*getExtendedRCode(query), 0U);
+ }
+
+ {
+ /* EDNS extended RCode 4095 (15 for the normal RCode, 255 for the EDNS part) */
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(query, qname, qtype, qclass, 0);
+ packetWriter.addOpt(512, 4095U, EDNS_HEADER_FLAG_DO);
+ packetWriter.commit();
+
+ BOOST_CHECK_EQUAL(*getExtendedRCode(query), 255U);
+ }
+}
+
BOOST_AUTO_TEST_CASE(test_addEDNSToQueryTurnedResponse)
{
InternalQueryState ids;
packetWriter.commit();
auto dnsQuestion = turnIntoResponse(ids, query);
- BOOST_CHECK_EQUAL(getEDNSZ(dnsQuestion), 0);
+ BOOST_CHECK_EQUAL(dnsdist::getEDNSZ(dnsQuestion), 0);
+ BOOST_CHECK(dnsdist::getEDNSVersion(dnsQuestion) == std::nullopt);
+ BOOST_CHECK(dnsdist::getEDNSExtendedRCode(dnsQuestion) == std::nullopt);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dnsQuestion.getData().data()), dnsQuestion.getData().size(), &udpPayloadSize, &zValue), false);
BOOST_CHECK_EQUAL(zValue, 0);
query.resize(query.size() - (/* RDLEN */ sizeof(uint16_t) + /* last byte of TTL / Z */ 1));
auto dnsQuestion = turnIntoResponse(ids, query, false);
- BOOST_CHECK_EQUAL(getEDNSZ(dnsQuestion), 0);
+ BOOST_CHECK_EQUAL(dnsdist::getEDNSZ(dnsQuestion), 0);
+ BOOST_CHECK(dnsdist::getEDNSVersion(dnsQuestion) == std::nullopt);
+ BOOST_CHECK(dnsdist::getEDNSExtendedRCode(dnsQuestion) == std::nullopt);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dnsQuestion.getData().data()), dnsQuestion.getData().size(), &udpPayloadSize, &zValue), false);
BOOST_CHECK_EQUAL(zValue, 0);
packetWriter.commit();
auto dnsQuestion = turnIntoResponse(ids, query);
- BOOST_CHECK_EQUAL(getEDNSZ(dnsQuestion), 0);
+ BOOST_CHECK_EQUAL(dnsdist::getEDNSZ(dnsQuestion), 0);
+ BOOST_CHECK_EQUAL(*dnsdist::getEDNSVersion(dnsQuestion), 0U);
+ BOOST_CHECK_EQUAL(*dnsdist::getEDNSExtendedRCode(dnsQuestion), 0U);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dnsQuestion.getData().data()), dnsQuestion.getData().size(), &udpPayloadSize, &zValue), true);
BOOST_CHECK_EQUAL(zValue, 0);
packetWriter.commit();
auto dnsQuestion = turnIntoResponse(ids, query);
- BOOST_CHECK_EQUAL(getEDNSZ(dnsQuestion), EDNS_HEADER_FLAG_DO);
+ BOOST_CHECK_EQUAL(dnsdist::getEDNSZ(dnsQuestion), EDNS_HEADER_FLAG_DO);
+ BOOST_CHECK_EQUAL(*dnsdist::getEDNSVersion(dnsQuestion), 0U);
+ BOOST_CHECK_EQUAL(*dnsdist::getEDNSExtendedRCode(dnsQuestion), 0U);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dnsQuestion.getData().data()), dnsQuestion.getData().size(), &udpPayloadSize, &zValue), true);
BOOST_CHECK_EQUAL(zValue, EDNS_HEADER_FLAG_DO);
packetWriter.commit();
auto dnsQuestion = turnIntoResponse(ids, query);
- BOOST_CHECK_EQUAL(getEDNSZ(dnsQuestion), 0);
+ BOOST_CHECK_EQUAL(dnsdist::getEDNSZ(dnsQuestion), 0);
+ BOOST_CHECK_EQUAL(*dnsdist::getEDNSVersion(dnsQuestion), 0U);
+ BOOST_CHECK_EQUAL(*dnsdist::getEDNSExtendedRCode(dnsQuestion), 0U);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dnsQuestion.getData().data()), dnsQuestion.getData().size(), &udpPayloadSize, &zValue), true);
BOOST_CHECK_EQUAL(zValue, 0);
packetWriter.commit();
auto dnsQuestion = turnIntoResponse(ids, query);
- BOOST_CHECK_EQUAL(getEDNSZ(dnsQuestion), EDNS_HEADER_FLAG_DO);
+ BOOST_CHECK_EQUAL(dnsdist::getEDNSZ(dnsQuestion), EDNS_HEADER_FLAG_DO);
+ BOOST_CHECK_EQUAL(*dnsdist::getEDNSVersion(dnsQuestion), 0U);
+ BOOST_CHECK_EQUAL(*dnsdist::getEDNSExtendedRCode(dnsQuestion), 0U);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dnsQuestion.getData().data()), dnsQuestion.getData().size(), &udpPayloadSize, &zValue), true);
BOOST_CHECK_EQUAL(zValue, EDNS_HEADER_FLAG_DO);
packetWriter.getHeader()->rcode = RCode::NXDomain;
packetWriter.commit();
- int res = getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
+ int res = dnsdist::getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
BOOST_CHECK_EQUAL(res, ENOENT);
/* truncated packet (should not matter) */
query.resize(query.size() - 1);
- res = getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
+ res = dnsdist::getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
BOOST_CHECK_EQUAL(res, ENOENT);
}
packetWriter.addOpt(512, 0, 0);
packetWriter.commit();
- int res = getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
+ int res = dnsdist::getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
BOOST_CHECK_EQUAL(res, 0);
BOOST_CHECK_EQUAL(optRDPosition, optRDExpectedOffset);
/* truncated packet */
query.resize(query.size() - 1);
- res = getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
+ res = dnsdist::getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
BOOST_CHECK_EQUAL(res, ENOENT);
}
packetWriter.addOpt(512, 0, 0, opts);
packetWriter.commit();
- int res = getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
+ int res = dnsdist::getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
BOOST_CHECK_EQUAL(res, 0);
BOOST_CHECK_EQUAL(optRDPosition, optRDExpectedOffset);
/* truncated options (should not matter for this test) */
query.resize(query.size() - 1);
- res = getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
+ res = dnsdist::getEDNSOptionsStart(query, qname.wirelength(), &optRDPosition, &remaining);
BOOST_CHECK_EQUAL(res, 0);
BOOST_CHECK_EQUAL(optRDPosition, optRDExpectedOffset);
BOOST_CHECK_EQUAL(remaining, query.size() - optRDExpectedOffset);