* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include "dnsdist-ecs.hh"
+#include "ednsoptions.hh"
+#include "noinitvector.hh"
#include "protozero-trace.hh"
+#include "dnswriter.hh"
+#include "dnsparser.hh"
+#include "views.hh"
+
+#include <array>
#include <boost/test/unit_test_suite.hpp>
+#include <initializer_list>
+#include <limits>
+#include <memory>
+#include <ostream>
+#include <vector>
#ifndef DISABLE_PROTOBUF
#ifndef BOOST_TEST_DYN_LINK
#define BOOST_TEST_DYN_LINK
}
}
+BOOST_AUTO_TEST_CASE(addTraceparentToPacketBuffer_empty)
+{
+ // Ensure we return false when Tracer is nullptr
+ PacketBuffer buf;
+ auto ret = pdns::trace::dnsdist::addTraceparentEdnsOptionToPacketBuffer(buf, nullptr, 0, 0, EDNSOptionCode::TRACEPARENT, false);
+ BOOST_CHECK(!ret);
+};
+
+static void checkTraceparent(const PacketBuffer& packet, const std::shared_ptr<pdns::trace::dnsdist::Tracer> tracer, const size_t optRDPosition)
+{
+ pdns::trace::EDNSOTTraceRecordView data{reinterpret_cast<const uint8_t*>(&packet[optRDPosition + DNS_RDLENGTH_SIZE + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE]), pdns::trace::EDNSOTTraceRecord::fullSize}; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
+
+ uint8_t version;
+ BOOST_ASSERT(data.getVersion(version));
+ BOOST_CHECK_EQUAL(version, 0U);
+
+ uint8_t reserved;
+ BOOST_ASSERT(data.getReserved(reserved));
+ BOOST_CHECK_EQUAL(reserved, 0U);
+
+ pdns::trace::TraceID traceid;
+ BOOST_ASSERT(data.getTraceID(traceid));
+ BOOST_CHECK_EQUAL(traceid, tracer->getTraceID());
+
+ pdns::trace::SpanID spanid;
+ BOOST_ASSERT(data.getSpanID(spanid));
+ BOOST_CHECK_EQUAL(spanid, tracer->getLastSpanID());
+
+ uint8_t flags;
+ BOOST_ASSERT(data.getFlags(flags));
+ BOOST_CHECK_EQUAL(flags, 0U);
+}
+
+static const uint16_t ednsBufSize{1700};
+static const DNSName qname{"powerdns.com"};
+static PacketBuffer getPacket(bool edns = false, bool traceparentOpt = false)
+{
+ PacketBuffer buf;
+ GenericDNSPacketWriter<PacketBuffer> pwQ(buf, qname, QType::A, QClass::IN, 0);
+ pwQ.getHeader()->rd = 1;
+ if (edns) {
+ vector<pair<uint16_t, std::string>> ednsopts;
+ if (traceparentOpt) {
+ ednsopts.push_back({EDNSOptionCode::TRACEPARENT, {
+ 0, // VERSION
+ 0, // RESERVED
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, // TRACEID
+ 1, 2, 3, 4, 5, 6, 7, 8, // SPANID
+ 0 // FLAGS
+ }});
+ }
+ pwQ.addOpt(ednsBufSize, 0, 0, ednsopts);
+ }
+ pwQ.commit();
+
+ return buf;
+}
+
+BOOST_AUTO_TEST_CASE(addTraceparentToPacketBuffer_udp)
+{
+ auto buf = getPacket();
+
+ auto tracer = pdns::trace::dnsdist::Tracer::getTracer();
+ auto closer = tracer->openSpan("foo");
+
+ pdns::trace::dnsdist::addTraceparentEdnsOptionToPacketBuffer(buf, tracer, qname.wirelength(), 0, EDNSOptionCode::TRACEPARENT, false);
+
+ uint16_t optRDPosition{0};
+ size_t remaining{0};
+ auto ednsOptstartRet = dnsdist::getEDNSOptionsStart(buf, qname.wirelength(), &optRDPosition, &remaining);
+
+ BOOST_CHECK_EQUAL(ednsOptstartRet, 0);
+ BOOST_CHECK_NE(optRDPosition, 0);
+ BOOST_CHECK_EQUAL(remaining, DNS_RDLENGTH_SIZE + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + pdns::trace::EDNSOTTraceRecord::fullSize);
+
+ // Ensure we set the EDNS buffer size to 512 for non-EDNS queries
+ uint16_t ednsBufSizeFromPacket = (buf.at(optRDPosition - 6) << 8) + buf.at(optRDPosition - 5);
+ BOOST_CHECK_EQUAL(ednsBufSizeFromPacket, 512);
+
+ checkTraceparent(buf, tracer, optRDPosition);
+}
+
+BOOST_AUTO_TEST_CASE(addTraceparentToPacketBuffer_udp_proxy)
+{
+ auto buf = getPacket();
+
+ auto tracer = pdns::trace::dnsdist::Tracer::getTracer();
+ auto closer = tracer->openSpan("foo");
+
+ // Let's just add 7 bytes to the beginning of the packet
+ const size_t proxyPayloadSize = 7;
+ std::array<uint8_t, proxyPayloadSize> proxyPayload{1, 2, 3, 4, 5, 6, 7};
+ buf.insert(buf.begin(), proxyPayload.begin(), proxyPayload.end());
+
+ pdns::trace::dnsdist::addTraceparentEdnsOptionToPacketBuffer(buf, tracer, qname.wirelength(), proxyPayloadSize, EDNSOptionCode::TRACEPARENT, false);
+
+ // Ensure we still have the full proxy payload
+ std::array<uint8_t, proxyPayloadSize> payloadAfter;
+ std::copy_n(buf.begin(), proxyPayloadSize, payloadAfter.begin());
+ BOOST_CHECK(proxyPayload == payloadAfter);
+
+ // Make a DNS packet without Proxy headers and test
+ PacketBuffer packet{buf.begin() + proxyPayloadSize, buf.end()};
+
+ uint16_t optRDPosition{0};
+ size_t remaining{0};
+ auto ednsOptstartRet = dnsdist::getEDNSOptionsStart(packet, qname.wirelength(), &optRDPosition, &remaining);
+
+ BOOST_CHECK_EQUAL(ednsOptstartRet, 0);
+ BOOST_CHECK_NE(optRDPosition, 0);
+ BOOST_CHECK_EQUAL(remaining, DNS_RDLENGTH_SIZE + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + pdns::trace::EDNSOTTraceRecord::fullSize);
+
+ // Ensure we set the EDNS buffer size to 512 for non-EDNS queries
+ uint16_t ednsBufSizeFromPacket = (packet.at(optRDPosition - 6) << 8) + packet.at(optRDPosition - 5);
+ BOOST_CHECK_EQUAL(ednsBufSizeFromPacket, 512);
+
+ checkTraceparent(packet, tracer, optRDPosition);
+}
+
+BOOST_AUTO_TEST_CASE(addTraceparentToPacketBuffer_udp_edns)
+{
+ auto buf = getPacket(true);
+
+ auto tracer = pdns::trace::dnsdist::Tracer::getTracer();
+ auto closer = tracer->openSpan("foo");
+
+ pdns::trace::dnsdist::addTraceparentEdnsOptionToPacketBuffer(buf, tracer, qname.wirelength(), 0, EDNSOptionCode::TRACEPARENT, false);
+
+ uint16_t optRDPosition{0};
+ size_t remaining{0};
+ auto ednsOptstartRet = dnsdist::getEDNSOptionsStart(buf, qname.wirelength(), &optRDPosition, &remaining);
+
+ BOOST_CHECK_EQUAL(ednsOptstartRet, 0);
+ BOOST_CHECK_NE(optRDPosition, 0);
+ BOOST_CHECK_EQUAL(remaining, DNS_RDLENGTH_SIZE + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + pdns::trace::EDNSOTTraceRecord::fullSize);
+
+ // Ensure we keep the EDNS buffer size
+ uint16_t ednsBufSizeFromPacket = (buf.at(optRDPosition - 6) << 8) + buf.at(optRDPosition - 5);
+ BOOST_CHECK_EQUAL(ednsBufSizeFromPacket, ednsBufSize);
+
+ checkTraceparent(buf, tracer, optRDPosition);
+}
+
+BOOST_AUTO_TEST_CASE(addTraceparentToPacketBuffer_udp_edns_traceparent)
+{
+ auto buf = getPacket(true, true);
+
+ auto tracer = pdns::trace::dnsdist::Tracer::getTracer();
+ auto closer = tracer->openSpan("foo");
+
+ pdns::trace::dnsdist::addTraceparentEdnsOptionToPacketBuffer(buf, tracer, qname.wirelength(), 0, EDNSOptionCode::TRACEPARENT, false);
+
+ uint16_t optRDPosition{0};
+ size_t remaining{0};
+ auto ednsOptstartRet = dnsdist::getEDNSOptionsStart(buf, qname.wirelength(), &optRDPosition, &remaining);
+
+ BOOST_CHECK_EQUAL(ednsOptstartRet, 0);
+ BOOST_CHECK_NE(optRDPosition, 0);
+
+ BOOST_CHECK_EQUAL(remaining, DNS_RDLENGTH_SIZE + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + pdns::trace::EDNSOTTraceRecord::fullSize);
+
+ // Ensure we keep the EDNS buffer size
+ uint16_t ednsBufSizeFromPacket = (buf.at(optRDPosition - 6) << 8) + buf.at(optRDPosition - 5);
+ BOOST_CHECK_EQUAL(ednsBufSizeFromPacket, ednsBufSize);
+
+ // Check that we overwrote the whole TRACEPARENT
+ checkTraceparent(buf, tracer, optRDPosition);
+}
+
+BOOST_AUTO_TEST_CASE(addTraceparentToPacketBuffer_udp_edns_proxy)
+{
+ auto buf = getPacket(true);
+
+ auto tracer = pdns::trace::dnsdist::Tracer::getTracer();
+ auto closer = tracer->openSpan("foo");
+
+ // Let's just add 7 bytes to the beginning of the packet
+ const size_t proxyPayloadSize = 7;
+ std::array<uint8_t, proxyPayloadSize> proxyPayload{1, 2, 3, 4, 5, 6, 7};
+ buf.insert(buf.begin(), proxyPayload.begin(), proxyPayload.end());
+
+ pdns::trace::dnsdist::addTraceparentEdnsOptionToPacketBuffer(buf, tracer, qname.wirelength(), proxyPayloadSize, EDNSOptionCode::TRACEPARENT, false);
+
+ // Ensure we still have the full proxy payload
+ std::array<uint8_t, proxyPayloadSize> payloadAfter;
+ std::copy_n(buf.begin(), proxyPayloadSize, payloadAfter.begin());
+ BOOST_CHECK(proxyPayload == payloadAfter);
+
+ // Make a DNS packet without Proxy headers and test
+ PacketBuffer packet{buf.begin() + proxyPayloadSize, buf.end()};
+
+ uint16_t optRDPosition{0};
+ size_t remaining{0};
+ auto ednsOptstartRet = dnsdist::getEDNSOptionsStart(packet, qname.wirelength(), &optRDPosition, &remaining);
+
+ BOOST_CHECK_EQUAL(ednsOptstartRet, 0);
+ BOOST_CHECK_NE(optRDPosition, 0);
+ BOOST_CHECK_EQUAL(remaining, DNS_RDLENGTH_SIZE + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + pdns::trace::EDNSOTTraceRecord::fullSize);
+
+ // Ensure we keep the EDNS buffer size
+ uint16_t ednsBufSizeFromPacket = (packet.at(optRDPosition - 6) << 8) + packet.at(optRDPosition - 5);
+ BOOST_CHECK_EQUAL(ednsBufSizeFromPacket, ednsBufSize);
+
+ checkTraceparent(packet, tracer, optRDPosition);
+}
+
+BOOST_AUTO_TEST_CASE(addTraceparentToPacketBuffer_tcp)
+{
+ auto buf = getPacket();
+
+ auto tracer = pdns::trace::dnsdist::Tracer::getTracer();
+ auto closer = tracer->openSpan("foo");
+
+ std::array<uint8_t, 2> tcpSize{(uint8_t)(buf.size() >> 8), (uint8_t)buf.size()};
+ buf.insert(buf.begin(), tcpSize.begin(), tcpSize.end());
+
+ pdns::trace::dnsdist::addTraceparentEdnsOptionToPacketBuffer(buf, tracer, qname.wirelength(), 0, EDNSOptionCode::TRACEPARENT, true);
+
+ // Verify we set the new TCP size correctly
+ uint16_t tcpSizeAfter = (buf.at(0) << 8) + buf.at(1);
+ BOOST_CHECK_EQUAL(tcpSizeAfter, buf.size() - 2);
+
+ PacketBuffer packet{buf.begin() + 2, buf.end()};
+
+ uint16_t optRDPosition{0};
+ size_t remaining{0};
+ auto ednsOptstartRet = dnsdist::getEDNSOptionsStart(packet, qname.wirelength(), &optRDPosition, &remaining);
+
+ BOOST_CHECK_EQUAL(ednsOptstartRet, 0);
+ BOOST_CHECK_NE(optRDPosition, 0);
+ BOOST_CHECK_EQUAL(remaining, DNS_RDLENGTH_SIZE + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + pdns::trace::EDNSOTTraceRecord::fullSize);
+
+ // Ensure we set the EDNS buffer size to max(uint16_t) for non-EDNS TCP queries
+ uint16_t ednsBufSizeFromPacket = (packet.at(optRDPosition - 6) << 8) + packet.at(optRDPosition - 5);
+ // Ignored over TCP, but we still set is to 512
+ BOOST_CHECK_EQUAL(ednsBufSizeFromPacket, 512);
+
+ checkTraceparent(packet, tracer, optRDPosition);
+}
+
+BOOST_AUTO_TEST_CASE(addTraceparentToPacketBuffer_tcp_proxy)
+{
+ auto buf = getPacket();
+
+ auto tracer = pdns::trace::dnsdist::Tracer::getTracer();
+ auto closer = tracer->openSpan("foo");
+
+ std::array<uint8_t, 2> tcpSize{(uint8_t)(buf.size() >> 8), (uint8_t)buf.size()};
+ buf.insert(buf.begin(), tcpSize.begin(), tcpSize.end());
+
+ // Let's just add 7 bytes to the beginning of the packet
+ const size_t proxyPayloadSize = 7;
+ std::array<uint8_t, proxyPayloadSize> proxyPayload{1, 2, 3, 4, 5, 6, 7};
+ buf.insert(buf.begin(), proxyPayload.begin(), proxyPayload.end());
+
+ pdns::trace::dnsdist::addTraceparentEdnsOptionToPacketBuffer(buf, tracer, qname.wirelength(), proxyPayloadSize, EDNSOptionCode::TRACEPARENT, true);
+
+ // Ensure we still have the full proxy payload
+ std::array<uint8_t, proxyPayloadSize> payloadAfter;
+ std::copy_n(buf.begin(), proxyPayloadSize, payloadAfter.begin());
+ BOOST_CHECK(proxyPayload == payloadAfter);
+
+ // Make a DNS packet without Proxy headers and test
+ PacketBuffer packet{buf.begin() + proxyPayloadSize, buf.end()};
+
+ // Verify we set the new TCP size correctly
+ uint16_t tcpSizeAfter = (packet.at(0) << 8) + packet.at(1);
+ BOOST_CHECK_EQUAL(tcpSizeAfter, packet.size() - 2);
+
+ // Strip the TCP header
+ packet = {packet.begin() + 2, packet.end()};
+
+ uint16_t optRDPosition{0};
+ size_t remaining{0};
+ auto ednsOptstartRet = dnsdist::getEDNSOptionsStart(packet, qname.wirelength(), &optRDPosition, &remaining);
+
+ BOOST_CHECK_EQUAL(ednsOptstartRet, 0);
+ BOOST_CHECK_NE(optRDPosition, 0);
+ BOOST_CHECK_EQUAL(remaining, DNS_RDLENGTH_SIZE + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + pdns::trace::EDNSOTTraceRecord::fullSize);
+
+ // Ensure we set the EDNS buffer size to max(uint16_t) for non-EDNS TCP queries
+ uint16_t ednsBufSizeFromPacket = (packet.at(optRDPosition - 6) << 8) + packet.at(optRDPosition - 5);
+ // Ignored over TCP, but we still set is to 512
+ BOOST_CHECK_EQUAL(ednsBufSizeFromPacket, 512);
+
+ checkTraceparent(packet, tracer, optRDPosition);
+}
+
+BOOST_AUTO_TEST_CASE(addTraceparentToPacketBuffer_tcp_edns)
+{
+ auto buf = getPacket(true);
+
+ auto tracer = pdns::trace::dnsdist::Tracer::getTracer();
+ auto closer = tracer->openSpan("foo");
+
+ std::array<uint8_t, 2> tcpSize{(uint8_t)(buf.size() >> 8), (uint8_t)buf.size()};
+ buf.insert(buf.begin(), tcpSize.begin(), tcpSize.end());
+
+ pdns::trace::dnsdist::addTraceparentEdnsOptionToPacketBuffer(buf, tracer, qname.wirelength(), 0, EDNSOptionCode::TRACEPARENT, true);
+
+ // Verify we set the new TCP size correctly
+ uint16_t tcpSizeAfter = (buf.at(0) << 8) + buf.at(1);
+ BOOST_CHECK_EQUAL(tcpSizeAfter, buf.size() - 2);
+
+ PacketBuffer packet{buf.begin() + 2, buf.end()};
+
+ uint16_t optRDPosition{0};
+ size_t remaining{0};
+ auto ednsOptstartRet = dnsdist::getEDNSOptionsStart(packet, qname.wirelength(), &optRDPosition, &remaining);
+
+ BOOST_CHECK_EQUAL(ednsOptstartRet, 0);
+ BOOST_CHECK_NE(optRDPosition, 0);
+ BOOST_CHECK_EQUAL(remaining, DNS_RDLENGTH_SIZE + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + pdns::trace::EDNSOTTraceRecord::fullSize);
+
+ // Ensure we keep the EDNS buffer size
+ uint16_t ednsBufSizeFromPacket = (packet.at(optRDPosition - 6) << 8) + packet.at(optRDPosition - 5);
+ BOOST_CHECK_EQUAL(ednsBufSizeFromPacket, ednsBufSize);
+
+ checkTraceparent(packet, tracer, optRDPosition);
+}
+
+BOOST_AUTO_TEST_CASE(addTraceparentToPacketBuffer_tcp_edns_proxy)
+{
+ auto buf = getPacket(true);
+
+ auto tracer = pdns::trace::dnsdist::Tracer::getTracer();
+ auto closer = tracer->openSpan("foo");
+
+ std::array<uint8_t, 2> tcpSize{(uint8_t)(buf.size() >> 8), (uint8_t)buf.size()};
+ buf.insert(buf.begin(), tcpSize.begin(), tcpSize.end());
+
+ // Let's just add 7 bytes to the beginning of the packet
+ const size_t proxyPayloadSize = 7;
+ std::array<uint8_t, proxyPayloadSize> proxyPayload{1, 2, 3, 4, 5, 6, 7};
+ buf.insert(buf.begin(), proxyPayload.begin(), proxyPayload.end());
+
+ pdns::trace::dnsdist::addTraceparentEdnsOptionToPacketBuffer(buf, tracer, qname.wirelength(), proxyPayloadSize, EDNSOptionCode::TRACEPARENT, true);
+
+ // Ensure we still have the full proxy payload
+ std::array<uint8_t, proxyPayloadSize> payloadAfter;
+ std::copy_n(buf.begin(), proxyPayloadSize, payloadAfter.begin());
+ BOOST_CHECK(proxyPayload == payloadAfter);
+
+ // Make a DNS packet without Proxy headers and test
+ PacketBuffer packet{buf.begin() + proxyPayloadSize, buf.end()};
+
+ // Verify we set the new TCP size correctly
+ uint16_t tcpSizeAfter = (packet.at(0) << 8) + packet.at(1);
+ BOOST_CHECK_EQUAL(tcpSizeAfter, packet.size() - 2);
+
+ packet = {packet.begin() + 2, packet.end()};
+
+ uint16_t optRDPosition{0};
+ size_t remaining{0};
+ auto ednsOptstartRet = dnsdist::getEDNSOptionsStart(packet, qname.wirelength(), &optRDPosition, &remaining);
+
+ BOOST_CHECK_EQUAL(ednsOptstartRet, 0);
+ BOOST_CHECK_NE(optRDPosition, 0);
+ BOOST_CHECK_EQUAL(remaining, DNS_RDLENGTH_SIZE + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + pdns::trace::EDNSOTTraceRecord::fullSize);
+
+ // Ensure we keep the EDNS buffer size
+ uint16_t ednsBufSizeFromPacket = (packet.at(optRDPosition - 6) << 8) + packet.at(optRDPosition - 5);
+ BOOST_CHECK_EQUAL(ednsBufSizeFromPacket, ednsBufSize);
+
+ checkTraceparent(packet, tracer, optRDPosition);
+}
+
BOOST_AUTO_TEST_SUITE_END()
#endif // DISABLE_PROTOBUF