});
}
else if (!rawResponses.empty()) {
+ if (qtype == QType::ANY && d_rawTypeForAny) {
+ qtype = *d_rawTypeForAny;
+ }
qtype = htons(qtype);
for(const auto& rawResponse : rawResponses){
uint16_t rdataLen = htons(rawResponse.size());
luaCtx.writeFunction("SpoofRawAction", [](LuaTypeOrArrayOf<std::string> inp, boost::optional<responseParams_t> vars) {
vector<string> raws;
- if(auto s = boost::get<std::string>(&inp)) {
- raws.push_back(*s);
+ if (auto str = boost::get<std::string>(&inp)) {
+ raws.push_back(*str);
} else {
- const auto& v = boost::get<LuaArray<std::string>>(inp);
- for(const auto& raw: v) {
+ const auto& vect = boost::get<LuaArray<std::string>>(inp);
+ for(const auto& raw: vect) {
raws.push_back(raw.second);
}
}
-
- auto ret = std::shared_ptr<DNSAction>(new SpoofAction(raws));
+ uint32_t qtypeForAny{0};
+ getOptionalValue<uint32_t>(vars, "typeForAny", qtypeForAny);
+ if (qtypeForAny > std::numeric_limits<uint16_t>::max()) {
+ qtypeForAny = 0;
+ }
+ auto ret = std::shared_ptr<DNSAction>(new SpoofAction(raws, qtypeForAny > 0 ? static_cast<uint16_t>(qtypeForAny) : std::optional<uint16_t>()));
auto sa = std::dynamic_pointer_cast<SpoofAction>(ret);
parseResponseConfig(vars, sa->d_responseConfig);
checkAllParametersConsumed("SpoofRawAction", vars);
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) {
+ luaCtx.registerFunction<void(DNSQuestion::*)(const boost::variant<LuaArray<ComboAddress>, LuaArray<std::string>>&, boost::optional<uint16_t>)>("spoof", [](DNSQuestion& dq, const boost::variant<LuaArray<ComboAddress>, LuaArray<std::string>>& response, boost::optional<uint16_t> typeForAny) {
if (response.type() == typeid(LuaArray<ComboAddress>)) {
std::vector<ComboAddress> data;
auto responses = boost::get<LuaArray<ComboAddress>>(response);
data.push_back(resp.second);
}
std::string result;
- SpoofAction sa(data);
+ SpoofAction sa(data, typeForAny ? *typeForAny : std::optional<uint16_t>());
sa(&dq, &result);
return;
}
{
}
- SpoofAction(const vector<std::string>& raws): d_rawResponses(raws)
+ SpoofAction(const vector<std::string>& raws, std::optional<uint16_t> typeForAny): d_rawResponses(raws), d_rawTypeForAny(typeForAny)
{
}
std::vector<std::string> d_rawResponses;
PacketBuffer d_raw;
DNSName d_cname;
+ std::optional<uint16_t> d_rawTypeForAny{};
};
class LimitTTLResponseAction : public DNSResponseAction, public boost::noncopyable
if (raw) {
std::vector<std::string> raws;
stringtok(raws, spoofContent, ",");
- SpoofAction sa(raws);
+ SpoofAction sa(raws, std::nullopt);
sa(&dq, &result);
}
else {
}
std::string result;
- SpoofAction sa(data);
+ SpoofAction sa(data, std::nullopt);
sa(dq->dq, &result);
}
:param string tail: The new data
:returns: true if the operation succeeded, false otherwise
- .. method:: DNSQuestion:spoof(ip|ips|raw|raws)
+ .. method:: DNSQuestion:spoof(ip|ips|raw|raws [, typeForAny])
.. versionadded:: 1.6.0
+ .. versionchanged:: 1.9.0
+ Optional parameter ``typeForAny`` added.
+
Forge a response with the specified record data as raw bytes. If you specify list of raws (it is assumed they match the query type), all will get spoofed in.
:param ComboAddress ip: The `ComboAddress` to be spoofed, e.g. `newCA("192.0.2.1")`.
:param table ComboAddresses ips: The `ComboAddress`es to be spoofed, e.g. `{ newCA("192.0.2.1"), newCA("192.0.2.2") }`.
:param string raw: The raw string to be spoofed, e.g. `"\\192\\000\\002\\001"`.
:param table raws: The raw strings to be spoofed, e.g. `{ "\\192\\000\\002\\001", "\\192\\000\\002\\002" }`.
+ :param int typeForAny: The type to use for raw responses when the requested type is ``ANY``, as using ``ANY` for the type of the response record would not make sense.
.. method:: DNSQuestion:suspend(asyncID, queryID, timeoutMS) -> bool
.. versionchanged:: 1.6.0
Up to 1.6.0, it was only possible to spoof one answer.
+ .. versionchanged:: 1.9.0
+ Added the optional parameter ``typeForAny``.
+
Forge a response with the specified raw bytes as record data.
.. code-block:: Lua
addAction(AndRule({QNameRule('raw-srv.powerdns.com.'), QTypeRule(DNSQType.SRV)}), SpoofRawAction("\000\000\000\000\255\255\003srv\008powerdns\003com\000", { aa=true, ttl=3600 }))
-- select reverse queries for '127.0.0.1' and answer with 'localhost'
addAction(AndRule({QNameRule('1.0.0.127.in-addr.arpa.'), QTypeRule(DNSQType.PTR)}), SpoofRawAction("\009localhost\000"))
+ -- rfc8482: Providing Minimal-Sized Responses to DNS Queries That Have QTYPE=ANY via HINFO of value "rfc8482"
+ addAction(QTypeRule(DNSQType.ANY), SpoofRawAction("\007rfc\056\052\056\050\000", { typeForAny=DNSQType.HINFO }))
:func:`DNSName:toDNSString` is convenient for converting names to wire format for passing to ``SpoofRawAction``.
* ``ad``: bool - Set the AD bit to this value (true means the bit is set, false means it's cleared). Default is to clear it.
* ``ra``: bool - Set the RA bit to this value (true means the bit is set, false means it's cleared). Default is to copy the value of the RD bit from the incoming query.
* ``ttl``: int - The TTL of the record.
+ * ``typeForAny``: int - The record type to use when responding to queries of type ``ANY``, as using ``ANY`` for the type of the response record would not make sense.
.. function:: SpoofSVCAction(svcParams [, options])
addAction(AndRule{SuffixMatchNodeRule("rawchaos.spoofing.tests.powerdns.com"), QTypeRule(DNSQType.TXT), QClassRule(DNSClass.CHAOS)}, SpoofRawAction("\\005chaos"))
addAction(AndRule{SuffixMatchNodeRule("multiraw.spoofing.tests.powerdns.com"), QTypeRule(DNSQType.TXT)}, SpoofRawAction({"\\003aaa\\004bbbb", "\\011ccccccccccc"}))
addAction(AndRule{SuffixMatchNodeRule("multiraw.spoofing.tests.powerdns.com"), QTypeRule(DNSQType.A)}, SpoofRawAction({"\\192\\000\\002\\001", "\\192\\000\\002\\002"}))
+ -- rfc8482
+ addAction(AndRule{SuffixMatchNodeRule("raw-any.spoofing.tests.powerdns.com"), QTypeRule(DNSQType.ANY)}, SpoofRawAction("\\007rfc\\056\\052\\056\\050\\000", { typeForAny=DNSQType.HINFO }))
newServer{address="127.0.0.1:%s"}
"""
self.assertEqual(expectedResponse, receivedResponse)
self.assertEqual(receivedResponse.answer[0].ttl, 60)
+ def testSpoofRawANYAction(self):
+ """
+ Spoofing: Spoof a HINFO response for ANY queries
+ """
+ name = 'raw-any.spoofing.tests.powerdns.com.'
+
+ query = dns.message.make_query(name, 'ANY', 'IN')
+ query.flags &= ~dns.flags.RD
+ expectedResponse = dns.message.make_response(query)
+ expectedResponse.flags &= ~dns.flags.AA
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.HINFO,
+ '"rfc8482" ""')
+ expectedResponse.answer.append(rrset)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (_, receivedResponse) = sender(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ self.assertEqual(expectedResponse, receivedResponse)
+ self.assertEqual(receivedResponse.answer[0].ttl, 60)
def testSpoofRawActionMulti(self):
"""