{ "DSTPortRule", true, "port", "matches questions received to the destination port specified" },
{ "dumpStats", true, "", "print all statistics we gather" },
{ "dynBlockRulesGroup", true, "", "return a new DynBlockRulesGroup object" },
+ { "EDNSVersionRule", true, "version", "matches queries with the specified EDNS version" },
{ "EDNSOptionRule", true, "optcode", "matches queries with the specified EDNS0 option present" },
{ "ERCodeRule", true, "rcode", "matches responses with the specified extended rcode (EDNS0)" },
{ "exceedNXDOMAINs", true, "rate, seconds", "get set of addresses that exceed `rate` NXDOMAIN/s over `seconds` seconds" },
generateEDNSOption(EDNSOptionCode::ECS, payload, res);
}
-void generateOptRR(const std::string& optRData, string& res, uint16_t udpPayloadSize, uint16_t ednsrcode, bool dnssecOK)
+void generateOptRR(const std::string& optRData, string& res, uint16_t udpPayloadSize, uint8_t ednsrcode, bool dnssecOK)
{
const uint8_t name = 0;
dnsrecordheader dh;
return 0;
}
-bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize, uint16_t ednsrcode)
+bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize, uint8_t ednsrcode)
{
if (dh->arcount != 0) {
return false;
return false;
}
-
-int getEDNSVersion(const DNSQuestion& dq)
-try
-{
- if (ntohs(dq.dh->qdcount) != 1 || dq.dh->ancount != 0 || ntohs(dq.dh->arcount) != 1 || dq.dh->nscount != 0) {
- return 0;
- }
-
- if (dq.len <= sizeof(dnsheader)) {
- return 0;
- }
-
- size_t pos = sizeof(dnsheader) + dq.consumed + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
-
- if (dq.len <= (pos + /* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE)) {
- return 0;
- }
-
- const char* packet = reinterpret_cast<const char*>(dq.dh);
-
- if (packet[pos] != 0) {
- /* not root, so not a valid OPT record */
- return 0;
- }
-
- pos++;
-
- uint16_t qtype = (reinterpret_cast<const unsigned char*>(packet)[pos])*256 + reinterpret_cast<const unsigned char*>(packet)[pos+1];
- pos += DNS_TYPE_SIZE;
- pos += DNS_CLASS_SIZE;
-
- if (qtype != QType::OPT || (pos + EDNS_EXTENDED_RCODE_SIZE + 1) >= dq.len) {
- return 0;
- }
-
- const uint8_t* z = reinterpret_cast<const uint8_t*>(packet) + pos + EDNS_EXTENDED_RCODE_SIZE;
- return 0x100 * (*z) + *(z+1);
-}
-catch(...)
-{
- return 0;
-}
int rewriteResponseWithoutEDNS(const std::string& initialPacket, vector<uint8_t>& newContent);
int locateEDNSOptRR(const std::string& packet, uint16_t * optStart, size_t * optLen, bool * last);
-void generateOptRR(const std::string& optRData, string& res, uint16_t udpPayloadSize, uint16_t ednsrcode, bool dnssecOK);
+void generateOptRR(const std::string& optRData, string& res, uint16_t udpPayloadSize, uint8_t ednsrcode, bool dnssecOK);
void generateECSOption(const ComboAddress& source, string& res, uint16_t ECSPrefixLength);
int removeEDNSOptionFromOPT(char* optStart, size_t* optLen, const uint16_t optionCodeToRemove);
int rewriteResponseWithoutEDNSOption(const std::string& initialPacket, const uint16_t optionCodeToSkip, vector<uint8_t>& newContent);
int getEDNSOptionsStart(const char* packet, const size_t offset, const size_t len, uint16_t* optRDPosition, size_t * remaining);
bool isEDNSOptionInOpt(const std::string& packet, const size_t optStart, const size_t optLen, const uint16_t optionCodeToFind, size_t* optContentStart = nullptr, uint16_t* optContentLen = nullptr);
-bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize, uint16_t ednsrcode);
+bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize, uint8_t ednsrcode);
bool addEDNSToQueryTurnedResponse(DNSQuestion& dq);
bool handleEDNSClientSubnet(DNSQuestion& dq, bool* ednsAdded, bool* ecsAdded, bool preserveTrailingData);
int getEDNSZ(const DNSQuestion& dq);
bool queryHasEDNS(const DNSQuestion& dq);
-int getEDNSVersion(const DNSQuestion& dq);
uint8_t d_rcode;
};
+class ERCodeAction : public DNSAction
+{
+public:
+ ERCodeAction(uint8_t rcode) : d_rcode(rcode) {}
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ dq->dh->rcode = (d_rcode & 0xF);
+ dq->ednsRCode = ((d_rcode & 0xFFF0) >> 4);
+ dq->dh->qr = true; // for good measure
+ return Action::HeaderModify;
+ }
+ string toString() const override
+ {
+ return "set ercode "+ERCode::to_s(d_rcode);
+ }
+
+private:
+ uint8_t d_rcode;
+};
+
class TCAction : public DNSAction
{
public:
return std::shared_ptr<DNSAction>(new RCodeAction(rcode));
});
+ g_lua.writeFunction("ERCodeAction", [](uint8_t rcode) {
+ return std::shared_ptr<DNSAction>(new ERCodeAction(rcode));
+ });
+
g_lua.writeFunction("SkipCacheAction", []() {
return std::shared_ptr<DNSAction>(new SkipCacheAction);
});
return std::shared_ptr<DNSRule>(new ERCodeRule(rcode));
});
+ g_lua.writeFunction("EDNSVersionRule", [](uint8_t version) {
+ return std::shared_ptr<DNSRule>(new EDNSVersionRule(version));
+ });
+
g_lua.writeFunction("EDNSOptionRule", [](uint16_t optcode) {
return std::shared_ptr<DNSRule>(new EDNSOptionRule(optcode));
});
return;
}
- if (queryHasEDNS(dq) && getEDNSVersion(dq) > 0) {
- dq.dh->qr = true;
- dq.dh->rcode = (16 & 0xF);
- dq.ednsRCode = ((16 & 0xFFF0)>>4); // set rcode to BADVERS
- }
-
if(dq.dh->qr) { // something turned it into a response
fixUpQueryTurnedResponse(dq, origFlags);
unsigned int consumed{0};
uint16_t len;
uint16_t ecsPrefixLength;
- uint16_t ednsRCode; // WARNING: this is really 12 bits
+ uint8_t ednsRCode;
boost::optional<uint32_t> tempFailureTTL;
const bool tcp;
const struct timespec* queryTime;
uint8_t d_extrcode; // upper bits in EDNS0 record
};
+class EDNSVersionRule : public DNSRule
+{
+public:
+ EDNSVersionRule(uint8_t version) : d_version(version)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ uint16_t optStart;
+ size_t optLen = 0;
+ bool last = false;
+ const char * packet = reinterpret_cast<const char*>(dq->dh);
+ std::string packetStr(packet, dq->len);
+ int res = locateEDNSOptRR(packetStr, &optStart, &optLen, &last);
+ if (res != 0) {
+ // no EDNS OPT RR
+ return false;
+ }
+
+ // root label (1), type (2), class (2), ttl (4) + rdlen (2)
+ if (optLen < 11) {
+ return false;
+ }
+
+ if (optStart < dq->len && packetStr.at(optStart) != 0) {
+ // OPT RR Name != '.'
+ return false;
+ }
+ EDNS0Record edns0;
+ static_assert(sizeof(EDNS0Record) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t) AKA RR TTL size");
+ // copy out 4-byte "ttl" (really the EDNS0 record), after root label (1) + type (2) + class (2).
+ memcpy(&edns0, packet + optStart + 5, sizeof edns0);
+
+ return d_version < edns0.version;
+ }
+ string toString() const override
+ {
+ return "ednsversion>"+std::to_string(d_version);
+ }
+private:
+ uint8_t d_version;
+};
+
class EDNSOptionRule : public DNSRule
{
public: