summaryrefslogtreecommitdiffstats
path: root/dnsdist-console.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dnsdist-console.cc256
1 files changed, 150 insertions, 106 deletions
diff --git a/dnsdist-console.cc b/dnsdist-console.cc
index f424c97..3e92b56 100644
--- a/dnsdist-console.cc
+++ b/dnsdist-console.cc
@@ -43,7 +43,7 @@
#include "dolog.hh"
#include "dnsdist.hh"
#include "dnsdist-console.hh"
-#include "sodcrypto.hh"
+#include "dnsdist-crypto.hh"
#include "threadname.hh"
GlobalStateHolder<NetmaskGroup> g_consoleACL;
@@ -58,39 +58,40 @@ static ConcurrentConnectionManager s_connManager(100);
class ConsoleConnection
{
public:
- ConsoleConnection(const ComboAddress& client, FDWrapper&& fd): d_client(client), d_fd(std::move(fd))
+ ConsoleConnection(const ComboAddress& client, FDWrapper&& fileDesc): d_client(client), d_fileDesc(std::move(fileDesc))
{
if (!s_connManager.registerConnection()) {
throw std::runtime_error("Too many concurrent console connections");
}
}
- ConsoleConnection(ConsoleConnection&& rhs): d_client(rhs.d_client), d_fd(std::move(rhs.d_fd))
+ ConsoleConnection(ConsoleConnection&& rhs) noexcept: d_client(rhs.d_client), d_fileDesc(std::move(rhs.d_fileDesc))
{
}
ConsoleConnection(const ConsoleConnection&) = delete;
ConsoleConnection& operator=(const ConsoleConnection&) = delete;
+ ConsoleConnection& operator=(ConsoleConnection&&) = delete;
~ConsoleConnection()
{
- if (d_fd.getHandle() != -1) {
+ if (d_fileDesc.getHandle() != -1) {
s_connManager.releaseConnection();
}
}
- int getFD() const
+ [[nodiscard]] int getFD() const
{
- return d_fd.getHandle();
+ return d_fileDesc.getHandle();
}
- const ComboAddress& getClient() const
+ [[nodiscard]] const ComboAddress& getClient() const
{
return d_client;
}
private:
ComboAddress d_client;
- FDWrapper d_fd;
+ FDWrapper d_fileDesc;
};
void setConsoleMaximumConcurrentConnections(size_t max)
@@ -101,10 +102,11 @@ void setConsoleMaximumConcurrentConnections(size_t max)
// MUST BE CALLED UNDER A LOCK - right now the LuaLock
static void feedConfigDelta(const std::string& line)
{
- if(line.empty())
+ if (line.empty()) {
return;
- struct timeval now;
- gettimeofday(&now, 0);
+ }
+ timeval now{};
+ gettimeofday(&now, nullptr);
g_confDelta.emplace_back(now, line);
}
@@ -113,18 +115,22 @@ static string historyFile(const bool &ignoreHOME = false)
{
string ret;
- struct passwd pwd;
- struct passwd *result;
- char buf[16384];
- getpwuid_r(geteuid(), &pwd, buf, sizeof(buf), &result);
+ passwd pwd{};
+ passwd *result{nullptr};
+ std::array<char, 16384> buf{};
+ getpwuid_r(geteuid(), &pwd, buf.data(), buf.size(), &result);
+ // NOLINTNEXTLINE(concurrency-mt-unsafe): we are not modifying the environment
const char *homedir = getenv("HOME");
- if (result)
+ if (result != nullptr) {
ret = string(pwd.pw_dir);
- if (homedir && !ignoreHOME) // $HOME overrides what the OS tells us
+ }
+ if (homedir != nullptr && !ignoreHOME) { // $HOME overrides what the OS tells us
ret = string(homedir);
- if (ret.empty())
+ }
+ if (ret.empty()) {
ret = "."; // CWD if nothing works..
+ }
ret.append("/.dnsdist_history");
return ret;
}
@@ -136,11 +142,11 @@ enum class ConsoleCommandResult : uint8_t {
TooLarge
};
-static ConsoleCommandResult getMsgLen32(int fd, uint32_t* len)
+static ConsoleCommandResult getMsgLen32(int fileDesc, uint32_t* len)
{
try {
- uint32_t raw;
- size_t ret = readn2(fd, &raw, sizeof(raw));
+ uint32_t raw{0};
+ size_t ret = readn2(fileDesc, &raw, sizeof(raw));
if (ret != sizeof raw) {
return ConsoleCommandResult::ConnectionClosed;
@@ -158,12 +164,12 @@ static ConsoleCommandResult getMsgLen32(int fd, uint32_t* len)
}
}
-static bool putMsgLen32(int fd, uint32_t len)
+static bool putMsgLen32(int fileDesc, uint32_t len)
{
try
{
uint32_t raw = htonl(len);
- size_t ret = writen2(fd, &raw, sizeof raw);
+ size_t ret = writen2(fileDesc, &raw, sizeof raw);
return ret == sizeof raw;
}
catch(...) {
@@ -171,28 +177,28 @@ static bool putMsgLen32(int fd, uint32_t len)
}
}
-static ConsoleCommandResult sendMessageToServer(int fd, const std::string& line, SodiumNonce& readingNonce, SodiumNonce& writingNonce, const bool outputEmptyLine)
+static ConsoleCommandResult sendMessageToServer(int fileDesc, const std::string& line, dnsdist::crypto::authenticated::Nonce& readingNonce, dnsdist::crypto::authenticated::Nonce& writingNonce, const bool outputEmptyLine)
{
- string msg = sodEncryptSym(line, g_consoleKey, writingNonce);
+ string msg = dnsdist::crypto::authenticated::encryptSym(line, g_consoleKey, writingNonce);
const auto msgLen = msg.length();
if (msgLen > std::numeric_limits<uint32_t>::max()) {
cerr << "Encrypted message is too long to be sent to the server, "<< std::to_string(msgLen) << " > " << std::numeric_limits<uint32_t>::max() << endl;
return ConsoleCommandResult::TooLarge;
}
- putMsgLen32(fd, static_cast<uint32_t>(msgLen));
+ putMsgLen32(fileDesc, static_cast<uint32_t>(msgLen));
if (!msg.empty()) {
- writen2(fd, msg);
+ writen2(fileDesc, msg);
}
- uint32_t len;
- auto commandResult = getMsgLen32(fd, &len);
+ uint32_t len{0};
+ auto commandResult = getMsgLen32(fileDesc, &len);
if (commandResult == ConsoleCommandResult::ConnectionClosed) {
cout << "Connection closed by the server." << endl;
return commandResult;
}
- else if (commandResult == ConsoleCommandResult::TooLarge) {
+ if (commandResult == ConsoleCommandResult::TooLarge) {
cerr << "Received a console message whose length (" << len << ") is exceeding the allowed one (" << g_consoleOutputMsgMaxSize << "), closing that connection" << endl;
return commandResult;
}
@@ -207,8 +213,8 @@ static ConsoleCommandResult sendMessageToServer(int fd, const std::string& line,
msg.clear();
msg.resize(len);
- readn2(fd, msg.data(), len);
- msg = sodDecryptSym(msg, g_consoleKey, readingNonce);
+ readn2(fileDesc, msg.data(), len);
+ msg = dnsdist::crypto::authenticated::decryptSym(msg, g_consoleKey, readingNonce);
cout << msg;
cout.flush();
@@ -217,7 +223,7 @@ static ConsoleCommandResult sendMessageToServer(int fd, const std::string& line,
void doClient(ComboAddress server, const std::string& command)
{
- if (!sodIsValidKey(g_consoleKey)) {
+ if (!dnsdist::crypto::authenticated::isValidKey(g_consoleKey)) {
cerr << "The currently configured console key is not valid, please configure a valid key using the setKey() directive" << endl;
return;
}
@@ -226,35 +232,38 @@ void doClient(ComboAddress server, const std::string& command)
cout<<"Connecting to "<<server.toStringWithPort()<<endl;
}
- auto fd = FDWrapper(socket(server.sin4.sin_family, SOCK_STREAM, 0));
- if (fd.getHandle() < 0) {
+ auto fileDesc = FDWrapper(socket(server.sin4.sin_family, SOCK_STREAM, 0));
+ if (fileDesc.getHandle() < 0) {
cerr<<"Unable to connect to "<<server.toStringWithPort()<<endl;
return;
}
- SConnect(fd.getHandle(), server);
- setTCPNoDelay(fd.getHandle());
- SodiumNonce theirs, ours, readingNonce, writingNonce;
+ SConnect(fileDesc.getHandle(), server);
+ setTCPNoDelay(fileDesc.getHandle());
+ dnsdist::crypto::authenticated::Nonce theirs;
+ dnsdist::crypto::authenticated::Nonce ours;
+ dnsdist::crypto::authenticated::Nonce readingNonce;
+ dnsdist::crypto::authenticated::Nonce writingNonce;
ours.init();
- writen2(fd.getHandle(), (const char*)ours.value, sizeof(ours.value));
- readn2(fd.getHandle(), (char*)theirs.value, sizeof(theirs.value));
+ writen2(fileDesc.getHandle(), ours.value.data(), ours.value.size());
+ readn2(fileDesc.getHandle(), theirs.value.data(), theirs.value.size());
readingNonce.merge(ours, theirs);
writingNonce.merge(theirs, ours);
/* try sending an empty message, the server should send an empty
one back. If it closes the connection instead, we are probably
having a key mismatch issue. */
- auto commandResult = sendMessageToServer(fd.getHandle(), "", readingNonce, writingNonce, false);
+ auto commandResult = sendMessageToServer(fileDesc.getHandle(), "", readingNonce, writingNonce, false);
if (commandResult == ConsoleCommandResult::ConnectionClosed) {
cerr<<"The server closed the connection right away, likely indicating a key mismatch. Please check your setKey() directive."<<endl;
return;
}
- else if (commandResult == ConsoleCommandResult::TooLarge) {
+ if (commandResult == ConsoleCommandResult::TooLarge) {
return;
}
if (!command.empty()) {
- sendMessageToServer(fd.getHandle(), command, readingNonce, writingNonce, false);
+ sendMessageToServer(fileDesc.getHandle(), command, readingNonce, writingNonce, false);
return;
}
@@ -272,7 +281,7 @@ void doClient(ComboAddress server, const std::string& command)
for (;;) {
char* sline = readline("> ");
rl_bind_key('\t',rl_complete);
- if (!sline) {
+ if (sline == nullptr) {
break;
}
@@ -283,6 +292,7 @@ void doClient(ComboAddress server, const std::string& command)
history.flush();
}
lastline = line;
+ // NOLINTNEXTLINE(cppcoreguidelines-no-malloc,cppcoreguidelines-owning-memory): readline
free(sline);
if (line == "quit") {
@@ -297,7 +307,7 @@ void doClient(ComboAddress server, const std::string& command)
continue;
}
- commandResult = sendMessageToServer(fd.getHandle(), line, readingNonce, writingNonce, true);
+ commandResult = sendMessageToServer(fileDesc.getHandle(), line, readingNonce, writingNonce, true);
if (commandResult != ConsoleCommandResult::Valid) {
break;
}
@@ -312,7 +322,7 @@ static std::optional<std::string> getNextConsoleLine(ofstream& history, std::str
{
char* sline = readline("> ");
rl_bind_key('\t', rl_complete);
- if (!sline) {
+ if (sline == nullptr) {
return std::nullopt;
}
@@ -324,6 +334,7 @@ static std::optional<std::string> getNextConsoleLine(ofstream& history, std::str
}
lastline = line;
+ // NOLINTNEXTLINE(cppcoreguidelines-no-malloc,cppcoreguidelines-owning-memory): readline
free(sline);
return line;
@@ -390,25 +401,26 @@ void doConsole()
>
>(withReturn ? ("return "+*line) : *line);
if (ret) {
- if (const auto dsValue = boost::get<shared_ptr<DownstreamState>>(&*ret)) {
+ if (const auto* dsValue = boost::get<shared_ptr<DownstreamState>>(&*ret)) {
if (*dsValue) {
cout<<(*dsValue)->getName()<<endl;
}
}
- else if (const auto csValue = boost::get<ClientState*>(&*ret)) {
- if (*csValue) {
+ else if (const auto* csValue = boost::get<ClientState*>(&*ret)) {
+ if (*csValue != nullptr) {
cout<<(*csValue)->local.toStringWithPort()<<endl;
}
}
- else if (const auto strValue = boost::get<string>(&*ret)) {
+ else if (const auto* strValue = boost::get<string>(&*ret)) {
cout<<*strValue<<endl;
}
- else if (const auto um = boost::get<std::unordered_map<string, double> >(&*ret)) {
+ else if (const auto* mapValue = boost::get<std::unordered_map<string, double> >(&*ret)) {
using namespace json11;
- Json::object o;
- for(const auto& v : *um)
- o[v.first]=v.second;
- Json out = o;
+ Json::object obj;
+ for (const auto& value : *mapValue) {
+ obj[value.first] = value.second;
+ }
+ Json out = obj;
cout<<out.dump()<<endl;
}
}
@@ -422,7 +434,8 @@ void doConsole()
}
catch (const LuaContext::SyntaxErrorException&) {
if (withReturn) {
- withReturn=false;
+ withReturn = false;
+ // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto)
goto retry;
}
throw;
@@ -433,7 +446,7 @@ void doConsole()
// tried to return something we don't understand
}
catch (const LuaContext::ExecutionErrorException& e) {
- if (!strcmp(e.what(), "invalid key to 'next'")) {
+ if (strcmp(e.what(), "invalid key to 'next'") == 0) {
std::cerr<<"Error parsing parameters, did you forget parameter name?";
}
else {
@@ -464,19 +477,23 @@ void doConsole()
const std::vector<ConsoleKeyword> g_consoleKeywords{
/* keyword, function, parameters, description */
{ "addACL", true, "netmask", "add to the ACL set who can use this server" },
- { "addAction", true, "DNS rule, DNS action [, {uuid=\"UUID\", name=\"name\"}]", "add a rule" },
+ { "addAction", true, R"(DNS rule, DNS action [, {uuid="UUID", name="name"}])", "add a rule" },
{ "addBPFFilterDynBlocks", true, "addresses, dynbpf[[, seconds=10], msg]", "This is the eBPF equivalent of addDynBlocks(), blocking a set of addresses for (optionally) a number of seconds, using an eBPF dynamic filter" },
{ "addCapabilitiesToRetain", true, "capability or list of capabilities", "Linux capabilities to retain after startup, like CAP_BPF" },
{ "addConsoleACL", true, "netmask", "add a netmask to the console ACL" },
- { "addDNSCryptBind", true, "\"127.0.0.1:8443\", \"provider name\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", {reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters" },
+ { "addDNSCryptBind", true, R"('127.0.0.1:8443", "provider name", "/path/to/resolver.cert", "/path/to/resolver.key", {reusePort=false, tcpFastOpenQueueSize=0, interface="", cpus={}})", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters" },
{ "addDOHLocal", true, "addr, certFile, keyFile [, urls [, vars]]", "listen to incoming DNS over HTTPS queries on the specified address using the specified certificate and key. The last two parameters are tables" },
+ { "addDOH3Local", true, "addr, certFile, keyFile [, vars]", "listen to incoming DNS over HTTP/3 queries on the specified address using the specified certificate and key. The last parameter is a table" },
+ { "addDOQLocal", true, "addr, certFile, keyFile [, vars]", "listen to incoming DNS over QUIC queries on the specified address using the specified certificate and key. The last parameter is a table" },
+ { "addDynamicBlock", true, "address, message[, action [, seconds [, clientIPMask [, clientIPPortMask]]]]", "block the supplied address with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
{ "addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
{ "addDynBlockSMT", true, "names, message[, seconds [, action]]", "block the set of names with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
- { "addLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}]", "add `addr` to the list of addresses we listen on" },
- { "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\", name=\"name\"}}]", "add a cache hit response rule" },
- { "addCacheInsertedResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\", name=\"name\"}}]", "add a cache inserted response rule" },
- { "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\", name=\"name\"}}]", "add a response rule" },
- { "addSelfAnsweredResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\", name=\"name\"}}]", "add a self-answered response rule" },
+ { "addLocal", true, R"(addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface="", cpus={}}])", "add `addr` to the list of addresses we listen on" },
+ { "addCacheHitResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a cache hit response rule" },
+ { "addCacheInsertedResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a cache inserted response rule" },
+ { "addMaintenanceCallback", true, "callback", "register a function to be called as part of the maintenance hook, every second" },
+ { "addResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a response rule" },
+ { "addSelfAnsweredResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a self-answered response rule" },
{ "addTLSLocal", true, "addr, certFile(s), keyFile(s) [,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate (or list of) and key (or list of). The last parameter is a table" },
{ "AllowAction", true, "", "let these packets go through" },
{ "AllowResponseAction", true, "", "let these packets go through" },
@@ -514,16 +531,24 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "exceedServFails", true, "rate, seconds", "get set of addresses that exceed `rate` servfails/s over `seconds` seconds" },
{ "firstAvailable", false, "", "picks the server with the lowest `order` that has not exceeded its QPS limit" },
{ "fixupCase", true, "bool", "if set (default to no), rewrite the first qname of the question part of the answer to match the one from the query. It is only useful when you have a downstream server that messes up the case of the question qname in the answer" },
- { "generateDNSCryptCertificate", true, "\"/path/to/providerPrivate.key\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", serial, validFrom, validUntil", "generate a new resolver private key and related certificate, valid from the `validFrom` timestamp until the `validUntil` one, signed with the provider private key" },
- { "generateDNSCryptProviderKeys", true, "\"/path/to/providerPublic.key\", \"/path/to/providerPrivate.key\"", "generate a new provider keypair" },
+ { "generateDNSCryptCertificate", true, R"("/path/to/providerPrivate.key", "/path/to/resolver.cert", "/path/to/resolver.key", serial, validFrom, validUntil)", "generate a new resolver private key and related certificate, valid from the `validFrom` timestamp until the `validUntil` one, signed with the provider private key" },
+ { "generateDNSCryptProviderKeys", true, R"("/path/to/providerPublic.key", "/path/to/providerPrivate.key")", "generate a new provider keypair" },
{ "getAction", true, "n", "Returns the Action associated with rule n" },
{ "getBind", true, "n", "returns the listener at index n" },
{ "getBindCount", true, "", "returns the number of listeners all kinds" },
+ { "getCacheHitResponseRule", true, "selector", "Return the cache-hit response rule corresponding to the selector, if any" },
+ { "getCacheInsertedResponseRule", true, "selector", "Return the cache-inserted response rule corresponding to the selector, if any" },
{ "getCurrentTime", true, "", "returns the current time" },
+ { "getDynamicBlocks", true, "", "returns a table of the current network-based dynamic blocks" },
+ { "getDynamicBlocksSMT", true, "", "returns a table of the current suffix-based dynamic blocks" },
{ "getDNSCryptBind", true, "n", "return the `DNSCryptContext` object corresponding to the bind `n`" },
{ "getDNSCryptBindCount", true, "", "returns the number of DNSCrypt listeners" },
- { "getDOHFrontend", true, "n", "returns the DOH frontend with index n" },
+ { "getDOHFrontend", true, "n", "returns the DoH frontend with index n" },
{ "getDOHFrontendCount", true, "", "returns the number of DoH listeners" },
+ { "getDOH3Frontend", true, "n", "returns the DoH3 frontend with index n" },
+ { "getDOH3FrontendCount", true, "", "returns the number of DoH3 listeners" },
+ { "getDOQFrontend", true, "n", "returns the DoQ frontend with index n" },
+ { "getDOQFrontendCount", true, "", "returns the number of DoQ listeners" },
{ "getListOfAddressesOfNetworkInterface", true, "itf", "returns the list of addresses configured on a given network interface, as strings" },
{ "getListOfNetworkInterfaces", true, "", "returns the list of network interfaces present on the system, as strings" },
{ "getListOfRangesOfNetworkInterface", true, "itf", "returns the list of network ranges configured on a given network interface, as strings" },
@@ -535,7 +560,10 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "getPoolNames", true, "", "returns a table with all the pool names" },
{ "getQueryCounters", true, "[max=10]", "show current buffer of query counters, limited by 'max' if provided" },
{ "getResponseRing", true, "", "return the current content of the response ring" },
+ { "getResponseRule", true, "selector", "Return the response rule corresponding to the selector, if any" },
{ "getRespRing", true, "", "return the qname/rcode content of the response ring" },
+ { "getRule", true, "selector", "Return the rule corresponding to the selector, if any" },
+ { "getSelfAnsweredResponseRule", true, "selector", "Return the self-answered response rule corresponding to the selector, if any" },
{ "getServer", true, "id", "returns server with index 'n' or whose uuid matches if 'id' is an UUID string" },
{ "getServers", true, "", "returns a table with all defined servers" },
{ "getStatisticsCounters", true, "", "returns a map of statistic counters" },
@@ -548,7 +576,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "getTLSFrontend", true, "n", "returns the TLS frontend with index n" },
{ "getTLSFrontendCount", true, "", "returns the number of DoT listeners" },
{ "getVerbose", true, "", "get whether log messages at the verbose level will be logged" },
- { "grepq", true, "Netmask|DNS Name|100ms|{\"::1\", \"powerdns.com\", \"100ms\"} [, n]", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms" },
+ { "grepq", true, R"(Netmask|DNS Name|100ms|{"::1", "powerdns.com", "100ms"} [, n] [,options])", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms" },
{ "hashPassword", true, "password [, workFactor]", "Returns a hashed and salted version of the supplied password, usable with 'setWebserverConfig()'"},
{ "HTTPHeaderRule", true, "name, regex", "matches DoH queries with a HTTP header 'name' whose content matches the regular expression 'regex'"},
{ "HTTPPathRegexRule", true, "regex", "matches DoH queries whose HTTP path matches 'regex'"},
@@ -617,8 +645,8 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, options={}]", "return a new Packet Cache" },
{ "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" },
{ "newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`" },
- { "newRuleAction", true, "DNS rule, DNS action [, {uuid=\"UUID\", name=\"name\"}]", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" },
- { "newServer", true, "{address=\"ip:port\", qps=1000, order=1, weight=10, pool=\"abuse\", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName=\"a.root-servers.net.\", checkType=\"A\", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source=\"address|interface name|address@interface\", sockets=1, reconnectOnUp=false}", "instantiate a server" },
+ { "newRuleAction", true, R"(DNS rule, DNS action [, {uuid="UUID", name="name"}])", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" },
+ { "newServer", true, R"({address="ip:port", qps=1000, order=1, weight=10, pool="abuse", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName="a.root-servers.net.", checkType="A", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source="address|interface name|address@interface", sockets=1, reconnectOnUp=false})", "instantiate a server" },
{ "newServerPolicy", true, "name, function", "create a policy object from a Lua function" },
{ "newSuffixMatchNode", true, "", "returns a new SuffixMatchNode" },
{ "newSVCRecordParameters", true, "priority, target, mandatoryParams, alpns, noDefaultAlpn [, port [, ech [, ipv4hints [, ipv6hints [, additionalParameters ]]]]]", "return a new SVCRecordParameters object, to use with SpoofSVCAction" },
@@ -630,7 +658,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "PoolAction", true, "poolname [, stop]", "set the packet into the specified pool" },
{ "PoolAvailableRule", true, "poolname", "Check whether a pool has any servers available to handle queries" },
{ "PoolOutstandingRule", true, "poolname, limit", "Check whether a pool has outstanding queries above limit" },
- { "printDNSCryptProviderFingerprint", true, "\"/path/to/providerPublic.key\"", "display the fingerprint of the provided resolver public key" },
+ { "printDNSCryptProviderFingerprint", true, R"("/path/to/providerPublic.key")", "display the fingerprint of the provided resolver public key" },
{ "ProbaRule", true, "probability", "Matches queries with a given probability. 1.0 means always" },
{ "ProxyProtocolValueRule", true, "type [, value]", "matches queries with a specified Proxy Protocol TLV value of that type, optionally matching the content of the option as well" },
{ "QClassRule", true, "qclass", "Matches queries with the specified qclass. class can be specified as an integer or as one of the built-in DNSClass" },
@@ -683,7 +711,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "setECSSourcePrefixV4", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv4 queries" },
{ "setECSSourcePrefixV6", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv6 queries" },
{ "setKey", true, "key", "set access key to that key" },
- { "setLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}]", "reset the list of addresses we listen on to this address" },
+ { "setLocal", true, R"(addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface="", cpus={}}])", "reset the list of addresses we listen on to this address" },
{ "setMaxCachedDoHConnectionsPerDownstream", true, "max", "Set the maximum number of inactive DoH connections to a backend cached by each worker DoH thread" },
{ "setMaxCachedTCPConnectionsPerDownstream", true, "max", "Set the maximum number of inactive TCP connections to a backend cached by each worker TCP thread" },
{ "setMaxTCPClientThreads", true, "n", "set the maximum of TCP client threads, handling TCP connections" },
@@ -717,6 +745,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "setServerPolicyLuaFFIPerThread", true, "name, code", "set server selection policy to one named 'name' and returned by the Lua FFI code passed in 'code'" },
{ "setServFailWhenNoServer", true, "bool", "if set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query" },
{ "setStaleCacheEntriesTTL", true, "n", "allows using cache entries expired for at most n seconds when there is no backend available to answer for a query" },
+ { "setStructuredLogging", true, "value [, options]", "set whether log messages should be in structured-logging-like format" },
{ "setSyslogFacility", true, "facility", "set the syslog logging facility to 'facility'. Defaults to LOG_DAEMON" },
{ "setTCPDownstreamCleanupInterval", true, "interval", "minimum interval in seconds between two cleanups of the idle TCP downstream connections" },
{ "setTCPFastOpenKey", true, "string", "TCP Fast Open Key" },
@@ -740,7 +769,9 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "showConsoleACL", true, "", "show our current console ACL set" },
{ "showDNSCryptBinds", true, "", "display the currently configured DNSCrypt binds" },
{ "showDOHFrontends", true, "", "list all the available DOH frontends" },
+ { "showDOH3Frontends", true, "", "list all the available DOH3 frontends" },
{ "showDOHResponseCodes", true, "", "show the HTTP response code statistics for the DoH frontends"},
+ { "showDOQFrontends", true, "", "list all the available DOQ frontends" },
{ "showDynBlocks", true, "", "show dynamic blocks in force" },
{ "showPools", true, "", "show the available pools" },
{ "showPoolServerPolicy", true, "pool", "show server selection policy for this pool" },
@@ -766,6 +797,8 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "SetECSPrefixLengthAction", true, "v4, v6", "Set the ECS prefix length. Subsequent rules are processed after this action" },
{ "SetMacAddrAction", true, "option", "Add the source MAC address to the query as EDNS0 option option. This action is currently only supported on Linux. Subsequent rules are processed after this action" },
{ "SetEDNSOptionAction", true, "option, data", "Add arbitrary EDNS option and data to the query. Subsequent rules are processed after this action" },
+ { "SetExtendedDNSErrorAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to the response corresponding to the current query. Subsequent rules are processed after this action" },
+ { "SetExtendedDNSErrorResponseAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to this response. Subsequent rules are processed after this action" },
{ "SetNoRecurseAction", true, "", "strip RD bit from the question, let it go through" },
{ "setOutgoingDoHWorkerThreads", true, "n", "Number of outgoing DoH worker threads" },
{ "SetProxyProtocolValuesAction", true, "values", "Set the Proxy-Protocol values for this queries to 'values'" },
@@ -785,6 +818,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
{ "TagRule", true, "name [, value]", "matches if the tag named 'name' is present, with the given 'value' matching if any" },
{ "TCAction", true, "", "create answer to query with TC and RD bits set, to move to TCP" },
{ "TCPRule", true, "[tcp]", "Matches question received over TCP if tcp is true, over UDP otherwise" },
+ { "TCResponseAction", true, "", "truncate a response" },
{ "TeeAction", true, "remote [, addECS [, local]]", "send copy of query to remote, optionally adding ECS info, optionally set local address" },
{ "testCrypto", true, "", "test of the crypto all works" },
{ "TimedIPSetRule", true, "", "Create a rule which matches a set of IP addresses which expire"},
@@ -811,17 +845,17 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
extern "C" {
static char* my_generator(const char* text, int state)
{
- string t(text);
+ string textStr(text);
/* to keep it readable, we try to keep only 4 keywords per line
and to start a new line when the first letter changes */
static int s_counter = 0;
- int counter=0;
- if (!state) {
+ int counter = 0;
+ if (state == 0) {
s_counter = 0;
}
for (const auto& keyword : g_consoleKeywords) {
- if (boost::starts_with(keyword.name, t) && counter++ == s_counter) {
+ if (boost::starts_with(keyword.name, textStr) && counter++ == s_counter) {
std::string value(keyword.name);
s_counter++;
if (keyword.function) {
@@ -833,14 +867,15 @@ static char* my_generator(const char* text, int state)
return strdup(value.c_str());
}
}
- return 0;
+ return nullptr;
}
char** my_completion( const char * text , int start, int end)
{
- char **matches=0;
+ char **matches = nullptr;
if (start == 0) {
- matches = rl_completion_matches ((char*)text, &my_generator);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): readline
+ matches = rl_completion_matches (const_cast<char*>(text), &my_generator);
}
// skip default filename completion.
@@ -859,15 +894,18 @@ static void controlClientThread(ConsoleConnection&& conn)
setTCPNoDelay(conn.getFD());
- SodiumNonce theirs, ours, readingNonce, writingNonce;
+ dnsdist::crypto::authenticated::Nonce theirs;
+ dnsdist::crypto::authenticated::Nonce ours;
+ dnsdist::crypto::authenticated::Nonce readingNonce;
+ dnsdist::crypto::authenticated::Nonce writingNonce;
ours.init();
- readn2(conn.getFD(), (char*)theirs.value, sizeof(theirs.value));
- writen2(conn.getFD(), (char*)ours.value, sizeof(ours.value));
+ readn2(conn.getFD(), theirs.value.data(), theirs.value.size());
+ writen2(conn.getFD(), ours.value.data(), ours.value.size());
readingNonce.merge(ours, theirs);
writingNonce.merge(theirs, ours);
for (;;) {
- uint32_t len;
+ uint32_t len{0};
if (getMsgLen32(conn.getFD(), &len) != ConsoleCommandResult::Valid) {
break;
}
@@ -883,7 +921,7 @@ static void controlClientThread(ConsoleConnection&& conn)
line.resize(len);
readn2(conn.getFD(), line.data(), len);
- line = sodDecryptSym(line, g_consoleKey, readingNonce);
+ line = dnsdist::crypto::authenticated::decryptSym(line, g_consoleKey, readingNonce);
string response;
try {
@@ -906,30 +944,30 @@ static void controlClientThread(ConsoleConnection&& conn)
>(withReturn ? ("return "+line) : line);
if (ret) {
- if (const auto dsValue = boost::get<shared_ptr<DownstreamState>>(&*ret)) {
+ if (const auto* dsValue = boost::get<shared_ptr<DownstreamState>>(&*ret)) {
if (*dsValue) {
response = (*dsValue)->getName()+"\n";
} else {
response = "";
}
}
- else if (const auto csValue = boost::get<ClientState*>(&*ret)) {
- if (*csValue) {
+ else if (const auto* csValue = boost::get<ClientState*>(&*ret)) {
+ if (*csValue != nullptr) {
response = (*csValue)->local.toStringWithPort()+"\n";
} else {
response = "";
}
}
- else if (const auto strValue = boost::get<string>(&*ret)) {
+ else if (const auto* strValue = boost::get<string>(&*ret)) {
response = *strValue+"\n";
}
- else if (const auto um = boost::get<std::unordered_map<string, double> >(&*ret)) {
+ else if (const auto* mapValue = boost::get<std::unordered_map<string, double> >(&*ret)) {
using namespace json11;
- Json::object o;
- for(const auto& v : *um) {
- o[v.first] = v.second;
+ Json::object obj;
+ for (const auto& value : *mapValue) {
+ obj[value.first] = value.second;
}
- Json out = o;
+ Json out = obj;
response = out.dump()+"\n";
}
}
@@ -941,8 +979,9 @@ static void controlClientThread(ConsoleConnection&& conn)
}
}
catch (const LuaContext::SyntaxErrorException&) {
- if(withReturn) {
- withReturn=false;
+ if (withReturn) {
+ withReturn = false;
+ // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto)
goto retry;
}
throw;
@@ -953,7 +992,7 @@ static void controlClientThread(ConsoleConnection&& conn)
// tried to return something we don't understand
}
catch (const LuaContext::ExecutionErrorException& e) {
- if (!strcmp(e.what(),"invalid key to 'next'")) {
+ if (strcmp(e.what(),"invalid key to 'next'") == 0) {
response = "Error: Parsing function parameters, did you forget parameter name?";
}
else {
@@ -974,7 +1013,7 @@ static void controlClientThread(ConsoleConnection&& conn)
catch (const LuaContext::SyntaxErrorException& e) {
response = "Error: " + string(e.what()) + ": ";
}
- response = sodEncryptSym(response, g_consoleKey, writingNonce);
+ response = dnsdist::crypto::authenticated::encryptSym(response, g_consoleKey, writingNonce);
putMsgLen32(conn.getFD(), response.length());
writen2(conn.getFD(), response.c_str(), response.length());
}
@@ -983,25 +1022,30 @@ static void controlClientThread(ConsoleConnection&& conn)
}
}
catch (const std::exception& e) {
- errlog("Got an exception in client connection from %s: %s", conn.getClient().toStringWithPort(), e.what());
+ infolog("Got an exception in client connection from %s: %s", conn.getClient().toStringWithPort(), e.what());
}
}
-void controlThread(int fd, ComboAddress local)
+// NOLINTNEXTLINE(performance-unnecessary-value-param): this is thread
+void controlThread(std::shared_ptr<Socket> acceptFD, ComboAddress local)
{
- FDWrapper acceptFD(fd);
try
{
setThreadName("dnsdist/control");
ComboAddress client;
- int sock;
+ // make sure that the family matches the one from the listening IP,
+ // so that getSocklen() returns the correct size later, otherwise
+ // the first IPv6 console connection might get refused
+ client.sin4.sin_family = local.sin4.sin_family;
+
+ int sock{-1};
auto localACL = g_consoleACL.getLocal();
infolog("Accepting control connections on %s", local.toStringWithPort());
- while ((sock = SAccept(acceptFD.getHandle(), client)) >= 0) {
+ while ((sock = SAccept(acceptFD->getHandle(), client)) >= 0) {
FDWrapper socket(sock);
- if (!sodIsValidKey(g_consoleKey)) {
+ if (!dnsdist::crypto::authenticated::isValidKey(g_consoleKey)) {
vinfolog("Control connection from %s dropped because we don't have a valid key configured, please configure one using setKey()", client.toStringWithPort());
continue;
}
@@ -1017,11 +1061,11 @@ void controlThread(int fd, ComboAddress local)
warnlog("Got control connection from %s", client.toStringWithPort());
}
- std::thread t(controlClientThread, std::move(conn));
- t.detach();
+ std::thread clientThread(controlClientThread, std::move(conn));
+ clientThread.detach();
}
catch (const std::exception& e) {
- errlog("Control connection died: %s", e.what());
+ infolog("Control connection died: %s", e.what());
}
}
}