From: Remi Gacogne Date: Fri, 1 Apr 2022 14:47:05 +0000 (+0200) Subject: dnsdist: Implement additional addresses on DoT/DoH frontends X-Git-Tag: dnsdist-1.8.0-rc1~287^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=57845fd81ff516df3c376989ef72e45c4b180471;p=thirdparty%2Fpdns.git dnsdist: Implement additional addresses on DoT/DoH frontends --- diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index f43b02a7ad..e0337d3fa3 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -424,11 +424,11 @@ void setupLuaBindings(LuaContext& luaCtx, bool client) #ifdef HAVE_EBPF luaCtx.registerFunction)>("attachFilter", [](ClientState& frontend, std::shared_ptr bpf) { if (bpf) { - frontend.attachFilter(bpf); + frontend.attachFilter(bpf, frontend.getSocket()); } }); luaCtx.registerFunction("detachFilter", [](ClientState& frontend) { - frontend.detachFilter(); + frontend.detachFilter(frontend.getSocket()); }); #endif /* HAVE_EBPF */ #endif /* DISABLE_CLIENT_STATE_BINDINGS */ @@ -629,7 +629,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client) } if (bpf) { for (const auto& frontend : g_frontends) { - frontend->attachFilter(bpf); + frontend->attachFilter(bpf, frontend->getSocket()); } } }); diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index dfd4b4d4b0..3092612192 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -2435,6 +2435,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) uint64_t tcpMaxConcurrentConnections = 0; std::string interface; std::set cpus; + std::vector> additionalAddresses; if (vars) { parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections); @@ -2473,11 +2474,27 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) frontend->d_exactPathMatching = boost::get((*vars)["exactPathMatching"]); } + if (vars->count("additionalAddresses")) { + auto addresses = boost::get>(vars->at("additionalAddresses")); + for (const auto& [_, add] : addresses) { + try { + ComboAddress address(add); + additionalAddresses.emplace_back(address, -1); + } + catch (const PDNSException& e) { + errlog("Unable to parse additional address %s for DOH bind: %s", add, e.reason); + return; + } + } + } + parseTLSConfig(frontend->d_tlsConfig, "addDOHLocal", vars); } g_dohlocals.push_back(frontend); auto cs = std::make_unique(frontend->d_local, true, reusePort, tcpFastOpenQueueSize, interface, cpus); cs->dohFrontend = frontend; + cs->d_additionalAddresses = std::move(additionalAddresses); + if (tcpListenQueueSize > 0) { cs->tcpListenQueueSize = tcpListenQueueSize; } @@ -2649,6 +2666,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) uint64_t tcpMaxConcurrentConns = 0; std::string interface; std::set cpus; + std::vector> additionalAddresses; if (vars) { parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConns); @@ -2658,6 +2676,20 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) boost::algorithm::to_lower(frontend->d_provider); } + if (vars->count("additionalAddresses")) { + auto addresses = boost::get>(vars->at("additionalAddresses")); + for (const auto& [_, add] : addresses) { + try { + ComboAddress address(add); + additionalAddresses.emplace_back(address, -1); + } + catch (const PDNSException& e) { + errlog("Unable to parse additional address %s for DoT bind: %s", add, e.reason); + return; + } + } + } + parseTLSConfig(frontend->d_tlsConfig, "addTLSLocal", vars); } @@ -2676,6 +2708,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) // only works pre-startup, so no sync necessary auto cs = std::make_unique(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus); cs->tlsFrontend = frontend; + cs->d_additionalAddresses = std::move(additionalAddresses); if (tcpListenQueueSize > 0) { cs->tcpListenQueueSize = tcpListenQueueSize; } diff --git a/pdns/dnsdist-tcp.cc b/pdns/dnsdist-tcp.cc index 311b5c7ef5..bf58871e6c 100644 --- a/pdns/dnsdist-tcp.cc +++ b/pdns/dnsdist-tcp.cc @@ -1366,86 +1366,129 @@ static void tcpClientThread(int pipefd, int crossProtocolQueriesPipeFD, int cros } } -/* spawn as many of these as required, they call Accept on a socket on which they will accept queries, and - they will hand off to worker threads & spawn more of them if required -*/ -void tcpAcceptorThread(ClientState* cs) +struct TCPAcceptorParam { - setThreadName("dnsdist/tcpAcce"); + ClientState& cs; + ComboAddress local; + LocalStateHolder& acl; +}; +static void acceptNewConnection(int socket, TCPAcceptorParam& param) +{ + auto& cs = param.cs; + auto& acl = param.acl; bool tcpClientCountIncremented = false; ComboAddress remote; - remote.sin4.sin_family = cs->local.sin4.sin_family; + remote.sin4.sin_family = param.local.sin4.sin_family; - auto acl = g_ACL.getLocal(); - for(;;) { - std::unique_ptr ci; - tcpClientCountIncremented = false; - try { - socklen_t remlen = remote.getSocklen(); - ci = std::make_unique(cs); + std::unique_ptr ci; + tcpClientCountIncremented = false; + try { + socklen_t remlen = remote.getSocklen(); + ci = std::make_unique(&cs); #ifdef HAVE_ACCEPT4 - ci->fd = accept4(cs->tcpFD, reinterpret_cast(&remote), &remlen, SOCK_NONBLOCK); + ci->fd = accept4(socket, reinterpret_cast(&remote), &remlen, SOCK_NONBLOCK); #else - ci->fd = accept(cs->tcpFD, reinterpret_cast(&remote), &remlen); + ci->fd = accept(socket, reinterpret_cast(&remote), &remlen); #endif - // will be decremented when the ConnectionInfo object is destroyed, no matter the reason - auto concurrentConnections = ++cs->tcpCurrentConnections; - if (cs->d_tcpConcurrentConnectionsLimit > 0 && concurrentConnections > cs->d_tcpConcurrentConnectionsLimit) { - continue; - } + if (ci->fd < 0) { + throw std::runtime_error((boost::format("accepting new connection on socket: %s") % stringerror()).str()); + } - if (concurrentConnections > cs->tcpMaxConcurrentConnections.load()) { - cs->tcpMaxConcurrentConnections.store(concurrentConnections); - } + if (!acl->match(remote)) { + ++g_stats.aclDrops; + vinfolog("Dropped TCP connection from %s because of ACL", remote.toStringWithPort()); + return; + } - if (ci->fd < 0) { - throw std::runtime_error((boost::format("accepting new connection on socket: %s") % stringerror()).str()); - } + // will be decremented when the ConnectionInfo object is destroyed, no matter the reason + auto concurrentConnections = ++cs.tcpCurrentConnections; + if (cs.d_tcpConcurrentConnectionsLimit > 0 && concurrentConnections > cs.d_tcpConcurrentConnectionsLimit) { + return; + } - if (!acl->match(remote)) { - ++g_stats.aclDrops; - vinfolog("Dropped TCP connection from %s because of ACL", remote.toStringWithPort()); - continue; - } + if (concurrentConnections > cs.tcpMaxConcurrentConnections.load()) { + cs.tcpMaxConcurrentConnections.store(concurrentConnections); + } #ifndef HAVE_ACCEPT4 - if (!setNonBlocking(ci->fd)) { - continue; - } + if (!setNonBlocking(ci->fd)) { + return; + } #endif - setTCPNoDelay(ci->fd); // disable NAGLE - if (g_maxTCPQueuedConnections > 0 && g_tcpclientthreads->getQueuedCount() >= g_maxTCPQueuedConnections) { - vinfolog("Dropping TCP connection from %s because we have too many queued already", remote.toStringWithPort()); - continue; - } - if (g_maxTCPConnectionsPerClient) { - auto tcpClientsCount = s_tcpClientsCount.lock(); + setTCPNoDelay(ci->fd); // disable NAGLE - if ((*tcpClientsCount)[remote] >= g_maxTCPConnectionsPerClient) { - vinfolog("Dropping TCP connection from %s because we have too many from this client already", remote.toStringWithPort()); - continue; - } - (*tcpClientsCount)[remote]++; - tcpClientCountIncremented = true; - } + if (g_maxTCPQueuedConnections > 0 && g_tcpclientthreads->getQueuedCount() >= g_maxTCPQueuedConnections) { + vinfolog("Dropping TCP connection from %s because we have too many queued already", remote.toStringWithPort()); + return; + } - vinfolog("Got TCP connection from %s", remote.toStringWithPort()); + if (g_maxTCPConnectionsPerClient) { + auto tcpClientsCount = s_tcpClientsCount.lock(); - ci->remote = remote; - if (!g_tcpclientthreads->passConnectionToThread(std::move(ci))) { - if (tcpClientCountIncremented) { - decrementTCPClientCount(remote); - } + if ((*tcpClientsCount)[remote] >= g_maxTCPConnectionsPerClient) { + vinfolog("Dropping TCP connection from %s because we have too many from this client already", remote.toStringWithPort()); + return; } + (*tcpClientsCount)[remote]++; + tcpClientCountIncremented = true; } - catch (const std::exception& e) { - errlog("While reading a TCP question: %s", e.what()); + + vinfolog("Got TCP connection from %s", remote.toStringWithPort()); + + ci->remote = remote; + if (!g_tcpclientthreads->passConnectionToThread(std::move(ci))) { if (tcpClientCountIncremented) { decrementTCPClientCount(remote); } } - catch (...){} + } + catch (const std::exception& e) { + errlog("While reading a TCP question: %s", e.what()); + if (tcpClientCountIncremented) { + decrementTCPClientCount(remote); + } + } + catch (...){} +} + +/* spawn as many of these as required, they call Accept on a socket on which they will accept queries, and + they will hand off to worker threads & spawn more of them if required +*/ +void tcpAcceptorThread(ClientState* cs) +{ + setThreadName("dnsdist/tcpAcce"); + + auto acl = g_ACL.getLocal(); + struct TCPAcceptorParam param{*cs, cs->local, acl}; + + if (cs->d_additionalAddresses.empty()) { + while (true) { + acceptNewConnection(cs->tcpFD, param); + } + } + else { + auto acceptCallback = [](int socket, FDMultiplexer::funcparam_t& funcparam) { + auto acceptorParam = boost::any_cast(funcparam); + acceptNewConnection(socket, *acceptorParam); + }; + + std::vector additionalParams; + auto mplexer = std::unique_ptr(FDMultiplexer::getMultiplexerSilent()); + mplexer->addReadFD(cs->tcpFD, acceptCallback, ¶m); + for (const auto& [addr, socket] : cs->d_additionalAddresses) { + additionalParams.emplace_back(TCPAcceptorParam{*cs, addr, acl}); + } + size_t idx = 0; + for (const auto& [addr, socket] : cs->d_additionalAddresses) { + mplexer->addReadFD(socket, acceptCallback, &additionalParams.at(idx)); + idx++; + } + + struct timeval tv; + while (true) { + mplexer->run(&tv); + } } } diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 353f19feb5..0002181e32 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -2050,149 +2050,159 @@ static void checkFileDescriptorsLimits(size_t udpBindsCount, size_t tcpBindsCoun static bool g_warned_ipv6_recvpktinfo = false; -static void setUpLocalBind(std::unique_ptr& cs) +static void setUpLocalBind(std::unique_ptr& cstate) { - /* skip some warnings if there is an identical UDP context */ - bool warn = cs->tcp == false || cs->tlsFrontend != nullptr || cs->dohFrontend != nullptr; - int& fd = cs->tcp == false ? cs->udpFD : cs->tcpFD; - (void) warn; - - fd = SSocket(cs->local.sin4.sin_family, cs->tcp == false ? SOCK_DGRAM : SOCK_STREAM, 0); + auto setupSocket = [](ClientState& cs, const ComboAddress& addr, int& socket, bool tcp, bool warn) { + (void) warn; + socket = SSocket(addr.sin4.sin_family, tcp == false ? SOCK_DGRAM : SOCK_STREAM, 0); - if (cs->tcp) { - SSetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 1); + if (tcp) { + SSetsockopt(socket, SOL_SOCKET, SO_REUSEADDR, 1); #ifdef TCP_DEFER_ACCEPT - SSetsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1); + SSetsockopt(socket, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1); #endif - if (cs->fastOpenQueueSize > 0) { + if (cs.fastOpenQueueSize > 0) { #ifdef TCP_FASTOPEN - SSetsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, cs->fastOpenQueueSize); + SSetsockopt(socket, IPPROTO_TCP, TCP_FASTOPEN, cs.fastOpenQueueSize); #ifdef TCP_FASTOPEN_KEY - if (!g_TCPFastOpenKey.empty()) { - auto res = setsockopt(fd, IPPROTO_IP, TCP_FASTOPEN_KEY, g_TCPFastOpenKey.data(), g_TCPFastOpenKey.size() * sizeof(g_TCPFastOpenKey[0])); - if (res == -1) - throw runtime_error("setsockopt for level IPPROTO_TCP and opname TCP_FASTOPEN_KEY failed: " + stringerror()); - } -#endif -#else - if (warn) { - warnlog("TCP Fast Open has been configured on local address '%s' but is not supported", cs->local.toStringWithPort()); + if (!g_TCPFastOpenKey.empty()) { + auto res = setsockopt(socket, IPPROTO_IP, TCP_FASTOPEN_KEY, g_TCPFastOpenKey.data(), g_TCPFastOpenKey.size() * sizeof(g_TCPFastOpenKey[0])); + if (res == -1) { + throw runtime_error("setsockopt for level IPPROTO_TCP and opname TCP_FASTOPEN_KEY failed: " + stringerror()); + } + } +#endif /* TCP_FASTOPEN_KEY */ +#else /* TCP_FASTOPEN */ + if (warn) { + warnlog("TCP Fast Open has been configured on local address '%s' but is not supported", addr.toStringWithPort()); + } +#endif /* TCP_FASTOPEN */ } -#endif } - } - if(cs->local.sin4.sin_family == AF_INET6) { - SSetsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, 1); - } + if (addr.sin4.sin_family == AF_INET6) { + SSetsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, 1); + } - bindAny(cs->local.sin4.sin_family, fd); + bindAny(addr.sin4.sin_family, socket); - if(!cs->tcp && IsAnyAddress(cs->local)) { - int one=1; - (void)setsockopt(fd, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); // linux supports this, so why not - might fail on other systems + if (!tcp && IsAnyAddress(addr)) { + int one = 1; + (void) setsockopt(socket, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); // linux supports this, so why not - might fail on other systems #ifdef IPV6_RECVPKTINFO - if (cs->local.isIPv6() && setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) < 0 && - !g_warned_ipv6_recvpktinfo) { + if (addr.isIPv6() && setsockopt(socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) < 0 && + !g_warned_ipv6_recvpktinfo) { warnlog("Warning: IPV6_RECVPKTINFO setsockopt failed: %s", stringerror()); g_warned_ipv6_recvpktinfo = true; - } -#endif - } - - if (cs->reuseport) { - if (!setReusePort(fd)) { - if (warn) { - /* no need to warn again if configured but support is not available, we already did for UDP */ - warnlog("SO_REUSEPORT has been configured on local address '%s' but is not supported", cs->local.toStringWithPort()); } +#endif } - } - /* Only set this on IPv4 UDP sockets. - Don't set it for DNSCrypt binds. DNSCrypt pads queries for privacy - purposes, so we do receive large, sometimes fragmented datagrams. */ - if (!cs->tcp && !cs->dnscryptCtx) { - try { - setSocketIgnorePMTU(cs->udpFD, cs->local.sin4.sin_family); - } - catch(const std::exception& e) { - warnlog("Failed to set IP_MTU_DISCOVER on UDP server socket for local address '%s': %s", cs->local.toStringWithPort(), e.what()); + if (cs.reuseport) { + if (!setReusePort(socket)) { + if (warn) { + /* no need to warn again if configured but support is not available, we already did for UDP */ + warnlog("SO_REUSEPORT has been configured on local address '%s' but is not supported", addr.toStringWithPort()); + } + } } - } - if (!cs->tcp) { - if (g_socketUDPSendBuffer > 0) { + /* Only set this on IPv4 UDP sockets. + Don't set it for DNSCrypt binds. DNSCrypt pads queries for privacy + purposes, so we do receive large, sometimes fragmented datagrams. */ + if (!tcp && !cs.dnscryptCtx) { try { - setSocketSendBuffer(cs->udpFD, g_socketUDPSendBuffer); + setSocketIgnorePMTU(socket, addr.sin4.sin_family); } catch (const std::exception& e) { - warnlog(e.what()); + warnlog("Failed to set IP_MTU_DISCOVER on UDP server socket for local address '%s': %s", addr.toStringWithPort(), e.what()); } } - if (g_socketUDPRecvBuffer > 0) { - try { - setSocketReceiveBuffer(cs->udpFD, g_socketUDPRecvBuffer); + if (!tcp) { + if (g_socketUDPSendBuffer > 0) { + try { + setSocketSendBuffer(socket, g_socketUDPSendBuffer); + } + catch (const std::exception& e) { + warnlog(e.what()); + } } - catch (const std::exception& e) { - warnlog(e.what()); + + if (g_socketUDPRecvBuffer > 0) { + try { + setSocketReceiveBuffer(socket, g_socketUDPRecvBuffer); + } + catch (const std::exception& e) { + warnlog(e.what()); + } } } - } - const std::string& itf = cs->interface; - if (!itf.empty()) { + const std::string& itf = cs.interface; + if (!itf.empty()) { #ifdef SO_BINDTODEVICE - int res = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, itf.c_str(), itf.length()); - if (res != 0) { - warnlog("Error setting up the interface on local address '%s': %s", cs->local.toStringWithPort(), stringerror()); - } + int res = setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, itf.c_str(), itf.length()); + if (res != 0) { + warnlog("Error setting up the interface on local address '%s': %s", addr.toStringWithPort(), stringerror()); + } #else - if (warn) { - warnlog("An interface has been configured on local address '%s' but SO_BINDTODEVICE is not supported", cs->local.toStringWithPort()); - } + if (warn) { + warnlog("An interface has been configured on local address '%s' but SO_BINDTODEVICE is not supported", addr.toStringWithPort()); + } #endif - } + } #ifdef HAVE_EBPF - if (g_defaultBPFFilter && !g_defaultBPFFilter->isExternal()) { - cs->attachFilter(g_defaultBPFFilter); - vinfolog("Attaching default BPF Filter to %s frontend %s", (!cs->tcp ? "UDP" : "TCP"), cs->local.toStringWithPort()); - } + if (g_defaultBPFFilter && !g_defaultBPFFilter->isExternal()) { + cs.attachFilter(g_defaultBPFFilter, socket); + vinfolog("Attaching default BPF Filter to %s frontend %s", (!tcp ? "UDP" : "TCP"), addr.toStringWithPort()); + } #endif /* HAVE_EBPF */ - if (cs->tlsFrontend != nullptr) { - if (!cs->tlsFrontend->setupTLS()) { - errlog("Error while setting up TLS on local address '%s', exiting", cs->local.toStringWithPort()); - _exit(EXIT_FAILURE); + SBind(socket, addr); + + if (tcp) { + SListen(socket, cs.tcpListenQueueSize); + + if (cs.tlsFrontend != nullptr) { + infolog("Listening on %s for TLS", addr.toStringWithPort()); + } + else if (cs.dohFrontend != nullptr) { + infolog("Listening on %s for DoH", addr.toStringWithPort()); + } + else if (cs.dnscryptCtx != nullptr) { + infolog("Listening on %s for DNSCrypt", addr.toStringWithPort()); + } + else { + infolog("Listening on %s", addr.toStringWithPort()); + } } - } + }; - if (cs->dohFrontend != nullptr) { - cs->dohFrontend->setup(); - } + /* skip some warnings if there is an identical UDP context */ + bool warn = cstate->tcp == false || cstate->tlsFrontend != nullptr || cstate->dohFrontend != nullptr; + int& fd = cstate->tcp == false ? cstate->udpFD : cstate->tcpFD; + (void) warn; - SBind(fd, cs->local); + setupSocket(*cstate, cstate->local, fd, cstate->tcp, warn); - if (cs->tcp) { - SListen(cs->tcpFD, cs->tcpListenQueueSize); + for (auto& [addr, socket] : cstate->d_additionalAddresses) { + setupSocket(*cstate, addr, socket, true, false); + } - if (cs->tlsFrontend != nullptr) { - infolog("Listening on %s for TLS", cs->local.toStringWithPort()); - } - else if (cs->dohFrontend != nullptr) { - infolog("Listening on %s for DoH", cs->local.toStringWithPort()); - } - else if (cs->dnscryptCtx != nullptr) { - infolog("Listening on %s for DNSCrypt", cs->local.toStringWithPort()); - } - else { - infolog("Listening on %s", cs->local.toStringWithPort()); + if (cstate->tlsFrontend != nullptr) { + if (!cstate->tlsFrontend->setupTLS()) { + errlog("Error while setting up TLS on local address '%s', exiting", cstate->local.toStringWithPort()); + _exit(EXIT_FAILURE); } } - cs->ready = true; + if (cstate->dohFrontend != nullptr) { + cstate->dohFrontend->setup(); + } + + cstate->ready = true; } struct diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index e8b2abe80b..95129a82a6 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -618,6 +618,7 @@ struct ClientState std::set cpus; std::string interface; ComboAddress local; + std::vector> d_additionalAddresses; std::shared_ptr dnscryptCtx{nullptr}; std::shared_ptr tlsFrontend{nullptr}; std::shared_ptr dohFrontend{nullptr}; @@ -702,19 +703,19 @@ struct ClientState return result; } - void detachFilter() + void detachFilter(int socket) { if (d_filter) { - d_filter->removeSocket(getSocket()); + d_filter->removeSocket(socket); d_filter = nullptr; } } - void attachFilter(shared_ptr bpf) + void attachFilter(shared_ptr bpf, int socket) { - detachFilter(); + detachFilter(socket); - bpf->addSocket(getSocket()); + bpf->addSocket(socket); d_filter = bpf; } diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index 767604da95..ef3dd48a33 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -120,7 +120,7 @@ Listen Sockets .. versionchanged:: 1.8.0 ``certFile`` now accepts a TLSCertificate object or a list of such objects (see :func:`newTLSCertificate`) - ``keepIncomingHeaders`` option added. + ``additionalAddresses`` and ``keepIncomingHeaders`` options added. Listen on the specified address and TCP port for incoming DNS over HTTPS connections, presenting the specified X.509 certificate. If no certificate (or key) files are specified, listen for incoming DNS over HTTP connections instead. @@ -162,6 +162,7 @@ Listen Sockets * ``releaseBuffers=true``: bool - Whether OpenSSL should release its I/O buffers when a connection goes idle, saving roughly 35 kB of memory per connection. * ``enableRenegotiation=false``: bool - Whether secure TLS renegotiation should be enabled. Disabled by default since it increases the attack surface and is seldom used for DNS. * ``keepIncomingHeaders``: bool - Whether to retain the incoming headers in memory, to be able to use :func:`HTTPHeaderRule` or :meth:`DNSQuestion.getHTTPHeaders`. Default is false. Before 1.8.0 the headers were always kept in-memory. + * ``additionalAddresses``: list - List of additional addresses (with port) to listen on. Using this option instead of creating a new frontend for each address avoids the creation of new thread and Frontend objects, reducing the memory usage. The drawback is that there will be a single set of metrics for all addresses. .. function:: addTLSLocal(address, certFile(s), keyFile(s) [, options]) @@ -174,7 +175,8 @@ Listen Sockets .. versionchanged:: 1.8.0 ``tlsAsyncMode`` option added. .. versionchanged:: 1.8.0 - ``certFile`` now accepts a TLSCertificate object or a list of such objects (see :func:`newTLSCertificate`) + ``certFile`` now accepts a TLSCertificate object or a list of such objects (see :func:`newTLSCertificate`). + ``additionalAddresses`` option added. Listen on the specified address and TCP port for incoming DNS over TLS connections, presenting the specified X.509 certificate. @@ -209,6 +211,7 @@ Listen Sockets * ``releaseBuffers=true``: bool - Whether OpenSSL should release its I/O buffers when a connection goes idle, saving roughly 35 kB of memory per connection. * ``enableRenegotiation=false``: bool - Whether secure TLS renegotiation should be enabled (OpenSSL only, the GnuTLS provider does not support it). Disabled by default since it increases the attack surface and is seldom used for DNS. * ``tlsAsyncMode=false``: bool - Whether to enable experimental asynchronous TLS I/O operations if OpenSSL is used as the TLS provider and an asynchronous capable SSL engine is loaded. See also :func:`loadTLSEngine` to load the engine. + * ``additionalAddresses``: list - List of additional addresses (with port) to listen on. Using this option instead of creating a new frontend for each address avoids the creation of new thread and Frontend objects, reducing the memory usage. The drawback is that there will be a single set of metrics for all addresses. .. function:: setLocal(address[, options]) diff --git a/pdns/dnsdistdist/doh.cc b/pdns/dnsdistdist/doh.cc index 7aedbd6ba8..cf9c316690 100644 --- a/pdns/dnsdistdist/doh.cc +++ b/pdns/dnsdistdist/doh.cc @@ -1404,7 +1404,7 @@ static void on_accept(h2o_socket_t *listener, const char *err) h2o_accept(conn.d_acceptCtx->get(), sock); } -static int create_listener(const ComboAddress& addr, std::shared_ptr& dsc, int fd) +static int create_listener(std::shared_ptr& dsc, int fd) { auto sock = h2o_evloop_socket_create(dsc->h2o_ctx.loop, fd, H2O_SOCKET_FLAG_DONT_READ); sock->data = dsc.get(); @@ -1629,9 +1629,14 @@ void dohThread(ClientState* cs) setupAcceptContext(*dsc->accept_ctx, *dsc, false); - if (create_listener(df->d_local, dsc, cs->tcpFD) != 0) { + if (create_listener(dsc, cs->tcpFD) != 0) { throw std::runtime_error("DOH server failed to listen on " + df->d_local.toStringWithPort() + ": " + strerror(errno)); } + for (const auto& [addr, fd] : cs->d_additionalAddresses) { + if (create_listener(dsc, fd) != 0) { + throw std::runtime_error("DOH server failed to listen on additional address " + addr.toStringWithPort() + " for DOH local" + df->d_local.toStringWithPort() + ": " + strerror(errno)); + } + } bool stop = false; do {