*/
#include "dnsdist.hh"
#include "dnsdist-async.hh"
+#include "dnsdist-dnsparser.hh"
#include "dnsdist-ecs.hh"
#include "dnsdist-internal-queries.hh"
#include "dnsdist-lua.hh"
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)) {
+ return false;
+ }
+ dq.ids.qname = newName;
+ return true;
+ });
+
luaCtx.registerFunction<void(DNSQuestion::*)(const boost::variant<LuaArray<ComboAddress>, LuaArray<std::string>>& response)>("spoof", [](DNSQuestion& dq, const boost::variant<LuaArray<ComboAddress>, LuaArray<std::string>>& response) {
if (response.type() == typeid(LuaArray<ComboAddress>)) {
std::vector<ComboAddress> data;
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)) {
+ return false;
+ }
+ dr.ids.qname = newName;
+ return true;
+ });
+
luaCtx.registerFunction<bool(DNSResponse::*)()>("restart", [](DNSResponse& dr) {
if (!dr.ids.d_packet) {
return false;
:returns: The trailing data as a null-safe string
+ .. method:: DNSQuestion:rebase(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`.
+ Returns false on failure, true on success.
+
+ :param DNSName newName: The new qname to use
+
.. method:: DNSQuestion:sendTrap(reason)
Send an SNMP trap.
:param string func: The function to call to edit TTLs.
+ .. method:: DNSResponse:rebase(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.
+ 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
+ no longer point to the correct position in the DNS payload. To prevent that, the records are checked against a list of supported record types,
+ and the rewriting will not be performed if an unsupported type is present. As of 1.8.0 the list of supported types is:
+ 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`.
+ Returns false on failure, true on success.
+
+ :param DNSName initialName: The initial qname
+
.. method:: DNSResponse:restart()
.. versionadded:: 1.8.0
receivedQuery.id = query.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
+
+class TestRebase(DNSDistTest):
+ _config_template = """
+ local tagName = 'initial-name'
+ function luarebasequery(dq)
+ dq:setTag(tagName, dq.qname:toString())
+ if not dq:rebase(newDNSName('rebase.advanced.tests.dnsdist.org')) then
+ errlog('Error rebasing the query')
+ return DNSAction.Drop
+ end
+ return DNSAction.None
+ end
+
+ function luarebaseresponse(dr)
+ local initialName = dr:getTag(tagName)
+ if not dr:rebase(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))
+ newServer{address="127.0.0.1:%s"}
+ """
+
+ def testRebase(self):
+ """
+ Advanced: Rebase the query name
+ """
+ name = 'rebase.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
+
+ response = dns.message.make_response(rebasedQuery)
+ rrset = dns.rrset.from_text(rebasedName,
+ 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.',
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.TXT,
+ 'This text contains sub.sub2.rebase.advanced.tests.dnsdist.org.')
+ response.additional.append(rrset)
+
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '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.',
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.TXT,
+ 'This text contains sub.sub2.rebase.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(receivedResponse, expectedResponse)