type: "bool"
default: "false"
description: "Whether to add a proxy protocol payload to the query"
+- name: "UnsetTag"
+ description: "Remove a tag named ``tag`` from this query. Subsequent rules are processed after this action"
+ parameters:
+ - name: "tag"
+ type: "String"
+ description: "The tag name"
std::string d_value;
};
+class UnsetTagAction : public DNSAction
+{
+public:
+ // this action does not stop the processing
+ UnsetTagAction(std::string tag) :
+ d_tag(std::move(tag))
+ {
+ }
+ DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
+ {
+ (void)ruleresult;
+ dnsquestion->unsetTag(d_tag);
+
+ return Action::None;
+ }
+ [[nodiscard]] std::string toString() const override
+ {
+ return "unset tag '" + d_tag;
+ }
+
+private:
+ std::string d_tag;
+};
+
#ifndef DISABLE_PROTOBUF
class DnstapLogResponseAction : public DNSResponseAction, public boost::noncopyable
{
std::string d_value;
};
+class UnsetTagResponseAction : public DNSResponseAction
+{
+public:
+ // this action does not stop the processing
+ UnsetTagResponseAction(std::string tag) :
+ d_tag(std::move(tag))
+ {
+ }
+ DNSResponseAction::Action operator()(DNSResponse* dnsresponse, std::string* ruleresult) const override
+ {
+ (void)ruleresult;
+ dnsresponse->unsetTag(d_tag);
+
+ return Action::None;
+ }
+ [[nodiscard]] std::string toString() const override
+ {
+ return "unset tag '" + d_tag;
+ }
+
+private:
+ std::string d_tag;
+};
+
class ClearRecordTypesResponseAction : public DNSResponseAction, public boost::noncopyable
{
public:
luaCtx.registerFunction<void (DNSQuestion::*)(std::string, std::string)>("setTag", [](DNSQuestion& dnsQuestion, const std::string& strLabel, const std::string& strValue) {
dnsQuestion.setTag(strLabel, strValue);
});
+ luaCtx.registerFunction<void (DNSQuestion::*)(std::string)>("unsetTag", [](DNSQuestion& dnsQuestion, const std::string& strLabel) {
+ dnsQuestion.unsetTag(strLabel);
+ });
luaCtx.registerFunction<void (DNSQuestion::*)(LuaAssociativeTable<std::string>)>("setTagArray", [](DNSQuestion& dnsQuestion, const LuaAssociativeTable<std::string>& tags) {
for (const auto& tag : tags) {
dnsQuestion.setTag(tag.first, tag.second);
}
});
luaCtx.registerFunction<string (DNSQuestion::*)(std::string) const>("getTag", [](const DNSQuestion& dnsQuestion, const std::string& strLabel) {
- if (!dnsQuestion.ids.qTag) {
- return string();
- }
-
- std::string strValue;
- const auto tagIt = dnsQuestion.ids.qTag->find(strLabel);
- if (tagIt == dnsQuestion.ids.qTag->cend()) {
+ auto value = dnsQuestion.getTag(strLabel);
+ if (!value) {
return string();
}
- return tagIt->second;
+ return *value;
});
luaCtx.registerFunction<QTag (DNSQuestion::*)(void) const>("getTagArray", [](const DNSQuestion& dnsQuestion) -> QTag {
if (!dnsQuestion.ids.qTag) {
dnsResponse.setTag(strLabel, strValue);
});
+ luaCtx.registerFunction<void (DNSResponse::*)(std::string)>("unsetTag", [](DNSResponse& dnsResponse, const std::string& strLabel) {
+ dnsResponse.unsetTag(strLabel);
+ });
+
luaCtx.registerFunction<void (DNSResponse::*)(LuaAssociativeTable<std::string>)>("setTagArray", [](DNSResponse& dnsResponse, const LuaAssociativeTable<string>& tags) {
for (const auto& tag : tags) {
dnsResponse.setTag(tag.first, tag.second);
}
});
luaCtx.registerFunction<string (DNSResponse::*)(std::string) const>("getTag", [](const DNSResponse& dnsResponse, const std::string& strLabel) {
- if (!dnsResponse.ids.qTag) {
- return string();
- }
-
- std::string strValue;
- const auto tagIt = dnsResponse.ids.qTag->find(strLabel);
- if (tagIt == dnsResponse.ids.qTag->cend()) {
+ auto value = dnsResponse.getTag(strLabel);
+ if (!value) {
return string();
}
- return tagIt->second;
+ return *value;
});
luaCtx.registerFunction<QTag (DNSResponse::*)(void) const>("getTagArray", [](const DNSResponse& dnsResponse) {
if (!dnsResponse.ids.qTag) {
void dnsdist_ffi_dnsquestion_set_ecs_prefix_length(dnsdist_ffi_dnsquestion_t* dq, uint16_t ecsPrefixLength) __attribute__ ((visibility ("default")));
void dnsdist_ffi_dnsquestion_set_temp_failure_ttl(dnsdist_ffi_dnsquestion_t* dq, uint32_t tempFailureTTL) __attribute__ ((visibility ("default")));
void dnsdist_ffi_dnsquestion_unset_temp_failure_ttl(dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
-void dnsdist_ffi_dnsquestion_set_tag(dnsdist_ffi_dnsquestion_t* dq, const char* label, const char* value) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_dnsquestion_set_tag(dnsdist_ffi_dnsquestion_t* dq, const char* label, const char* value) __attribute__((visibility("default")));
+void dnsdist_ffi_dnsquestion_unset_tag(dnsdist_ffi_dnsquestion_t* dq, const char* label) __attribute__ ((visibility ("default")));
void dnsdist_ffi_dnsquestion_set_tag_raw(dnsdist_ffi_dnsquestion_t* dq, const char* label, const char* value, size_t valueSize) __attribute__ ((visibility ("default")));
void dnsdist_ffi_dnsquestion_set_requestor_id(dnsdist_ffi_dnsquestion_t* dq, const char* value, size_t valueSize) __attribute__ ((visibility ("default")));
dq->dq->setTag(label, value);
}
+void dnsdist_ffi_dnsquestion_unset_tag(dnsdist_ffi_dnsquestion_t* dq, const char* label)
+{
+ dq->dq->unsetTag(label);
+}
+
void dnsdist_ffi_dnsquestion_set_tag_raw(dnsdist_ffi_dnsquestion_t* dq, const char* label, const char* value, size_t valueSize)
{
dq->dq->setTag(label, std::string(value, valueSize));
description: "The SNMP trap reason"
- name: "TC"
description: "Truncate an existing answer, to force the client to TCP. Only applied to answers that will be sent to the client over TCP. In addition to the TC bit being set, all records are removed from the answer, authority and additional sections"
+- name: "UnsetTag"
+ description: "Remove a tag named ``tag`` from this response. Subsequent rules are processed after this action"
+ parameters:
+ - name: "tag"
+ type: "String"
+ description: "The tag name"
ids.qTag->insert_or_assign(key, std::move(value));
}
+ void unsetTag(const std::string& key)
+ {
+ if (ids.qTag) {
+ ids.qTag->erase(key);
+ }
+ }
+
+ std::optional<std::string> getTag(const std::string& key) const
+ {
+ if (ids.qTag) {
+ const auto tagIt = ids.qTag->find(key);
+ if (tagIt != ids.qTag->cend()) {
+ return tagIt->second;
+ }
+ }
+ return std::nullopt;
+ }
+
const struct timespec& getQueryRealTime() const
{
return ids.queryRealTime.d_start;
Subsequent rules are processed after this action.
:param int ttl: Cache TTL for temporary failure replies
+
+.. function:: UnsetTagAction(name)
+
+ .. versionadded:: 2.1.0
+
+ Remove a tag named ``name``.
+ Subsequent rules are processed after this action.
+
+ :param string name: The name of the tag to set
+
+.. function:: UnsetTagResponseAction(name)
+
+ .. versionadded:: 2.1.0
+
+ Remove a tag named ``name``.
+ Subsequent rules are processed after this action.
+
+ :param string name: The name of the tag to set
:param int queryID: A numeric identifier used to identify the suspended query for later retrieval. This ID does not have to match the query ID present in the initial DNS header. A given (asyncID, queryID) tuple should be unique at a given time. Valid values range from 0 to 65535, both included.
:param int timeoutMS: The maximum duration this query will be kept in the asynchronous holder before being automatically resumed, in milliseconds.
+ .. method:: DNSQuestion:unsetTag(key)
+
+ .. versionadded:: 2.1.0
+
+ Remove a tag from the DNSQuestion object.
+
+ :param string key: The tag's key
+
+
.. _DNSResponse:
DNSResponse object
BOOST_CHECK_EQUAL(std::string(tags[0].name), tagName.c_str());
BOOST_CHECK_EQUAL(std::string(tags[0].value), tagValue.c_str());
+ dnsdist_ffi_dnsquestion_unset_tag(&lightDQ, tagName.c_str());
+
+ got = dnsdist_ffi_dnsquestion_get_tag(&lightDQ, tagName.c_str());
+ BOOST_CHECK(got == nullptr);
+
dnsdist_ffi_dnsquestion_set_tag_raw(&lightDQ, tagName.c_str(), tagRawValue.c_str(), tagRawValue.size());
// too small
BOOST_CHECK_EQUAL(cookiesOptionStr, std::string(ecsOption->second.values.at(0).content, ecsOption->second.values.at(0).size));
}
+BOOST_AUTO_TEST_CASE(test_DNSQuestion_Tags)
+{
+ InternalQueryState ids;
+ ids.origRemote = ComboAddress("192.0.2.1:42");
+ ids.origDest = ComboAddress("127.0.0.1:53");
+ ids.protocol = dnsdist::Protocol::DoUDP;
+ ids.qname = DNSName("powerdns.com.");
+ ids.qtype = QType::A;
+ ids.qclass = QClass::IN;
+ ids.queryRealTime.start();
+
+ PacketBuffer packet;
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(packet, ids.qname, ids.qtype, ids.qclass, 0);
+ packetWriter.addOpt(4096, 0, EDNS_HEADER_FLAG_DO);
+ packetWriter.commit();
+
+ DNSQuestion dnsQuestion(ids, packet);
+
+ BOOST_CHECK(dnsQuestion.getTag("not-existing") == std::nullopt);
+
+ const std::string tagName{"my-tag-name"};
+ const std::string tagValue{"my-tag-value"};
+ const std::string tagValue2{"my-tag-value-2"};
+ dnsQuestion.setTag(tagName, tagValue);
+ auto got = dnsQuestion.getTag(tagName);
+ BOOST_REQUIRE(got);
+ BOOST_CHECK_EQUAL(*got, tagValue);
+
+ dnsQuestion.setTag(tagName, tagValue2);
+ got = dnsQuestion.getTag(tagName);
+ BOOST_REQUIRE(got);
+ BOOST_CHECK_EQUAL(*got, tagValue2);
+
+ dnsQuestion.unsetTag(tagName);
+ got = dnsQuestion.getTag(tagName);
+ BOOST_CHECK(!got);
+}
+
BOOST_AUTO_TEST_SUITE_END();
return false
end
+ tag = ffi.C.dnsdist_ffi_dnsquestion_get_tag(dq, 'b-tag')
+ if tag ~= nil then
+ print('invalid B tag value')
+ print(ffi.string(tag))
+ return false
+ end
+
local raw_tag_buf_size = 255
local raw_tag_buf = ffi.new("char [?]", raw_tag_buf_size)
local raw_tag_size = ffi.C.dnsdist_ffi_dnsquestion_get_tag_raw(dq, 'raw-tag', raw_tag_buf, raw_tag_buf_size)
function luaffiactionsettag(dq)
ffi.C.dnsdist_ffi_dnsquestion_set_tag(dq, 'a-tag', 'a-value')
+ ffi.C.dnsdist_ffi_dnsquestion_set_tag(dq, 'b-tag', 'b-value')
+ return DNSAction.None
+ end
+
+ function luaffiactionunsettag(dq)
+ ffi.C.dnsdist_ffi_dnsquestion_unset_tag(dq, 'b-tag')
return DNSAction.None
end
addAction(AllRule(), LuaFFIAction(luaffiactionsettag))
addAction(AllRule(), LuaFFIAction(luaffiactionsettagraw))
+ addAction(AllRule(), LuaFFIAction(luaffiactionunsettag))
addAction(LuaFFIRule(luaffirulefunction), LuaFFIAction(luaffiactionfunction))
-- newServer{address="127.0.0.1:%d"}
"""
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertTrue(receivedResponse)
self.assertEqual(expectedResponse, receivedResponse)
+
+class TestUnsetTag(DNSDistTest):
+
+ _config_template = """
+ newServer{address="127.0.0.1:%d"}
+
+ function dqset(dq)
+ dq:setTag("dns", "value1")
+ return DNSAction.None, ""
+ end
+
+ addAction(AllRule(), LuaAction(dqset))
+
+ addAction(AllRule(), UnsetTagAction("dns"))
+ addAction(TagRule("dns", "value1"), SpoofAction("1.2.3.4"))
+ """
+
+ def testUnsetTag(self):
+
+ """
+ Tag: Test UnsetTagAction
+ """
+ name = 'unset.tags.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ # dnsdist set RA = RD for spoofed responses
+ query.flags &= ~dns.flags.RD
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '1.2.3.50')
+ expectedResponse.answer.append(rrset)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response=expectedResponse)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEqual(query, receivedQuery)
+ self.assertEqual(expectedResponse, receivedResponse)
+
+class TestUnsetTagViaLua(DNSDistTest):
+
+ _config_template = """
+ newServer{address="127.0.0.1:%d"}
+
+ function dqunset(dq)
+ dq:unsetTag("dns")
+ return DNSAction.None, ""
+ end
+
+ addAction(AllRule(), SetTagAction("dns", "value1"))
+ addAction(AllRule(), LuaAction(dqunset))
+
+ addAction(TagRule("dns", "value1"), SpoofAction("1.2.3.4"))
+ """
+
+ def testUnsetTag(self):
+
+ """
+ Tag: Test UnsetTag via Lua
+ """
+ name = 'unset-lua.tags.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ # dnsdist set RA = RD for spoofed responses
+ query.flags &= ~dns.flags.RD
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '1.2.3.50')
+ expectedResponse.answer.append(rrset)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response=expectedResponse)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEqual(query, receivedQuery)
+ self.assertEqual(expectedResponse, receivedResponse)