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

auth lua records: new option to set the http status code to match in ifurlup function #15127

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ htbp
htmlescape
htmlhelp
httpapi
httpcode
httpdomain
hubert
iana
Expand Down
1 change: 1 addition & 0 deletions docs/lua-records/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ Record creation functions
- ``stringmatch``: check ``url`` for this string, only declare 'up' if found
- ``useragent``: Set the HTTP "User-Agent" header in the requests. By default it is set to "PowerDNS Authoritative Server"
- ``byteslimit``: Limit the maximum download size to ``byteslimit`` bytes (default 0 meaning no limit).
- ``httpcode``: Set the HTTP status code to match in response. (default is 200)

An example of a list of address sets:

Expand Down
11 changes: 8 additions & 3 deletions pdns/lua-record.cc
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,12 @@ class IsUpOracle
if (cd.opts.count("byteslimit")) {
byteslimit = static_cast<size_t>(std::atoi(cd.opts.at("byteslimit").c_str()));
}
MiniCurl mc(useragent);
int http_code = 200;
if (cd.opts.count("httpcode") != 0) {
http_code = pdns::checked_stoi<int>(cd.opts.at("httpcode"));
}

MiniCurl minicurl(useragent, false);

string content;
const ComboAddress* rem = nullptr;
Expand All @@ -126,10 +131,10 @@ class IsUpOracle

if (cd.opts.count("source")) {
ComboAddress src(cd.opts.at("source"));
content=mc.getURL(cd.url, rem, &src, timeout, false, false, byteslimit);
content=minicurl.getURL(cd.url, rem, &src, timeout, false, false, byteslimit, http_code);
}
else {
content=mc.getURL(cd.url, rem, nullptr, timeout, false, false, byteslimit);
content=minicurl.getURL(cd.url, rem, nullptr, timeout, false, false, byteslimit, http_code);
}
if (cd.opts.count("stringmatch") && content.find(cd.opts.at("stringmatch")) == string::npos) {
throw std::runtime_error(boost::str(boost::format("unable to match content with `%s`") % cd.opts.at("stringmatch")));
Expand Down
8 changes: 4 additions & 4 deletions pdns/minicurl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void MiniCurl::init()
}
}

MiniCurl::MiniCurl(const string& useragent)
MiniCurl::MiniCurl(const string& useragent, bool failonerror) : d_failonerror(failonerror)
{
#ifdef CURL_STRICTER
d_curl = std::unique_ptr<CURL, decltype(&curl_easy_cleanup)>(curl_easy_init(), curl_easy_cleanup);
Expand Down Expand Up @@ -170,7 +170,7 @@ void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const C

curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_SSL_VERIFYPEER, verify);
curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_SSL_VERIFYHOST, verify ? 2 : 0);
curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_FAILONERROR, true);
curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_FAILONERROR, d_failonerror);
curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_URL, str.c_str());
curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_WRITEDATA, this);
Expand Down Expand Up @@ -198,14 +198,14 @@ void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const C
d_data.clear();
}

std::string MiniCurl::getURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src, int timeout, [[maybe_unused]] bool fastopen, bool verify, size_t byteslimit)
std::string MiniCurl::getURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src, int timeout, [[maybe_unused]] bool fastopen, bool verify, size_t byteslimit, int http_status)
{
setupURL(str, rem, src, timeout, byteslimit, fastopen, verify);
auto res = curl_easy_perform(getCURLPtr(d_curl));
long http_code = 0;
curl_easy_getinfo(getCURLPtr(d_curl), CURLINFO_RESPONSE_CODE, &http_code);

if ((res != CURLE_OK && res != CURLE_ABORTED_BY_CALLBACK) || http_code != 200) {
if ((res != CURLE_OK && res != CURLE_ABORTED_BY_CALLBACK) || http_code != http_status) {
throw std::runtime_error("Unable to retrieve URL ("+std::to_string(http_code)+"): "+string(curl_easy_strerror(res)));
}
std::string ret = d_data;
Expand Down
5 changes: 3 additions & 2 deletions pdns/minicurl.hh
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ public:

static void init();

MiniCurl(const string& useragent="MiniCurl/0.0");
MiniCurl(const string& useragent="MiniCurl/0.0", bool failonerror=true);
~MiniCurl();
MiniCurl& operator=(const MiniCurl&) = delete;

std::string getURL(const std::string& str, const ComboAddress* rem=nullptr, const ComboAddress* src=nullptr, int timeout = 2, bool fastopen = false, bool verify = false, size_t byteslimit = 0);
std::string getURL(const std::string& str, const ComboAddress* rem=nullptr, const ComboAddress* src=nullptr, int timeout = 2, bool fastopen = false, bool verify = false, size_t byteslimit = 0, int http_status = 200);
std::string postURL(const std::string& str, const std::string& postdata, MiniCurlHeaders& headers, int timeout = 2, bool fastopen = false, bool verify = false);

private:
Expand All @@ -70,6 +70,7 @@ private:
std::string d_data;
size_t d_byteslimit{};
bool d_fresh{true};
bool d_failonerror;

void setupURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src, int timeout, size_t byteslimit, bool fastopen, bool verify);
void setHeaders(const MiniCurlHeaders& headers);
Expand Down
32 changes: 32 additions & 0 deletions regression-tests.auth-py/test_LuaRecords.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ class BaseLuaTest(AuthTest):
"{{ '192.168.42.101', '{prefix}.101' }}, "
"{{ stringmatch='pong' }}) ")

usa-404 IN LUA A ( ";include('config') "
"return ifurlup('http://www.lua.org:8080/404', "
"USAips, {{ httpcode='404' }}) ")

ifurlextup IN LUA A "ifurlextup({{{{['192.168.0.1']='http://{prefix}.101:8080/404',['192.168.0.2']='http://{prefix}.102:8080/404'}}, {{['192.168.0.3']='http://{prefix}.101:8080/'}}}})"

nl IN LUA A ( ";include('config') "
Expand Down Expand Up @@ -463,6 +467,34 @@ def testIfurlup(self):
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, reachable_rrs)

def testIfurlupHTTPCode(self):
"""
Basic ifurlup() test, with non-default HTTP code
"""
reachable = [
'{prefix}.103'.format(prefix=self._PREFIX)
]
unreachable = ['192.168.42.105']
ips = reachable + unreachable
all_rrs = []
reachable_rrs = []
for ip in ips:
rr = dns.rrset.from_text('usa-404.example.org.', 0, dns.rdataclass.IN, 'A', ip)
all_rrs.append(rr)
if ip in reachable:
reachable_rrs.append(rr)

query = dns.message.make_query('usa-404.example.org', 'A')
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, all_rrs)

# the timeout in the LUA health checker is 2 second, so we make sure to wait slightly longer here
time.sleep(3)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, reachable_rrs)

def testIfurlupMultiSet(self):
"""
Basic ifurlup() test with mutiple sets
Expand Down