#ifdef HAVE_EBPF
luaCtx.registerFunction<void(ClientState::*)(std::shared_ptr<BPFFilter>)>("attachFilter", [](ClientState& frontend, std::shared_ptr<BPFFilter> bpf) {
if (bpf) {
- frontend.attachFilter(bpf);
+ frontend.attachFilter(bpf, frontend.getSocket());
}
});
luaCtx.registerFunction<void(ClientState::*)()>("detachFilter", [](ClientState& frontend) {
- frontend.detachFilter();
+ frontend.detachFilter(frontend.getSocket());
});
#endif /* HAVE_EBPF */
#endif /* DISABLE_CLIENT_STATE_BINDINGS */
}
if (bpf) {
for (const auto& frontend : g_frontends) {
- frontend->attachFilter(bpf);
+ frontend->attachFilter(bpf, frontend->getSocket());
}
}
});
uint64_t tcpMaxConcurrentConnections = 0;
std::string interface;
std::set<int> cpus;
+ std::vector<std::pair<ComboAddress, int>> additionalAddresses;
if (vars) {
parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections);
frontend->d_exactPathMatching = boost::get<bool>((*vars)["exactPathMatching"]);
}
+ if (vars->count("additionalAddresses")) {
+ auto addresses = boost::get<LuaArray<std::string>>(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<ClientState>(frontend->d_local, true, reusePort, tcpFastOpenQueueSize, interface, cpus);
cs->dohFrontend = frontend;
+ cs->d_additionalAddresses = std::move(additionalAddresses);
+
if (tcpListenQueueSize > 0) {
cs->tcpListenQueueSize = tcpListenQueueSize;
}
uint64_t tcpMaxConcurrentConns = 0;
std::string interface;
std::set<int> cpus;
+ std::vector<std::pair<ComboAddress, int>> additionalAddresses;
if (vars) {
parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConns);
boost::algorithm::to_lower(frontend->d_provider);
}
+ if (vars->count("additionalAddresses")) {
+ auto addresses = boost::get<LuaArray<std::string>>(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);
}
// only works pre-startup, so no sync necessary
auto cs = std::make_unique<ClientState>(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus);
cs->tlsFrontend = frontend;
+ cs->d_additionalAddresses = std::move(additionalAddresses);
if (tcpListenQueueSize > 0) {
cs->tcpListenQueueSize = tcpListenQueueSize;
}
}
}
-/* 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<NetmaskGroup>& 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<ConnectionInfo> ci;
- tcpClientCountIncremented = false;
- try {
- socklen_t remlen = remote.getSocklen();
- ci = std::make_unique<ConnectionInfo>(cs);
+ std::unique_ptr<ConnectionInfo> ci;
+ tcpClientCountIncremented = false;
+ try {
+ socklen_t remlen = remote.getSocklen();
+ ci = std::make_unique<ConnectionInfo>(&cs);
#ifdef HAVE_ACCEPT4
- ci->fd = accept4(cs->tcpFD, reinterpret_cast<struct sockaddr*>(&remote), &remlen, SOCK_NONBLOCK);
+ ci->fd = accept4(socket, reinterpret_cast<struct sockaddr*>(&remote), &remlen, SOCK_NONBLOCK);
#else
- ci->fd = accept(cs->tcpFD, reinterpret_cast<struct sockaddr*>(&remote), &remlen);
+ ci->fd = accept(socket, reinterpret_cast<struct sockaddr*>(&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<TCPAcceptorParam*>(funcparam);
+ acceptNewConnection(socket, *acceptorParam);
+ };
+
+ std::vector<TCPAcceptorParam> additionalParams;
+ auto mplexer = std::unique_ptr<FDMultiplexer>(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);
+ }
}
}
static bool g_warned_ipv6_recvpktinfo = false;
-static void setUpLocalBind(std::unique_ptr<ClientState>& cs)
+static void setUpLocalBind(std::unique_ptr<ClientState>& 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
std::set<int> cpus;
std::string interface;
ComboAddress local;
+ std::vector<std::pair<ComboAddress, int>> d_additionalAddresses;
std::shared_ptr<DNSCryptContext> dnscryptCtx{nullptr};
std::shared_ptr<TLSFrontend> tlsFrontend{nullptr};
std::shared_ptr<DOHFrontend> dohFrontend{nullptr};
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<BPFFilter> bpf)
+ void attachFilter(shared_ptr<BPFFilter> bpf, int socket)
{
- detachFilter();
+ detachFilter(socket);
- bpf->addSocket(getSocket());
+ bpf->addSocket(socket);
d_filter = bpf;
}
.. 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.
* ``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])
.. 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.
* ``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])
h2o_accept(conn.d_acceptCtx->get(), sock);
}
-static int create_listener(const ComboAddress& addr, std::shared_ptr<DOHServerConfig>& dsc, int fd)
+static int create_listener(std::shared_ptr<DOHServerConfig>& dsc, int fd)
{
auto sock = h2o_evloop_socket_create(dsc->h2o_ctx.loop, fd, H2O_SOCKET_FLAG_DONT_READ);
sock->data = dsc.get();
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 {