Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dnsdist: Make custom LuaAction and LuaResponsAction second return value optional #6363

Merged
merged 7 commits into from Mar 20, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 19 additions & 4 deletions pdns/dnsdist-lua-actions.cc
Expand Up @@ -29,6 +29,7 @@
#include "ednsoptions.hh"
#include "fstrm_logger.hh"
#include "remote_logger.hh"
#include "boost/optional/optional_io.hpp"

class DropAction : public DNSAction
{
Expand Down Expand Up @@ -311,8 +312,15 @@ DNSAction::Action LuaAction::operator()(DNSQuestion* dq, string* ruleresult) con
std::lock_guard<std::mutex> lock(g_luamutex);
try {
auto ret = d_func(dq);
if(ruleresult)
*ruleresult=std::get<1>(ret);
if (ruleresult) {
if (boost::optional<string> rule = std::get<1>(ret)) {
*ruleresult = *rule;
}
else {
// default to empty string
ruleresult->clear();
}
}
return (Action)std::get<0>(ret);
} catch (std::exception &e) {
warnlog("LuaAction failed inside lua, returning ServFail: %s", e.what());
Expand All @@ -327,8 +335,15 @@ DNSResponseAction::Action LuaResponseAction::operator()(DNSResponse* dr, string*
std::lock_guard<std::mutex> lock(g_luamutex);
try {
auto ret = d_func(dr);
if(ruleresult)
*ruleresult=std::get<1>(ret);
if(ruleresult) {
if (boost::optional<string> rule = std::get<1>(ret)) {
*ruleresult = *rule;
}
else {
// default to empty string
ruleresult->clear();
}
}
return (Action)std::get<0>(ret);
} catch (std::exception &e) {
warnlog("LuaResponseAction failed inside lua, returning ServFail: %s", e.what());
Expand Down
3 changes: 2 additions & 1 deletion pdns/dnsdist-lua-vars.cc
Expand Up @@ -23,7 +23,7 @@

void setupLuaVars()
{
g_lua.writeVariable("DNSAction", std::unordered_map<string,int>{
g_lua.writeVariable("DNSAction", std::unordered_map<string,int>{
{"Drop", (int)DNSAction::Action::Drop},
{"Nxdomain", (int)DNSAction::Action::Nxdomain},
{"Refused", (int)DNSAction::Action::Refused},
Expand All @@ -40,6 +40,7 @@ void setupLuaVars()
g_lua.writeVariable("DNSResponseAction", std::unordered_map<string,int>{
{"Allow", (int)DNSResponseAction::Action::Allow },
{"Delay", (int)DNSResponseAction::Action::Delay },
{"Drop", (int)DNSResponseAction::Action::Drop },
{"HeaderModify", (int)DNSResponseAction::Action::HeaderModify },
{"ServFail", (int)DNSResponseAction::Action::ServFail },
{"None", (int)DNSResponseAction::Action::None }
Expand Down
4 changes: 2 additions & 2 deletions pdns/dnsdist-lua.hh
Expand Up @@ -24,7 +24,7 @@
class LuaAction : public DNSAction
{
public:
typedef std::function<std::tuple<int, string>(DNSQuestion* dq)> func_t;
typedef std::function<std::tuple<int, boost::optional<string> >(DNSQuestion* dr)> func_t;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a nit but dq made more sense than dr here :)

LuaAction(LuaAction::func_t func) : d_func(func)
{}
Action operator()(DNSQuestion* dq, string* ruleresult) const override;
Expand All @@ -39,7 +39,7 @@ private:
class LuaResponseAction : public DNSResponseAction
{
public:
typedef std::function<std::tuple<int, string>(DNSResponse* dr)> func_t;
typedef std::function<std::tuple<int, boost::optional<string> >(DNSResponse* dr)> func_t;
LuaResponseAction(LuaResponseAction::func_t func) : d_func(func)
{}
Action operator()(DNSResponse* dr, string* ruleresult) const override;
Expand Down
19 changes: 18 additions & 1 deletion pdns/dnsdistdist/docs/reference/constants.rst
Expand Up @@ -73,7 +73,7 @@ DNS Section
DNSAction
---------

These constants represent an Action that can be returned from the functions invoked by :func:`addLuaAction` and :func:`addLuaResponseAction`.
These constants represent an Action that can be returned from the functions invoked by :func:`addLuaAction`.

* ``DNSAction.Allow``: let the query pass, skipping other rules
* ``DNSAction.Delay``: delay the response for the specified milliseconds (UDP-only), continue to the next rule
Expand All @@ -83,4 +83,21 @@ These constants represent an Action that can be returned from the functions invo
* ``DNSAction.Nxdomain``: return a response with a NXDomain rcode
* ``DNSAction.Pool``: use the specified pool to forward this query
* ``DNSAction.Refused``: return a response with a Refused rcode
* ``DNSAction.ServFail``: return a response with a ServFail rcode
* ``DNSAction.Spoof``: spoof the response using the supplied IPv4 (A), IPv6 (AAAA) or string (CNAME) value
* ``DNSAction.Truncate``: truncate the response


.. _DNSResponseAction:

DNSResponseAction
-----------------

These constants represent an Action that can be returned from the functions invoked by :func:`addLuaResponseAction`.

* ``DNSResponseAction.Allow``: let the query pass, skipping other rules
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/query/response/

* ``DNSResponseAction.Delay``: delay the response for the specified milliseconds (UDP-only), continue to the next rule
* ``DNSResponseAction.Drop``: drop the query
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/query/response/

* ``DNSResponseAction.HeaderModify``: indicate that the query has been turned into a response
* ``DNSResponseAction.None``: continue to the next rule
* ``DNSResponseAction.ServFail``: return a response with a ServFail rcode
22 changes: 20 additions & 2 deletions pdns/dnsdistdist/docs/rules-actions.rst
Expand Up @@ -150,7 +150,9 @@ Rule Generators

Invoke a Lua function that accepts a :class:`DNSQuestion`.
This function works similar to using :func:`LuaAction`.
The ``function`` should return a :ref:`DNSAction`. If the Lua code fails, ServFail is returned.
The ``function`` should return both a :ref:`DNSAction` and its argument `rule`. The `rule` is used as an argument
of the following :ref:`DNSAction`: `DNSAction.Spoof`, `DNSAction.Pool` and `DNSAction.Delay`. As of version `1.3.0`, you can
ommit the argument. For earlier releases, simply return an empty string. If the Lua code fails, ServFail is returned.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/ommit/omit/


:param DNSRule: match queries based on this rule
:param string function: the name of a Lua function
Expand All @@ -160,14 +162,30 @@ Rule Generators

* ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule.

::

function luarule(dq)
if(dq.qtype==35) -- NAPTR
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't dnsdist.NAPTR work here?

then
return DNSAction.Pool, "abuse" -- send to abuse pool
else
return DNSAction.None, "" -- no action
-- return DNSAction.None -- as of dnsdist version 1.3.0
end
end

addLuaAction(AllRule(), luarule)

.. function:: addLuaResponseAction(DNSrule, function [, options])

.. versionchanged:: 1.3.0
Added the optional parameter ``options``.

Invoke a Lua function that accepts a :class:`DNSResponse`.
This function works similar to using :func:`LuaResponseAction`.
The ``function`` should return a :ref:`DNSResponseAction`. If the Lua code fails, ServFail is returned.
The ``function`` should return both a :ref:`DNSResponseAction` and its argument `rule`. The `rule` is used as an argument
of the `DNSResponseAction.Delay`. As of version `1.3.0`, you can ommit the argument (see :func:`addLuaAction`). For earlier
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/ommit/omit/

releases, simply return an empty string. If the Lua code fails, ServFail is returned.

:param DNSRule: match queries based on this rule
:param string function: the name of a Lua function
Expand Down
7 changes: 7 additions & 0 deletions regression-tests.dnsdist/.gitignore
Expand Up @@ -9,3 +9,10 @@ DNSCryptResolver*
dnsdist.log
/*_pb2.py
/__pycache__/
ca.key
ca.pem
ca.srl
server.chain
server.csr
server.key
server.pem
36 changes: 36 additions & 0 deletions regression-tests.dnsdist/test_Advanced.py
Expand Up @@ -1279,6 +1279,42 @@ def testRefusedViaLua(self):
refusedResponse.id = receivedResponse.id
self.assertEquals(receivedResponse, refusedResponse)

class TestAdvancedLuaActionReturnSyntax(DNSDistTest):

_config_template = """
function refuse(dq)
return DNSAction.Refused
end
addAction(AllRule(), LuaAction(refuse))
newServer{address="127.0.0.1:%s"}
"""

def testRefusedWithEmptyRule(self):
"""
Advanced: Short syntax for LuaAction return values
"""
name = 'refused.advanced.tests.powerdns.com.'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind using a different qname than the previous test? It often makes debugging easier.

query = dns.message.make_query(name, 'A', 'IN')
response = dns.message.make_response(query)
rrset = dns.rrset.from_text(name,
3600,
dns.rdataclass.IN,
dns.rdatatype.AAAA,
'::1')
response.answer.append(rrset)
refusedResponse = dns.message.make_response(query)
refusedResponse.set_rcode(dns.rcode.REFUSED)

(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertTrue(receivedResponse)
refusedResponse.id = receivedResponse.id
self.assertEquals(receivedResponse, refusedResponse)

(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertTrue(receivedResponse)
refusedResponse.id = receivedResponse.id
self.assertEquals(receivedResponse, refusedResponse)

class TestAdvancedLuaTruncated(DNSDistTest):

_config_template = """
Expand Down
57 changes: 57 additions & 0 deletions regression-tests.dnsdist/test_Responses.py
Expand Up @@ -249,3 +249,60 @@ def testTTLEdited(self):
self.assertEquals(response, receivedResponse)
self.assertNotEquals(response.answer[0].ttl, receivedResponse.answer[0].ttl)
self.assertEquals(receivedResponse.answer[0].ttl, self._ttl)

class TestResponseLuaActionReturnSyntax(DNSDistTest):

_config_template = """
newServer{address="127.0.0.1:%s"}
function customDelay(dr)
return DNSResponseAction.Delay, "1000"
end
function customDrop(dr)
return DNSResponseAction.Drop
end
addResponseAction("drop.responses.tests.powerdns.com.", LuaResponseAction(customDrop))
addResponseAction(RCodeRule(dnsdist.NXDOMAIN), LuaResponseAction(customDelay))
"""

def testResponseActionDelayed(self):
"""
Responses: Delayed via LuaResponseAction

Send an A query to "delayed.responses.tests.powerdns.com.",
check that the response delay is longer than 1000 ms
for a NXDomain response over UDP, shorter for a NoError one.
"""
name = 'delayed.responses.tests.powerdns.com.'
query = dns.message.make_query(name, 'A', 'IN')
response = dns.message.make_response(query)

# NX over UDP
response.set_rcode(dns.rcode.NXDOMAIN)
begin = datetime.now()
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
end = datetime.now()
receivedQuery.id = query.id
self.assertEquals(query, receivedQuery)
self.assertEquals(response, receivedResponse)
self.assertTrue((end - begin) > timedelta(0, 1))

def testDropped(self):
"""
Responses: Dropped via user defined LuaResponseAction

Send an A query to "drop.responses.tests.powerdns.com.",
check that the response (not the query) is dropped.
"""
name = 'drop.responses.tests.powerdns.com.'
query = dns.message.make_query(name, 'A', 'IN')
response = dns.message.make_response(query)

(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
receivedQuery.id = query.id
self.assertEquals(query, receivedQuery)
self.assertEquals(receivedResponse, None)

(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
receivedQuery.id = query.id
self.assertEquals(query, receivedQuery)
self.assertEquals(receivedResponse, None)