return result;
});
- luaCtx.registerFunction<bool(DNSQuestion::*)(const DNSName& newName)>("rebase", [](DNSQuestion& dq, const DNSName& newName) -> bool {
- if (!dnsdist::rebaseDNSPacket(dq.getMutableData(), dq.ids.qname, newName)) {
+ luaCtx.registerFunction<bool(DNSQuestion::*)(const DNSName& newName)>("changeName", [](DNSQuestion& dq, const DNSName& newName) -> bool {
+ if (!dnsdist::changeNameInDNSPacket(dq.getMutableData(), dq.ids.qname, newName)) {
return false;
}
dq.ids.qname = newName;
return dnsdist::suspendResponse(dr, asyncID, queryID, timeoutMs);
});
- luaCtx.registerFunction<bool(DNSResponse::*)(const DNSName& newName)>("rebase", [](DNSResponse& dr, const DNSName& newName) -> bool {
- if (!dnsdist::rebaseDNSPacket(dr.getMutableData(), dr.ids.qname, newName)) {
+ luaCtx.registerFunction<bool(DNSResponse::*)(const DNSName& newName)>("changeName", [](DNSResponse& dr, const DNSName& newName) -> bool {
+ if (!dnsdist::changeNameInDNSPacket(dr.getMutableData(), dr.ids.qname, newName)) {
return false;
}
dr.ids.qname = newName;
/* decrease the returned TTL but _after_ inserting the original response into the packet cache */
void dnsdist_ffi_dnsresponse_set_max_returned_ttl(dnsdist_ffi_dnsresponse_t* dr, uint32_t max) __attribute__ ((visibility ("default")));
void dnsdist_ffi_dnsresponse_clear_records_type(dnsdist_ffi_dnsresponse_t* dr, uint16_t qtype) __attribute__ ((visibility ("default")));
+bool dnsdist_ffi_dnsresponse_rebase(dnsdist_ffi_dnsresponse_t* dr, const char* initialName, size_t initialNameSize) __attribute__ ((visibility ("default")));
bool dnsdist_ffi_dnsquestion_set_async(dnsdist_ffi_dnsquestion_t* dq, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) __attribute__ ((visibility ("default")));
bool dnsdist_ffi_dnsresponse_set_async(dnsdist_ffi_dnsquestion_t* dq, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) __attribute__ ((visibility ("default")));
bool dnsdist_ffi_drop_from_async(uint16_t asyncID, uint16_t queryID) __attribute__ ((visibility ("default")));
bool dnsdist_ffi_set_answer_from_async(uint16_t asyncID, uint16_t queryID, const char* raw, size_t rawSize) __attribute__ ((visibility ("default")));
bool dnsdist_ffi_set_rcode_from_async(uint16_t asyncID, uint16_t queryID, uint8_t rcode, bool clearAnswers) __attribute__ ((visibility ("default")));
+bool dnsdist_ffi_resume_from_async_with_alternate_name(uint16_t asyncID, uint16_t queryID, const char* alternateName, size_t alternateNameSize, const char* tag, size_t tagSize, const char* tagValue, size_t tagValueSize, const char* formerNameTagName, size_t formerNameTagSize) __attribute__ ((visibility ("default")));
typedef struct dnsdist_ffi_proxy_protocol_value {
const char* value;
uint32_t dnsdist_ffi_dnspacket_get_record_ttl(const dnsdist_ffi_dnspacket_t* packet, size_t idx) __attribute__ ((visibility ("default")));
uint16_t dnsdist_ffi_dnspacket_get_record_content_length(const dnsdist_ffi_dnspacket_t* packet, size_t idx) __attribute__ ((visibility ("default")));
uint16_t dnsdist_ffi_dnspacket_get_record_content_offset(const dnsdist_ffi_dnspacket_t* packet, size_t idx) __attribute__ ((visibility ("default")));
+size_t dnsdist_ffi_dnspacket_get_name_at_offset_raw(const char* packet, size_t packetSize, size_t offset, char* name, size_t nameSize) __attribute__ ((visibility ("default")));
void dnsdist_ffi_dnspacket_free(dnsdist_ffi_dnspacket_t*) __attribute__ ((visibility ("default")));
void dnsdist_ffi_metric_inc(const char* metricName, size_t metricNameLen) __attribute__ ((visibility ("default")));
}
}
+bool dnsdist_ffi_dnsresponse_rebase(dnsdist_ffi_dnsresponse_t* dr, const char* initialName, size_t initialNameSize)
+{
+ if (dr == nullptr || dr->dr == nullptr || initialName == nullptr || initialNameSize == 0) {
+ return false;
+ }
+
+ try {
+ DNSName parsed(initialName, initialNameSize, 0, false);
+
+ if (!dnsdist::changeNameInDNSPacket(dr->dr->getMutableData(), dr->dr->ids.qname, parsed)) {
+ return false;
+ }
+
+ // set qname to new one
+ dr->dr->ids.qname = parsed;
+ dr->dr->ids.skipCache = true;
+ }
+ catch (const std::exception& e) {
+ vinfolog("Error rebasing packet on a new DNSName: %s", e.what());
+ return false;
+ }
+
+ return true;
+}
+
bool dnsdist_ffi_dnsquestion_set_async(dnsdist_ffi_dnsquestion_t* dq, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs)
{
try {
return dnsdist::queueQueryResumptionEvent(std::move(query));
}
+bool dnsdist_ffi_resume_from_async_with_alternate_name(uint16_t asyncID, uint16_t queryID, const char* alternateName, size_t alternateNameSize, const char* tag, size_t tagSize, const char* tagValue, size_t tagValueSize, const char* formerNameTagName, size_t formerNameTagSize)
+{
+ if (!dnsdist::g_asyncHolder) {
+ return false;
+ }
+
+ auto query = dnsdist::g_asyncHolder->get(asyncID, queryID);
+ if (!query) {
+ vinfolog("Unable to resume with an alternate name, no object found for asynchronous ID %d and query ID %d", asyncID, queryID);
+ return false;
+ }
+
+ auto& ids = query->query.d_idstate;
+ DNSName originalName = ids.qname;
+
+ try {
+ DNSName parsed(alternateName, alternateNameSize, 0, false);
+
+ PacketBuffer initialPacket;
+ if (query->d_isResponse) {
+ if (!ids.d_packet) {
+ return false;
+ }
+ initialPacket = std::move(*ids.d_packet);
+ }
+ else {
+ initialPacket = std::move(query->query.d_buffer);
+ }
+
+ // edit qname in query packet
+ if (!dnsdist::changeNameInDNSPacket(initialPacket, originalName, parsed)) {
+ return false;
+ }
+ if (query->d_isResponse) {
+ query->d_isResponse = false;
+ }
+ query->query.d_buffer = std::move(initialPacket);
+ // set qname to new one
+ ids.qname = std::move(parsed);
+ }
+ catch (const std::exception& e) {
+ vinfolog("Error rebasing packet on a new DNSName: %s", e.what());
+ return false;
+ }
+
+ // save existing qname in tag
+ if (formerNameTagName != nullptr && formerNameTagSize > 0) {
+ if (!ids.qTag) {
+ ids.qTag = std::make_unique<QTag>();
+ }
+ (*ids.qTag)[std::string(formerNameTagName, formerNameTagSize)] = originalName.getStorage();
+ }
+
+ if (tag != nullptr && tagSize > 0) {
+ if (!ids.qTag) {
+ ids.qTag = std::make_unique<QTag>();
+ }
+ (*ids.qTag)[std::string(tag, tagSize)] = std::string(tagValue, tagValueSize);
+ }
+
+ ids.skipCache = true;
+
+ // resume as query
+ return dnsdist::queueQueryResumptionEvent(std::move(query));
+}
+
bool dnsdist_ffi_drop_from_async(uint16_t asyncID, uint16_t queryID)
{
if (!dnsdist::g_asyncHolder) {
return packet->overlay.d_records.at(idx).d_contentOffset;
}
+size_t dnsdist_ffi_dnspacket_get_name_at_offset_raw(const char* packet, size_t packetSize, size_t offset, char* name, size_t nameSize)
+{
+ if (packet == nullptr || name == nullptr || nameSize == 0 || offset >= packetSize) {
+ return 0;
+ }
+ try {
+ DNSName parsed(packet, packetSize, offset, true);
+ const auto& storage = parsed.getStorage();
+ if (nameSize < storage.size()) {
+ return 0;
+ }
+ memcpy(name, storage.data(), storage.size());
+ return storage.size();
+ }
+ catch (const std::exception& e) {
+ vinfolog("Error parsing DNSName via dnsdist_ffi_dnspacket_get_name_at_offset_raw: %s", e.what());
+ }
+ return 0;
+}
+
void dnsdist_ffi_dnspacket_free(dnsdist_ffi_dnspacket_t* packet)
{
if (packet != nullptr) {
:returns: The trailing data as a null-safe string
- .. method:: DNSQuestion:rebase(newName) -> bool
+ .. method:: DNSQuestion:changeName(newName) -> bool
.. versionadded:: 1.8.0
Change the qname of the current query in the DNS payload.
The reverse operation will have to be done on the response to set it back to the initial name, or the client will be confused and likely drop the response.
- See :func:`DNSResponse:rebase`.
+ See :func:`DNSResponse:changeName`.
Returns false on failure, true on success.
:param DNSName newName: The new qname to use
:param string func: The function to call to edit TTLs.
- .. method:: DNSResponse:rebase(initialName) -> bool
+ .. method:: DNSResponse:changeName(initialName) -> bool
.. versionadded:: 1.8.0
- Change the qname and records matching exactly this qname in the DNS payload of the current response to the supplied new name.
+ Change, in the DNS payload of the current response, the qname and the owner name of records to the supplied new name, if they are matching exactly the initial qname.
This only makes if the reverse operation was performed on the query, or the client will be confused and likely drop the response.
Note that only records whose owner name matches the qname in the initial response will be rewritten, and that only the owner name itself will be altered,
not the content of the record rdata. For some records this might cause an issue with compression pointers contained in the payload, as they might
A, AAAA, DHCID, TXT, OPT, HINFO, DNSKEY, CDNSKEY, DS, CDS, DLV, SSHFP, KEY, CERT, TLSA, SMIMEA, OPENPGPKEY, NSEC, NSEC3, CSYNC, NSEC3PARAM, LOC, NID, L32, L64, EUI48, EUI64, URI, CAA, NS, PTR, CNAME, DNAME, RRSIG, MX, SOA, SRV
Therefore this functionality only makes sense when the initial query is requesting a very simple type, like A or AAAA.
- See also :func:`DNSQuestion:rebase`.
+ See also :func:`DNSQuestion:changeName`.
Returns false on failure, true on success.
:param DNSName initialName: The initial qname
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
-class TestRebase(DNSDistTest):
+class TestChangeName(DNSDistTest):
_config_template = """
local tagName = 'initial-name'
- function luarebasequery(dq)
+ function luaChangeNamequery(dq)
dq:setTag(tagName, dq.qname:toString())
- if not dq:rebase(newDNSName('rebase.advanced.tests.dnsdist.org')) then
+ if not dq:changeName(newDNSName('changeName.advanced.tests.dnsdist.org')) then
errlog('Error rebasing the query')
return DNSAction.Drop
end
return DNSAction.None
end
- function luarebaseresponse(dr)
+ function luaChangeNameresponse(dr)
local initialName = dr:getTag(tagName)
- if not dr:rebase(newDNSName(initialName)) then
+ if not dr:changeName(newDNSName(initialName)) then
errlog('Error rebasing the response')
return DNSAction.Drop
end
return DNSAction.None
end
- addAction('rebase.advanced.tests.powerdns.com', LuaAction(luarebasequery))
- addResponseAction('rebase.advanced.tests.dnsdist.org', LuaResponseAction(luarebaseresponse))
+ addAction('changeName.advanced.tests.powerdns.com', LuaAction(luaChangeNamequery))
+ addResponseAction('changeName.advanced.tests.dnsdist.org', LuaResponseAction(luaChangeNameresponse))
newServer{address="127.0.0.1:%s"}
"""
- def testRebase(self):
+ def testChangeName(self):
"""
- Advanced: Rebase the query name
+ Advanced: ChangeName the query name
"""
- name = 'rebase.advanced.tests.powerdns.com.'
+ name = 'changeName.advanced.tests.powerdns.com.'
query = dns.message.make_query(name, 'A', 'IN')
- rebasedName = 'rebase.advanced.tests.dnsdist.org.'
- rebasedQuery = dns.message.make_query(rebasedName, 'A', 'IN')
- rebasedQuery.id = query.id
+ changedName = 'changeName.advanced.tests.dnsdist.org.'
+ changedQuery = dns.message.make_query(changedName, 'A', 'IN')
+ changedQuery.id = query.id
- response = dns.message.make_response(rebasedQuery)
- rrset = dns.rrset.from_text(rebasedName,
+ response = dns.message.make_response(changedQuery)
+ rrset = dns.rrset.from_text(changedName,
60,
dns.rdataclass.IN,
dns.rdatatype.A,
'4.3.2.1')
response.answer.append(rrset)
- rrset = dns.rrset.from_text('sub.sub2.rebase.advanced.tests.dnsdist.org.',
+ rrset = dns.rrset.from_text('sub.sub2.changeName.advanced.tests.dnsdist.org.',
60,
dns.rdataclass.IN,
dns.rdatatype.TXT,
- 'This text contains sub.sub2.rebase.advanced.tests.dnsdist.org.')
+ 'This text contains sub.sub2.changeName.advanced.tests.dnsdist.org.')
response.additional.append(rrset)
expectedResponse = dns.message.make_response(query)
'4.3.2.1')
expectedResponse.answer.append(rrset)
# we only rewrite records if the owner name matches the new target, nothing else
- rrset = dns.rrset.from_text('sub.sub2.rebase.advanced.tests.dnsdist.org.',
+ rrset = dns.rrset.from_text('sub.sub2.changeName.advanced.tests.dnsdist.org.',
60,
dns.rdataclass.IN,
dns.rdatatype.TXT,
- 'This text contains sub.sub2.rebase.advanced.tests.dnsdist.org.')
+ 'This text contains sub.sub2.changeName.advanced.tests.dnsdist.org.')
expectedResponse.additional.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
(receivedQuery, receivedResponse) = sender(query, response)
receivedQuery.id = query.id
- self.assertEqual(receivedQuery, rebasedQuery)
+ self.assertEqual(receivedQuery, changedQuery)
self.assertEqual(receivedResponse, expectedResponse)