Rijsdijk
ringbuffer
rkey
+rmem
rname
rng
rocommunity
YYYYMMD
YYYYMMDD
YYYYMMDDSS
+wmem
Zealey
zeha
Zengers
changelog_sections = ['New Features', 'Removed Features', 'Improvements', 'Bug Fixes']
changelog_inner_tag_sort = ['Internals', 'API', 'Tools', 'ALIAS', 'DNSUpdate', 'BIND', 'MySQL', 'Postgresql', 'LDAP', 'GeoIP', 'Remote']
-changelog_render_tags = False
+changelog_hide_tags_in_entry = True
# -- Options for HTML output ----------------------------------------------
Sphinx>=1.5.0,!=1.8.0,<2.0
git+https://github.com/pieterlexis/sphinxcontrib-openapi@use-jsondomain-pdns
git+https://github.com/pieterlexis/sphinx-jsondomain@no-type-links
-git+https://github.com/pieterlexis/sphinx-changelog@render-tags
+changelog>=0.5.6,<0.6
sphinxcontrib-fulltoc
guzzle_sphinx_theme
docutils!=0.15,<0.18
return 0;
}
-static void setSocketBuffer(int fd, int optname, uint32_t size)
-{
- uint32_t psize=0;
- socklen_t len=sizeof(psize);
-
- if(!getsockopt(fd, SOL_SOCKET, optname, (char*)&psize, &len) && psize > size) {
- if (!g_quiet) {
- cerr<<"Not decreasing socket buffer size from "<<psize<<" to "<<size<<endl;
- }
- return;
- }
-
- if (setsockopt(fd, SOL_SOCKET, optname, (char*)&size, sizeof(size)) < 0 ) {
- if (!g_quiet) {
- cerr<<"Warning: unable to raise socket buffer size to "<<size<<": "<<stringerror()<<endl;
- }
- }
-}
-
-
-static void setSocketReceiveBuffer(int fd, uint32_t size)
-{
- setSocketBuffer(fd, SO_RCVBUF, size);
-}
-
-static void setSocketSendBuffer(int fd, uint32_t size)
-{
- setSocketBuffer(fd, SO_SNDBUF, size);
-}
-
static ComboAddress getRandomAddressFromRange(const Netmask& ecsRange)
{
ComboAddress result = ecsRange.getMaskedNetwork();
for(int i=0; i < 24; ++i) {
auto sock = make_unique<Socket>(dest.sin4.sin_family, SOCK_DGRAM);
// sock->connect(dest);
- setSocketSendBuffer(sock->getHandle(), 2000000);
- setSocketReceiveBuffer(sock->getHandle(), 2000000);
+ try {
+ setSocketSendBuffer(sock->getHandle(), 2000000);
+ }
+ catch (const std::exception& e) {
+ if (!g_quiet) {
+ cerr<<e.what()<<endl;
+ }
+ }
+ try {
+ setSocketReceiveBuffer(sock->getHandle(), 2000000);
+ }
+ catch (const std::exception& e) {
+ if (!g_quiet) {
+ cerr<<e.what()<<endl;
+ }
+ }
+
sockets.push_back(std::move(sock));
}
new thread(recvThread, &sockets);
{ "setTCPRecvTimeout", true, "n", "set the read timeout on TCP connections from the client, in seconds" },
{ "setTCPSendTimeout", true, "n", "set the write timeout on TCP connections from the client, in seconds" },
{ "setUDPMultipleMessagesVectorSize", true, "n", "set the size of the vector passed to recvmmsg() to receive UDP messages. Default to 1 which means that the feature is disabled and recvmsg() is used instead" },
+ { "setUDPSocketBufferSizes", true, "recv, send", "Set the size of the receive (SO_RCVBUF) and send (SO_SNDBUF) buffers for incoming UDP sockets" },
{ "setUDPTimeout", true, "n", "set the maximum time dnsdist will wait for a response from a backend over UDP, in seconds" },
{ "setVerboseHealthChecks", true, "bool", "set whether health check errors will be logged" },
{ "setWebserverConfig", true, "[{password=string, apiKey=string, customHeaders, statsRequireAuthentication}]", "Updates webserver configuration" },
static void checkParameterBound(const std::string& parameter, uint64_t value, size_t max = std::numeric_limits<uint16_t>::max())
{
- if (value > std::numeric_limits<uint16_t>::max()) {
- throw std::runtime_error("The value passed to " + parameter + " is too large, the maximum is " + std::to_string(max));
+ if (value > max) {
+ throw std::runtime_error("The value (" + std::to_string(value) + ") passed to " + parameter + " is too large, the maximum is " + std::to_string(max));
}
}
}
}
});
+
+ luaCtx.writeFunction("setUDPSocketBufferSizes", [client](uint64_t recv, uint64_t snd) {
+ if (client) {
+ return;
+ }
+ checkParameterBound("setUDPSocketBufferSizes", recv, std::numeric_limits<uint32_t>::max());
+ checkParameterBound("setUDPSocketBufferSizes", snd, std::numeric_limits<uint32_t>::max());
+ setLuaSideEffect();
+
+ if (g_configurationDone) {
+ g_outputBuffer = "setUDPSocketBufferSizes cannot be used at runtime!\n";
+ return;
+ }
+
+ g_socketUDPSendBuffer = snd;
+ g_socketUDPRecvBuffer = recv;
+ });
}
vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool configCheck, const std::string& config)
bool g_truncateTC{false};
bool g_fixupCase{false};
bool g_dropEmptyQueries{false};
+uint32_t g_socketUDPSendBuffer{0};
+uint32_t g_socketUDPRecvBuffer{0};
std::set<std::string> g_capabilitiesToRetain;
}
}
+ if (!cs->tcp) {
+ if (g_socketUDPSendBuffer > 0) {
+ try {
+ setSocketSendBuffer(cs->udpFD, g_socketUDPSendBuffer);
+ }
+ catch (const std::exception& e) {
+ warnlog(e.what());
+ }
+ }
+
+ if (g_socketUDPRecvBuffer > 0) {
+ try {
+ setSocketReceiveBuffer(cs->udpFD, g_socketUDPRecvBuffer);
+ }
+ catch (const std::exception& e) {
+ warnlog(e.what());
+ }
+ }
+ }
+
const std::string& itf = cs->interface;
if (!itf.empty()) {
#ifdef SO_BINDTODEVICE
extern bool g_servFailOnNoPolicy;
extern size_t g_udpVectorSize;
extern bool g_allowEmptyResponse;
+extern uint32_t g_socketUDPSendBuffer;
+extern uint32_t g_socketUDPRecvBuffer;
extern shared_ptr<BPFFilter> g_defaultBPFFilter;
extern std::vector<std::shared_ptr<DynBPFFilter> > g_dynBPFFilters;
changelog_sections = ['New Features', 'Improvements', 'Bug Fixes', 'Removals']
changelog_inner_tag_sort = ['Security', 'DNS over HTTPS', 'DNS over TLS', 'DNSCrypt', 'DNSTAP', 'Protobuf', 'Performance', 'Webserver', 'Metrics']
-changelog_render_tags = False
+changelog_hide_tags_in_entry = True
# -- Options for HTML output ----------------------------------------------
:param int num: maximum number of UDP queries to accept
+.. function:: setUDPSocketBufferSize(recv, send)
+
+ .. versionadded:: 1.7.0
+
+ Set the size of the receive (``SO_RCVBUF``) and send (``SO_SNDBUF``) buffers for incoming UDP sockets. On Linux the default
+ values correspond to ``net.core.rmem_default`` and ``net.core.wmem_default`` , and the maximum values are restricted
+ by ``net.core.rmem_max`` and ``net.core.wmem_max``.
+
+ :param int recv: ``SO_RCVBUF`` value. Default is 0, meaning the system value will be kept.
+ :param int send: ``SO_SNDBUF`` value. Default is 0, meaning the system value will be kept.
+
.. function:: setUDPTimeout(num)
Set the maximum time dnsdist will wait for a response from a backend over UDP, in seconds. Defaults to 2
Sphinx>=1.5.0,!=1.8.0,<2.0
git+https://github.com/pieterlexis/sphinx-lua@pdns
git+https://github.com/pieterlexis/sphinx-jsondomain@no-type-links
-git+https://github.com/pieterlexis/sphinx-changelog@render-tags
+changelog>=0.5.6,<0.6
sphinxcontrib-httpdomain
sphinxcontrib-fulltoc
docutils!=0.15,<0.18
} s_idmanager;
-
-static void setSocketBuffer(int fd, int optname, uint32_t size)
-{
- uint32_t psize=0;
- socklen_t len=sizeof(psize);
-
- if(!getsockopt(fd, SOL_SOCKET, optname, (char*)&psize, &len) && psize > size) {
- cerr<<"Not decreasing socket buffer size from "<<psize<<" to "<<size<<endl;
- return;
- }
-
- if (setsockopt(fd, SOL_SOCKET, optname, (char*)&size, sizeof(size)) < 0 )
- cerr<<"Warning: unable to raise socket buffer size to "<<size<<": "<<stringerror()<<endl;
-}
-
-static void setSocketReceiveBuffer(int fd, uint32_t size)
-{
- setSocketBuffer(fd, SO_RCVBUF, size);
-}
-
-static void setSocketSendBuffer(int fd, uint32_t size)
-{
- setSocketBuffer(fd, SO_SNDBUF, size);
-}
-
-
struct AssignedIDTag{};
struct QuestionTag{};
if(g_vm.count("source-ip") && !g_vm["source-ip"].as<string>().empty())
s_socket->bind(ComboAddress(g_vm["source-ip"].as<string>(), g_vm["source-port"].as<uint16_t>()));
- setSocketReceiveBuffer(s_socket->getHandle(), 2000000);
- setSocketSendBuffer(s_socket->getHandle(), 2000000);
+ try {
+ setSocketReceiveBuffer(s_socket->getHandle(), 2000000);
+ }
+ catch (const std::exception& e) {
+ cerr<<e.what()<<endl;
+ }
+ try {
+ setSocketSendBuffer(s_socket->getHandle(), 2000000);
+ }
+ catch (const std::exception& e) {
+ cerr<<e.what()<<endl;
+ }
ComboAddress remote(g_vm["target-ip"].as<string>(),
g_vm["target-port"].as<uint16_t>());
return ComboAddress(input, port);
}
}
+
+void setSocketBuffer(int fd, int optname, uint32_t size)
+{
+ uint32_t psize = 0;
+ socklen_t len = sizeof(psize);
+
+ if (!getsockopt(fd, SOL_SOCKET, optname, &psize, &len) && psize > size) {
+ throw std::runtime_error("Not decreasing socket buffer size from " + std::to_string(psize) + " to " + std::to_string(size));
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, optname, &size, sizeof(size)) < 0) {
+ throw std::runtime_error("Unable to raise socket buffer size to " + std::to_string(size) + ": " + stringerror());
+ }
+}
+
+void setSocketReceiveBuffer(int fd, uint32_t size)
+{
+ setSocketBuffer(fd, SO_RCVBUF, size);
+}
+
+void setSocketSendBuffer(int fd, uint32_t size)
+{
+ setSocketBuffer(fd, SO_SNDBUF, size);
+}
extern template class NetmaskTree<bool>;
ComboAddress parseIPAndPort(const std::string& input, uint16_t port);
+
+/* These functions throw if the value was already set to a higher value,
+ or on error */
+void setSocketBuffer(int fd, int optname, uint32_t size);
+void setSocketReceiveBuffer(int fd, uint32_t size);
+void setSocketSendBuffer(int fd, uint32_t size);
static void handleUDPServerResponse(int fd, FDMultiplexer::funcparam_t&);
-static void setSocketBuffer(int fd, int optname, uint32_t size)
-{
- uint32_t psize=0;
- socklen_t len=sizeof(psize);
-
- if(!getsockopt(fd, SOL_SOCKET, optname, (char*)&psize, &len) && psize > size) {
- g_log<<Logger::Error<<"Not decreasing socket buffer size from "<<psize<<" to "<<size<<endl;
- return;
- }
-
- if (setsockopt(fd, SOL_SOCKET, optname, (char*)&size, sizeof(size)) < 0) {
- int err = errno;
- g_log << Logger::Error << "Unable to raise socket buffer size to " << size << ": " << stringerror(err) << endl;
- }
-}
-
-
-static void setSocketReceiveBuffer(int fd, uint32_t size)
-{
- setSocketBuffer(fd, SO_RCVBUF, size);
-}
-
-static void setSocketSendBuffer(int fd, uint32_t size)
-{
- setSocketBuffer(fd, SO_SNDBUF, size);
-}
-
-
// you can ask this class for a UDP socket to send a query from
// this socket is not yours, don't even think about deleting it
// but after you call 'returnSocket' on it, don't assume anything anymore
g_log<<endl;
}
- if (sr.d_outqueries || sr.d_authzonequeries) {
- g_recCache->cacheMisses++;
- }
- else {
- g_recCache->cacheHits++;
+ if (dc->d_mdp.d_header.opcode == Opcode::Query) {
+ if (sr.d_outqueries || sr.d_authzonequeries) {
+ g_recCache->cacheMisses++;
+ }
+ else {
+ g_recCache->cacheHits++;
+ }
}
g_stats.answers(spentUsec);
}
g_stats.sourceDisallowedNotify++;
+ terminateTCPConnection(fd);
return;
}
}
g_stats.zoneDisallowedNotify++;
+ terminateTCPConnection(fd);
return;
}
}
if (dc->d_eventTrace.enabled() && SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_log) {
g_log << Logger::Info << dc->d_eventTrace.toString() << endl;
}
+ return;
} // cache hit
} // query opcode
throw PDNSException("Binding TCP server socket for "+ st.host +": "+stringerror());
setNonBlocking(fd);
- setSocketSendBuffer(fd, 65000);
+ try {
+ setSocketSendBuffer(fd, 65000);
+ }
+ catch (const std::exception& e) {
+ g_log<<Logger::Error<<e.what()<<endl;
+ }
+
listen(fd, 128);
deferredAdds.emplace_back(fd, handleNewTCPQuestion);
tcpSockets.insert(fd);
setCloseOnExec(fd);
- setSocketReceiveBuffer(fd, 250000);
+ try {
+ setSocketReceiveBuffer(fd, 250000);
+ }
+ catch (const std::exception& e) {
+ g_log<<Logger::Error<<e.what()<<endl;
+ }
sin.sin4.sin_port = htons(st.port);
changelog_sections = ['New Features', 'Improvements', 'Bug Fixes']
changelog_inner_tag_sort = ['General', 'DNSSEC', 'Protobuf', 'RPZ']
-changelog_render_tags = False
+changelog_hide_tags_in_entry = True
# -- Options for HTML output ----------------------------------------------
Sphinx>=1.5.0,!=1.8.0,<2.0
git+https://github.com/pieterlexis/sphinx-jsondomain@no-type-links
git+https://github.com/pieterlexis/sphinx-lua@pdns
-git+https://github.com/pieterlexis/sphinx-changelog@render-tags
+changelog>=0.5.6,<0.6
guzzle_sphinx_theme
sphinxcontrib.httpdomain
sphinxcontrib-fulltoc
feature, supply one domain name per line, with optional comments
preceded by a "#".
+NOTIFY-allowed zones can also be specified using `forward-zones-file`_.
+
.. _setting-allow-notify-from:
``allow-notify-from``
void pushAlmostExpiredTask(const DNSName& qname, uint16_t qtype, time_t deadline)
{
++s_almost_expired_tasks_pushed;
- t_taskQueue.push({qname, qtype, deadline, true, resolve});
+ pdns::ResolveTask task{qname, qtype, deadline, true, resolve};
+ t_taskQueue.push(std::move(task));
}
uint64_t getTaskPushes()
*/
#pragma once
+#include <sys/time.h>
#include <thread>
#include <boost/multi_index_container.hpp>
export PDNSRECURSOR=${PDNSRECURSOR:-${PWD}/../pdns/recursordist/pdns_recursor}
export RECCONTROL=${RECCONTROL:-${PWD}/../pdns/recursordist/rec_control}
-LIBFAKETIME_DEFAULT=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 # ubuntu default
+LIBFAKETIME_DEFAULT=/usr/lib/x86_64-linux-gnu/faketime/libfaketimeMT.so.1 # ubuntu default
LIBAUTHBIND_DEFAULT=/usr/lib/authbind/libauthbind.so.1
if [ $(uname -s) = "Darwin" ]; then
# macOS is not /really/ supported here; it works for some tests, and then you might need sudo.
export NODNSTAPTESTS=1
fi
-# libfstrm has a bad interaction with libfaketime on at least Ubuntu Focal.
-# to run the test without LIBFAKETIME, we clear the var if it set to /bin/false
-if [ "$LIBFAKETIME" = "/bin/false" ]; then
- LIBFAKETIME=""
-fi
-
-if [ "${LIBAUTHBIND}" != "" -o "${LIBFAKETIME}" != "" ]; then
-LD_PRELOAD="${LIBASAN} ${LIBAUTHBIND} ${LIBFAKETIME}" nosetests -I test_WellKnown.py --with-xunit $@
+# LIBFAKETIME is only added to LD_PRELOAD by the pyton code when needed
+if [ "${LIBASAN}" != "" -o "${LIBAUTHBIND}" != "" ]; then
+LD_PRELOAD="${LIBASAN} ${LIBAUTHBIND}" nosetests -I test_WellKnown.py --with-xunit $@
else
nosetests -I test_WellKnown.py --with-xunit $@
fi
--- /dev/null
+import clientsubnetoption
+import cookiesoption
+import dns
+import os
+import requests
+import subprocess
+
+from recursortests import RecursorTest
+
+class NotifyRecursorTest(RecursorTest):
+
+ _auth_zones = {
+ '8': {'threads': 1,
+ 'zones': ['ROOT']}
+ }
+
+ _confdir = 'Notify'
+ _wsPort = 8042
+ _wsTimeout = 2
+ _wsPassword = 'secretpassword'
+ _apiKey = 'secretapikey'
+ _config_template = """
+ disable-packetcache=yes
+ auth-zones=example=configs/%s/example.zone
+ allow-notify-from=127.0.0.1
+ allow-notify-for=example
+ quiet=no
+ loglevel=9
+ webserver=yes
+ webserver-port=%d
+ webserver-address=127.0.0.1
+ webserver-password=%s
+ api-key=%s
+ """ % (_confdir, _wsPort, _wsPassword, _apiKey)
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ authzonepath = os.path.join(confdir, 'example.zone')
+ with open(authzonepath, 'w') as authzone:
+ authzone.write("""$ORIGIN example.
+@ 3600 IN SOA {soa}
+a 3600 IN A 192.0.2.42
+b 3600 IN A 192.0.2.42
+c 3600 IN A 192.0.2.42
+d 3600 IN A 192.0.2.42
+e 3600 IN A 192.0.2.42
+f 3600 IN CNAME f ; CNAME loop: dirty trick to get a ServFail in an authzone
+""".format(soa=cls._SOA))
+ super(NotifyRecursorTest, cls).generateRecursorConfig(confdir)
+
+ def checkRecordCacheMetrics(self, expectedHits, expectedMisses):
+ headers = {'x-api-key': self._apiKey}
+ url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+ r = requests.get(url, headers=headers, timeout=self._wsTimeout)
+ self.assertTrue(r)
+ self.assertEqual(r.status_code, 200)
+ self.assertTrue(r.json())
+ content = r.json()
+ foundHits = False
+ foundMisses = True
+ for entry in content:
+ if entry['name'] == 'cache-hits':
+ foundHits = True
+ self.assertEqual(int(entry['value']), expectedHits)
+ elif entry['name'] == 'cache-misses':
+ foundMisses = True
+ self.assertEqual(int(entry['value']), expectedMisses)
+
+ self.assertTrue(foundHits)
+ self.assertTrue(foundMisses)
+
+ def testNotify(self):
+ # first query
+ qname = 'a.example.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ self.checkRecordCacheMetrics(1, 1)
+
+ # we should get a hit over UDP this time
+ res = self.sendUDPQuery(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.checkRecordCacheMetrics(2, 1)
+
+ # we should get a hit over TCP this time
+ res = self.sendTCPQuery(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.checkRecordCacheMetrics(3, 1)
+
+ notify = dns.message.make_query('example', 'SOA', want_dnssec=False)
+ notify.set_opcode(4) # notify
+ notifyexpected = dns.rrset.from_text('example.', 0, dns.rdataclass.IN, 'SOA')
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(notify)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertEquals(res.opcode(), 4)
+ print(res)
+ self.assertEquals(res.question[0].to_text(), 'example. IN SOA')
+
+ self.checkRecordCacheMetrics(3, 1)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ self.checkRecordCacheMetrics(4, 2)
@task
def test_regression_recursor(c):
c.run('/opt/pdns-recursor/sbin/pdns_recursor --version')
- c.run('PDNSRECURSOR=/opt/pdns-recursor/sbin/pdns_recursor RECCONTROL=/opt/pdns-recursor/bin/rec_control SKIP_IPV6_TESTS=y LIBFAKETIME=/bin/false ./build-scripts/test-recursor test_RecDnstap.py')
- c.run('PDNSRECURSOR=/opt/pdns-recursor/sbin/pdns_recursor RECCONTROL=/opt/pdns-recursor/bin/rec_control SKIP_IPV6_TESTS=y ./build-scripts/test-recursor -I test_RecDnstap.py')
+ c.run('PDNSRECURSOR=/opt/pdns-recursor/sbin/pdns_recursor RECCONTROL=/opt/pdns-recursor/bin/rec_control SKIP_IPV6_TESTS=y ./build-scripts/test-recursor')
@task
def test_bulk_recursor(c, threads, mthreads, shards):