The following webserver related configuration items are available:
* :ref:`setting-webserver`: If set to anything but 'no', a webserver is launched.
-* :ref:`setting-webserver-address`: Address to bind the webserver to. Defaults to 127.0.0.1, which implies that only the local computer is able to connect to the nameserver! To allow remote hosts to connect, change to 0.0.0.0 or the physical IP address of your nameserver.
+* :ref:`setting-webserver-address`: IP address or UNIX domain socket path to bind the webserver to. Defaults to 127.0.0.1, which implies that only the local computer is able to connect to the nameserver! To allow remote hosts to connect, change to 0.0.0.0 or the physical IP address of your nameserver.
* :ref:`setting-webserver-password`: If set, viewers will have to enter this password in order to gain access to the statistics, in addition to entering the configured API key on the index page.
-* :ref:`setting-webserver-port`: Port to bind the webserver to.
-* :ref:`setting-webserver-allow-from`: Netmasks that are allowed to connect to the webserver
+* :ref:`setting-webserver-port`: Port to bind the webserver to (not relevant if :ref:`setting-webserver-address` is set to a UNIX domain socket).
+* :ref:`setting-webserver-allow-from`: Netmasks that are allowed to connect to the webserver (not relevant if :ref:`setting-webserver-address` is set to a UNIX domain socket).
* :ref:`setting-webserver-max-bodysize`: Maximum request/response body size in megabytes
* :ref:`setting-webserver-connection-timeout`: Request/response timeout in seconds
- IP Address
- Default: 127.0.0.1
-IP Address for webserver/API to listen on.
+IP Address or path to UNIX domain socket for webserver/API to listen on.
.. _setting-webserver-allow-from:
- Default: 127.0.0.1,::1
Webserver/API access is only allowed from these subnets.
+Ignored if ``webserver-address`` is set to a UNIX domain socket.
.. _setting-webserver-hash-plaintext-credentials:
- Default: 8081
The port where webserver/API will listen on.
+Ignored if ``webserver-address`` is set to a UNIX domain socket.
.. _setting-webserver-print-arguments:
::arg().setSwitch("webserver", "Start a webserver for monitoring (api=yes also enables the HTTP listener)") = "no";
::arg().setSwitch("webserver-print-arguments", "If the webserver should print arguments") = "no";
- ::arg().set("webserver-address", "IP Address of webserver/API to listen on") = "127.0.0.1";
+ ::arg().set("webserver-address", "IP Address or path to UNIX domain socket for webserver/API to listen on") = "127.0.0.1";
::arg().set("webserver-port", "Port of webserver/API to listen on") = "8081";
::arg().set("webserver-password", "Password required for accessing the webserver") = "";
::arg().set("webserver-allow-from", "Webserver/API access is only allowed from these subnets") = "127.0.0.1,::1";
#include "misc.hh"
#include <netdb.h>
#include <sstream>
+#include <sys/un.h>
#include "namespaces.hh"
};
};
+union SockaddrWrapper
+{
+ sockaddr_in sin4{};
+ sockaddr_in6 sin6;
+ sockaddr_un sinun;
+
+ [[nodiscard]] socklen_t getSocklen() const
+ {
+ if (sin4.sin_family == AF_INET) {
+ return sizeof(sin4);
+ }
+ if (sin6.sin6_family == AF_INET6) {
+ return sizeof(sin6);
+ }
+ if (sinun.sun_family == AF_UNIX) {
+ return sizeof(sinun);
+ }
+ return 0;
+ }
+
+ SockaddrWrapper()
+ {
+ sin4.sin_family = AF_INET;
+ sin4.sin_addr.s_addr = 0;
+ sin4.sin_port = 0;
+ }
+
+ SockaddrWrapper(const struct sockaddr* socketAddress, socklen_t salen)
+ {
+ setSockaddr(socketAddress, salen);
+ };
+
+ SockaddrWrapper(const struct sockaddr_in6* socketAddress)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ setSockaddr(reinterpret_cast<const struct sockaddr*>(socketAddress), sizeof(struct sockaddr_in6));
+ };
+
+ SockaddrWrapper(const struct sockaddr_in* socketAddress)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ setSockaddr(reinterpret_cast<const struct sockaddr*>(socketAddress), sizeof(struct sockaddr_in));
+ };
+
+ SockaddrWrapper(const struct sockaddr_un* socketAddress)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ setSockaddr(reinterpret_cast<const struct sockaddr*>(socketAddress), sizeof(struct sockaddr_un));
+ };
+
+ void setSockaddr(const struct sockaddr* socketAddress, socklen_t salen)
+ {
+ if (salen > sizeof(struct sockaddr_un)) {
+ throw PDNSException("ComboAddress can't handle other than sockaddr_in, sockaddr_in6 or sockaddr_un");
+ }
+ memcpy(this, socketAddress, salen);
+ }
+
+ explicit SockaddrWrapper(const string& str, uint16_t port = 0)
+ {
+ memset(&sinun, 0, sizeof(sinun));
+ sin4.sin_family = AF_INET;
+ sin4.sin_port = 0;
+ if (str == "\"\"" || str == "''") {
+ throw PDNSException("Stray quotation marks in address.");
+ }
+ if (makeIPv4sockaddr(str, &sin4) != 0) {
+ sin6.sin6_family = AF_INET6;
+ if (makeIPv6sockaddr(str, &sin6) < 0) {
+ sinun.sun_family = AF_UNIX;
+ // only attempt Unix socket address if address candidate does not contain a port
+ if (str.find(':') != string::npos || makeUNsockaddr(str, &sinun) < 0) {
+ throw PDNSException("Unable to convert presentation address '" + str + "'");
+ }
+ }
+ }
+ if (sinun.sun_family != AF_UNIX && sin4.sin_port == 0) { // 'str' overrides port!
+ sin4.sin_port = htons(port);
+ }
+ }
+
+ [[nodiscard]] bool isIPv6() const
+ {
+ return sin4.sin_family == AF_INET6;
+ }
+ [[nodiscard]] bool isIPv4() const
+ {
+ return sin4.sin_family == AF_INET;
+ }
+ [[nodiscard]] bool isUnixSocket() const
+ {
+ return sin4.sin_family == AF_UNIX;
+ }
+
+ [[nodiscard]] string toString() const
+ {
+ if (sinun.sun_family == AF_UNIX) {
+ return sinun.sun_path;
+ }
+ std::array<char, 1024> host{};
+ if (sin4.sin_family != 0) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ int retval = getnameinfo(reinterpret_cast<const struct sockaddr*>(this), getSocklen(), host.data(), host.size(), nullptr, 0, NI_NUMERICHOST);
+ if (retval == 0) {
+ return host.data();
+ }
+ return "invalid " + string(gai_strerror(retval));
+ }
+ return "invalid";
+ }
+
+ [[nodiscard]] string toStringWithPort() const
+ {
+ if (sinun.sun_family == AF_UNIX) {
+ return toString();
+ }
+ if (sin4.sin_family == AF_INET) {
+ return toString() + ":" + std::to_string(ntohs(sin4.sin_port));
+ }
+ return "[" + toString() + "]:" + std::to_string(ntohs(sin4.sin_port));
+ }
+
+ void reset()
+ {
+ memset(&sinun, 0, sizeof(sinun));
+ }
+};
+
/** This exception is thrown by the Netmask class and by extension by the NetmaskGroup class */
class NetmaskException : public PDNSException
{
return *this;
}
+Logger& Logger::operator<<(const SockaddrWrapper& sockaddr)
+{
+ *this << sockaddr.toString();
+ return *this;
+}
+
void addTraceTS(const timeval& start, ostringstream& str)
{
const auto& content = str.str();
Logger& operator<<(const string& s); //!< log a string
Logger& operator<<(const DNSName&);
Logger& operator<<(const ComboAddress&); //!< log an address
+ Logger& operator<<(const SockaddrWrapper&); //!< log an address
Logger& operator<<(Urgency); //!< set the urgency, << style
Logger& operator<<(const QType& qtype)
}
//! Bind the socket to a specified endpoint
- void bind(const ComboAddress& local, bool reuseaddr = true) const
+ template <typename T>
+ void bind(const T& local, bool reuseaddr = true) const
{
int tmp = 1;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
#include <yahttp/router.hpp>
#include <algorithm>
#include <bitset>
+#include <unistd.h>
+#include <filesystem>
+
+namespace filesystem = std::filesystem;
json11::Json HttpRequest::json()
{
void WebServer::bind()
{
+ if (filesystem::is_socket(d_listenaddress.c_str())) {
+ int err=unlink(d_listenaddress.c_str());
+ if(err < 0 && errno!=ENOENT) {
+ SLOG(g_log<<Logger::Error<<d_logprefix<<"Listening on HTTP socket failed, unable to remove existing socket at "<<d_listenaddress<<endl,
+ d_slog->error(Logr::Error, e.what(), "Listening on HTTP socket failed, unable to remove existing socket", "exception", d_listenaddress));
+ d_server = nullptr;
+ return;
+ }
+ }
+
try {
d_server = createServer();
- SLOG(g_log<<Logger::Warning<<d_logprefix<<"Listening for HTTP requests on "<<d_server->d_local.toStringWithPort()<<endl,
- d_slog->info(Logr::Info, "Listening for HTTP requests", "address", Logging::Loggable(d_server->d_local)));
+ if (d_server->d_local.isUnixSocket()) {
+ SLOG(g_log<<Logger::Warning<<d_logprefix<<"Listening for HTTP requests on "<<d_listenaddress<<endl,
+ d_slog->info(Logr::Info, "Listening for HTTP requests", "path", Logging::Loggable(d_listenaddress)));
+ } else {
+ SLOG(g_log<<Logger::Warning<<d_logprefix<<"Listening for HTTP requests on "<<d_server->d_local.toStringWithPort()<<endl,
+ d_slog->info(Logr::Info, "Listening for HTTP requests", "address", Logging::Loggable(d_server->d_local)));
+ }
}
catch(NetworkError &e) {
SLOG(g_log<<Logger::Error<<d_logprefix<<"Listening on HTTP socket failed: "<<e.what()<<endl,
if (!client) {
continue;
}
- if (client->acl(d_acl)) {
+ if (d_server->d_local.isUnixSocket() || client->acl(d_acl)) {
std::thread webHandler(WebServerConnectionThreadStart, this, client);
webHandler.detach();
} else {
}
virtual ~Server() = default;
- ComboAddress d_local;
+ SockaddrWrapper d_local;
std::shared_ptr<Socket> accept() {
return std::shared_ptr<Socket>(d_server_socket.accept());