From 93c1fb36defee84434496df5c1a2be1055915c9f Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Mon, 1 Nov 2021 21:47:43 +0100 Subject: [PATCH] dnsdist: master port for openwrt 19.07, with uci and odhcpd integration Signed-off-by: Peter van Dijk --- .github/workflows/multi-arch-test-build.yml | 7 - libs/h2o-tiny/Makefile | 57 ++ .../patches/100-socket_disable_npn.patch | 22 + .../200-libh2o-evloop_wslay-link.patch | 43 + .../patches/300-picotls-chacha-detect.patch | 17 + .../patches/400-backtrace-detection.patch | 70 ++ libs/h2o-tiny/patches/500-openssl.patch | 96 +++ libs/h2o-tiny/patches/600-engine.patch | 28 + .../patches/800-smaller-write-buffer.patch | 11 + libs/h2o/Makefile | 59 ++ libs/h2o/patches/100-socket_disable_npn.patch | 22 + .../200-libh2o-evloop_wslay-link.patch | 43 + .../patches/300-picotls-chacha-detect.patch | 17 + .../h2o/patches/400-backtrace-detection.patch | 70 ++ libs/h2o/patches/500-openssl.patch | 96 +++ libs/h2o/patches/600-engine.patch | 28 + libs/libcap/Makefile | 2 + libs/tinycdb/Makefile | 30 +- net/dnsdist-maindns/Makefile | 57 ++ net/dnsdist/Makefile | 339 +++++++- net/dnsdist/files/common.lua | 54 ++ net/dnsdist/files/configuration.lua | 796 ++++++++++++++++++ net/dnsdist/files/diag.sh | 38 + net/dnsdist/files/dnsdist-odhcpd.lua | 190 +++++ net/dnsdist/files/dnsdist.conf | 1 + net/dnsdist/files/dnsdist.config | 150 +++- net/dnsdist/files/dnsdist.init | 27 +- net/dnsdist/files/dnsdist_acl.json | 9 + net/dnsdist/files/local-domains.lua | 117 +++ net/dnsdist/files/os.lua | 126 +++ net/dnsdist/files/sample.uci.conf | 227 +++++ net/dnsdist/files/start.lua | 78 ++ ...nmp_agent_libs-instead-of-agent_libs.patch | 23 - .../patches/200-libatomic-detect.patch | 34 - .../patches/300-openssl-deprecated.patch | 29 - 35 files changed, 2878 insertions(+), 135 deletions(-) create mode 100644 libs/h2o-tiny/Makefile create mode 100644 libs/h2o-tiny/patches/100-socket_disable_npn.patch create mode 100644 libs/h2o-tiny/patches/200-libh2o-evloop_wslay-link.patch create mode 100644 libs/h2o-tiny/patches/300-picotls-chacha-detect.patch create mode 100644 libs/h2o-tiny/patches/400-backtrace-detection.patch create mode 100644 libs/h2o-tiny/patches/500-openssl.patch create mode 100644 libs/h2o-tiny/patches/600-engine.patch create mode 100644 libs/h2o-tiny/patches/800-smaller-write-buffer.patch create mode 100644 libs/h2o/Makefile create mode 100644 libs/h2o/patches/100-socket_disable_npn.patch create mode 100644 libs/h2o/patches/200-libh2o-evloop_wslay-link.patch create mode 100644 libs/h2o/patches/300-picotls-chacha-detect.patch create mode 100644 libs/h2o/patches/400-backtrace-detection.patch create mode 100644 libs/h2o/patches/500-openssl.patch create mode 100644 libs/h2o/patches/600-engine.patch create mode 100644 net/dnsdist-maindns/Makefile create mode 100644 net/dnsdist/files/common.lua create mode 100644 net/dnsdist/files/configuration.lua create mode 100755 net/dnsdist/files/diag.sh create mode 100644 net/dnsdist/files/dnsdist-odhcpd.lua create mode 100644 net/dnsdist/files/dnsdist_acl.json create mode 100644 net/dnsdist/files/local-domains.lua create mode 100644 net/dnsdist/files/os.lua create mode 100644 net/dnsdist/files/sample.uci.conf create mode 100644 net/dnsdist/files/start.lua delete mode 100644 net/dnsdist/patches/100-net-snmp-config-Use-netsnmp_agent_libs-instead-of-agent_libs.patch delete mode 100644 net/dnsdist/patches/200-libatomic-detect.patch delete mode 100644 net/dnsdist/patches/300-openssl-deprecated.patch diff --git a/.github/workflows/multi-arch-test-build.yml b/.github/workflows/multi-arch-test-build.yml index e5e3845e6f8b3f..a2d6f873f7b28d 100644 --- a/.github/workflows/multi-arch-test-build.yml +++ b/.github/workflows/multi-arch-test-build.yml @@ -11,17 +11,10 @@ jobs: fail-fast: false matrix: arch: - - arc_archs - arm_cortex-a9_vfpv3-d16 - mips_24kc - - powerpc_464fp - - powerpc_8540 runtime_test: [false] include: - - arch: aarch64_cortex-a53 - runtime_test: true - - arch: arm_cortex-a15_neon-vfpv4 - runtime_test: true - arch: x86_64 runtime_test: true diff --git a/libs/h2o-tiny/Makefile b/libs/h2o-tiny/Makefile new file mode 100644 index 00000000000000..1d128a81766359 --- /dev/null +++ b/libs/h2o-tiny/Makefile @@ -0,0 +1,57 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=h2o +PKG_VERSION:=2.2.6 +PKG_RELEASE:=$(AUTORELEASE) + +PKG_SOURCE_URL:=https://codeload.github.com/h2o/h2o/tar.gz/v${PKG_VERSION}? +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_HASH:=f8cbc1b530d85ff098f6efc2c3fdbc5e29baffb30614caac59d5c710f7bda201 + +PKG_MAINTAINER:=Peter van Dijk +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +CMAKE_OPTIONS += \ + -DBUILD_SHARED_LIBS=ON \ + -DWITH_MRUBY=OFF + +define Package/libh2o-evloop-tiny + SECTION:=libs + CATEGORY:=Libraries + TITLE:=H2O Library compiled with its own event loop + URL:=https://h2o.examp1e.net/ + DEPENDS:=+libopenssl +zlib +endef + +define Package/libh2o-tiny + SECTION:=libs + CATEGORY:=Libraries + TITLE:=H2O Library compiled with libuv + URL:=https://h2o.examp1e.net/ + DEPENDS:=+libuv +libopenssl +zlib +libyaml +endef + +define Build/InstallDev + $(call Build/InstallDev/cmake,$(1)) + $(SED) 's,/usr/include,$$$${prefix}/include,g' $(1)/usr/lib/pkgconfig/libh2o-evloop.pc + $(SED) 's,/usr/lib,$$$${exec_prefix}/lib,g' $(1)/usr/lib/pkgconfig/libh2o-evloop.pc + $(SED) 's,/usr/include,$$$${prefix}/include,g' $(1)/usr/lib/pkgconfig/libh2o.pc + $(SED) 's,/usr/lib,$$$${exec_prefix}/lib,g' $(1)/usr/lib/pkgconfig/libh2o.pc +endef + +define Package/libh2o-evloop-tiny/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libh2o-evloop.so* $(1)/usr/lib/ +endef + +define Package/libh2o-tiny/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libh2o.so* $(1)/usr/lib/ +endef + +$(eval $(call BuildPackage,libh2o-evloop-tiny)) +$(eval $(call BuildPackage,libh2o-tiny)) diff --git a/libs/h2o-tiny/patches/100-socket_disable_npn.patch b/libs/h2o-tiny/patches/100-socket_disable_npn.patch new file mode 100644 index 00000000000000..d3f9c7169819e0 --- /dev/null +++ b/libs/h2o-tiny/patches/100-socket_disable_npn.patch @@ -0,0 +1,22 @@ +--- a/include/h2o/socket.h ++++ b/include/h2o/socket.h +@@ -29,6 +29,7 @@ extern "C" { + #include + #include + #include ++#include + #include "h2o/cache.h" + #include "h2o/memory.h" + #include "h2o/openssl_backport.h" +@@ -44,7 +45,11 @@ extern "C" { + + #if OPENSSL_VERSION_NUMBER >= 0x10002000L + #define H2O_USE_ALPN 1 ++#ifndef OPENSSL_NO_NEXTPROTONEG + #define H2O_USE_NPN 1 ++#else ++#define H2O_USE_NPN 0 ++#endif + #elif OPENSSL_VERSION_NUMBER >= 0x10001000L + #define H2O_USE_ALPN 0 + #define H2O_USE_NPN 1 diff --git a/libs/h2o-tiny/patches/200-libh2o-evloop_wslay-link.patch b/libs/h2o-tiny/patches/200-libh2o-evloop_wslay-link.patch new file mode 100644 index 00000000000000..d15a6b3e9a0042 --- /dev/null +++ b/libs/h2o-tiny/patches/200-libh2o-evloop_wslay-link.patch @@ -0,0 +1,43 @@ +From f7d5cb83826c7e2b1a3dc618b434d85df130a4d5 Mon Sep 17 00:00:00 2001 +From: James Taylor +Date: Tue, 10 Dec 2019 21:58:45 +1100 +Subject: [PATCH] Explicitly link against WSLAY when available + +When other libraries attempt to link against libh2o and libh2o-evloop that was +compiled with libwslay available, there are errors from missing symbols +associated with code which makes use of the wslay library. To rectify this, +explicitly link against libwslay during the build process. + +Fixes #2105 + +Signed-off-by: James Taylor +--- + CMakeLists.txt | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -386,13 +386,21 @@ SET_TARGET_PROPERTIES(libh2o PROPERTIES + OUTPUT_NAME h2o + VERSION ${LIBRARY_VERSION} + SOVERSION ${LIBRARY_SOVERSION}) +-TARGET_LINK_LIBRARIES(libh2o ${LIBUV_LIBRARIES} ${EXTRA_LIBS}) ++IF (WSLAY_FOUND) ++ TARGET_LINK_LIBRARIES(libh2o ${WSLAY_LIBRARIES} ${LIBUV_LIBRARIES} ${EXTRA_LIBS}) ++ELSE () ++ TARGET_LINK_LIBRARIES(libh2o ${LIBUV_LIBRARIES} ${EXTRA_LIBS}) ++ENDIF (WSLAY_FOUND) + SET_TARGET_PROPERTIES(libh2o-evloop PROPERTIES + OUTPUT_NAME h2o-evloop + COMPILE_FLAGS "-DH2O_USE_LIBUV=0" + VERSION ${LIBRARY_VERSION} + SOVERSION ${LIBRARY_SOVERSION}) +-TARGET_LINK_LIBRARIES(libh2o-evloop ${EXTRA_LIBS}) ++IF (WSLAY_FOUND) ++ TARGET_LINK_LIBRARIES(libh2o-evloop ${WSLAY_LIBRARIES} ${EXTRA_LIBS}) ++ELSE () ++ TARGET_LINK_LIBRARIES(libh2o-evloop ${EXTRA_LIBS}) ++ENDIF (WSLAY_FOUND) + + IF (OPENSSL_FOUND) + TARGET_INCLUDE_DIRECTORIES(libh2o PUBLIC ${OPENSSL_INCLUDE_DIR}) diff --git a/libs/h2o-tiny/patches/300-picotls-chacha-detect.patch b/libs/h2o-tiny/patches/300-picotls-chacha-detect.patch new file mode 100644 index 00000000000000..5fc7932850422e --- /dev/null +++ b/libs/h2o-tiny/patches/300-picotls-chacha-detect.patch @@ -0,0 +1,17 @@ +--- a/deps/picotls/include/picotls/openssl.h ++++ b/deps/picotls/include/picotls/openssl.h +@@ -26,11 +26,14 @@ + #include + #include + #include ++#include + #include "../picotls.h" + + #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) ++#if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) + #define PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 + #endif ++#endif + + extern ptls_key_exchange_algorithm_t ptls_openssl_secp256r1; + extern ptls_key_exchange_algorithm_t *ptls_openssl_key_exchanges[]; diff --git a/libs/h2o-tiny/patches/400-backtrace-detection.patch b/libs/h2o-tiny/patches/400-backtrace-detection.patch new file mode 100644 index 00000000000000..d74937f17be947 --- /dev/null +++ b/libs/h2o-tiny/patches/400-backtrace-detection.patch @@ -0,0 +1,70 @@ +From 03dbd6757d043581b5d250107b6f1cda6ae203a9 Mon Sep 17 00:00:00 2001 +From: Frederik Deweerdt +Date: Wed, 25 Oct 2017 13:52:28 -0700 +Subject: [PATCH] Autodetect backtrace and backtrace_symbols_fd + +--- + CMakeLists.txt | 13 +++++++++++++ + src/main.c | 10 ++++++---- + 2 files changed, 19 insertions(+), 4 deletions(-) + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -58,6 +58,19 @@ __sync_add_and_fetch(&a, 1); + return 0; + }" ARCH_SUPPORTS_64BIT_ATOMICS) + ++CHECK_C_SOURCE_COMPILES(" ++#include ++int main(void) { ++void *p[10]; ++int ret = backtrace(p, 10); ++backtrace_symbols_fd(p, ret, 2); ++return 0; ++}" LIBC_HAS_BACKTRACE) ++ ++IF (LIBC_HAS_BACKTRACE) ++ ADD_DEFINITIONS("-DLIBC_HAS_BACKTRACE") ++ENDIF () ++ + SET(WITH_BUNDLED_SSL_DEFAULT "ON") + IF ((NOT UNIX) OR CYGWIN) + SET(WITH_BUNDLED_SSL_DEFAULT "OFF") +--- a/src/main.c ++++ b/src/main.c +@@ -48,7 +48,7 @@ + #include + #include + #include +-#ifdef __GLIBC__ ++#ifdef LIBC_HAS_BACKTRACE + #include + #endif + #if H2O_USE_PICOTLS +@@ -1436,7 +1436,8 @@ static void on_sigterm(int signo) + notify_all_threads(); + } + +-#ifdef __GLIBC__ ++#ifdef LIBC_HAS_BACKTRACE ++ + static int popen_crash_handler(void) + { + char *cmd_fullpath = h2o_configurator_get_cmd_path(conf.crash_handler), *argv[] = {cmd_fullpath, NULL}; +@@ -1488,13 +1489,14 @@ static void on_sigfatal(int signo) + + raise(signo); + } +-#endif ++ ++#endif /* LIBC_HAS_BACKTRACE */ + + static void setup_signal_handlers(void) + { + h2o_set_signal_handler(SIGTERM, on_sigterm); + h2o_set_signal_handler(SIGPIPE, SIG_IGN); +-#ifdef __GLIBC__ ++#ifdef LIBC_HAS_BACKTRACE + if ((crash_handler_fd = popen_crash_handler()) == -1) + crash_handler_fd = 2; + h2o_set_signal_handler(SIGABRT, on_sigfatal); diff --git a/libs/h2o-tiny/patches/500-openssl.patch b/libs/h2o-tiny/patches/500-openssl.patch new file mode 100644 index 00000000000000..609077ee235eed --- /dev/null +++ b/libs/h2o-tiny/patches/500-openssl.patch @@ -0,0 +1,96 @@ +--- a/deps/neverbleed/neverbleed.c ++++ b/deps/neverbleed/neverbleed.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + #include + #include +--- a/deps/picotls/lib/openssl.c ++++ b/deps/picotls/lib/openssl.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -941,7 +942,7 @@ int ptls_openssl_encrypt_ticket(ptls_buf + + Exit: + if (cctx != NULL) +- EVP_CIPHER_CTX_cleanup(cctx); ++ EVP_CIPHER_CTX_reset(cctx); + if (hctx != NULL) + HMAC_CTX_free(hctx); + return ret; +@@ -1011,7 +1012,7 @@ int ptls_openssl_decrypt_ticket(ptls_buf + + Exit: + if (cctx != NULL) +- EVP_CIPHER_CTX_cleanup(cctx); ++ EVP_CIPHER_CTX_reset(cctx); + if (hctx != NULL) + HMAC_CTX_free(hctx); + return ret; +--- a/src/main.c ++++ b/src/main.c +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1827,7 +1828,7 @@ static h2o_iovec_t on_extra_status(void + " \"listeners\": %zu,\n" + " \"worker-threads\": %zu,\n" + " \"num-sessions\": %lu", +- SSLeay_version(SSLEAY_VERSION), current_time, restart_time, (uint64_t)(now - conf.launch_time), generation, ++ OpenSSL_version(OPENSSL_VERSION), current_time, restart_time, (uint64_t)(now - conf.launch_time), generation, + num_connections(0), conf.max_connections, conf.num_listeners, conf.num_threads, num_sessions(0)); + assert(ret.len < BUFSIZE); + +@@ -2008,7 +2009,7 @@ int main(int argc, char **argv) + break; + case 'v': + printf("h2o version " H2O_VERSION "\n"); +- printf("OpenSSL: %s\n", SSLeay_version(SSLEAY_VERSION)); ++ printf("OpenSSL: %s\n", OpenSSL_version(OPENSSL_VERSION)); + #if H2O_USE_MRUBY + printf( + "mruby: YES\n"); /* TODO determine the way to obtain the version of mruby (that is being linked dynamically) */ +--- a/src/ssl.c ++++ b/src/ssl.c +@@ -911,6 +911,7 @@ void ssl_setup_session_resumption(SSL_CT + #endif + } + ++#if OPENSSL_VERSION_NUMBER < 0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER) + static pthread_mutex_t *mutexes; + + static void lock_callback(int mode, int n, const char *file, int line) +@@ -937,9 +938,11 @@ static int add_lock_callback(int *num, i + + return __sync_add_and_fetch(num, amount); + } ++#endif + + void init_openssl(void) + { ++#if OPENSSL_VERSION_NUMBER < 0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER) + int nlocks = CRYPTO_num_locks(), i; + mutexes = h2o_mem_alloc(sizeof(*mutexes) * nlocks); + for (i = 0; i != nlocks; ++i) +@@ -953,6 +956,7 @@ void init_openssl(void) + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); ++#endif + + cache_init_defaults(); + #if H2O_USE_SESSION_TICKETS diff --git a/libs/h2o-tiny/patches/600-engine.patch b/libs/h2o-tiny/patches/600-engine.patch new file mode 100644 index 00000000000000..90f677d97bbf0a --- /dev/null +++ b/libs/h2o-tiny/patches/600-engine.patch @@ -0,0 +1,28 @@ +--- a/deps/neverbleed/neverbleed.c ++++ b/deps/neverbleed/neverbleed.c +@@ -1486,6 +1486,7 @@ int neverbleed_init(neverbleed_t *nb, ch + close(pipe_fds[0]); + pipe_fds[0] = -1; + ++#ifndef OPENSSL_NO_ENGINE + /* setup engine */ + if ((nb->engine = ENGINE_new()) == NULL || !ENGINE_set_id(nb->engine, "neverbleed") || + !ENGINE_set_name(nb->engine, "privilege separation software engine") || !ENGINE_set_RSA(nb->engine, rsa_method) +@@ -1497,6 +1498,7 @@ int neverbleed_init(neverbleed_t *nb, ch + goto Fail; + } + ENGINE_add(nb->engine); ++#endif + + /* setup thread key */ + pthread_key_create(&nb->thread_key, dispose_thread_data); +@@ -1515,7 +1517,9 @@ Fail: + if (listen_fd != -1) + close(listen_fd); + if (nb->engine != NULL) { ++#ifndef OPENSSL_NO_ENGINE + ENGINE_free(nb->engine); ++#endif + nb->engine = NULL; + } + return -1; diff --git a/libs/h2o-tiny/patches/800-smaller-write-buffer.patch b/libs/h2o-tiny/patches/800-smaller-write-buffer.patch new file mode 100644 index 00000000000000..5527ad57dad2e7 --- /dev/null +++ b/libs/h2o-tiny/patches/800-smaller-write-buffer.patch @@ -0,0 +1,11 @@ +--- a/include/h2o/http2_internal.h ++++ b/include/h2o/http2_internal.h +@@ -33,7 +33,7 @@ + typedef struct st_h2o_http2_conn_t h2o_http2_conn_t; + typedef struct st_h2o_http2_stream_t h2o_http2_stream_t; + +-#define H2O_HTTP2_DEFAULT_OUTBUF_SIZE 81920 /* the target size of each write call; connection flow control window + alpha */ ++#define H2O_HTTP2_DEFAULT_OUTBUF_SIZE 8192 /* the target size of each write call; connection flow control window + alpha */ + #define H2O_HTTP2_DEFAULT_OUTBUF_SOFT_MAX_SIZE 524288 /* 512KB; stops reading if size exceeds this value */ + + /* hpack */ diff --git a/libs/h2o/Makefile b/libs/h2o/Makefile new file mode 100644 index 00000000000000..59f2e30476ab65 --- /dev/null +++ b/libs/h2o/Makefile @@ -0,0 +1,59 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=h2o +PKG_VERSION:=2.2.6 +PKG_RELEASE:=$(AUTORELEASE) + +PKG_SOURCE_URL:=https://codeload.github.com/h2o/h2o/tar.gz/v${PKG_VERSION}? +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_HASH:=f8cbc1b530d85ff098f6efc2c3fdbc5e29baffb30614caac59d5c710f7bda201 + +PKG_MAINTAINER:=Peter van Dijk +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +PKG_BUILD_DEPENDS:=libwslay + +CMAKE_OPTIONS += \ + -DBUILD_SHARED_LIBS=ON \ + -DWITH_MRUBY=OFF + +define Package/libh2o-evloop + SECTION:=libs + CATEGORY:=Libraries + TITLE:=H2O Library compiled with its own event loop + URL:=https://h2o.examp1e.net/ + DEPENDS:=+libopenssl +zlib +libyaml +endef + +define Package/libh2o + SECTION:=libs + CATEGORY:=Libraries + TITLE:=H2O Library compiled with libuv + URL:=https://h2o.examp1e.net/ + DEPENDS:=+libuv +libopenssl +zlib +libyaml +endef + +define Build/InstallDev + $(call Build/InstallDev/cmake,$(1)) + $(SED) 's,/usr/include,$$$${prefix}/include,g' $(1)/usr/lib/pkgconfig/libh2o-evloop.pc + $(SED) 's,/usr/lib,$$$${exec_prefix}/lib,g' $(1)/usr/lib/pkgconfig/libh2o-evloop.pc + $(SED) 's,/usr/include,$$$${prefix}/include,g' $(1)/usr/lib/pkgconfig/libh2o.pc + $(SED) 's,/usr/lib,$$$${exec_prefix}/lib,g' $(1)/usr/lib/pkgconfig/libh2o.pc +endef + +define Package/libh2o-evloop/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libh2o-evloop.so* $(1)/usr/lib/ +endef + +define Package/libh2o/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libh2o.so* $(1)/usr/lib/ +endef + +$(eval $(call BuildPackage,libh2o-evloop)) +$(eval $(call BuildPackage,libh2o)) diff --git a/libs/h2o/patches/100-socket_disable_npn.patch b/libs/h2o/patches/100-socket_disable_npn.patch new file mode 100644 index 00000000000000..d3f9c7169819e0 --- /dev/null +++ b/libs/h2o/patches/100-socket_disable_npn.patch @@ -0,0 +1,22 @@ +--- a/include/h2o/socket.h ++++ b/include/h2o/socket.h +@@ -29,6 +29,7 @@ extern "C" { + #include + #include + #include ++#include + #include "h2o/cache.h" + #include "h2o/memory.h" + #include "h2o/openssl_backport.h" +@@ -44,7 +45,11 @@ extern "C" { + + #if OPENSSL_VERSION_NUMBER >= 0x10002000L + #define H2O_USE_ALPN 1 ++#ifndef OPENSSL_NO_NEXTPROTONEG + #define H2O_USE_NPN 1 ++#else ++#define H2O_USE_NPN 0 ++#endif + #elif OPENSSL_VERSION_NUMBER >= 0x10001000L + #define H2O_USE_ALPN 0 + #define H2O_USE_NPN 1 diff --git a/libs/h2o/patches/200-libh2o-evloop_wslay-link.patch b/libs/h2o/patches/200-libh2o-evloop_wslay-link.patch new file mode 100644 index 00000000000000..d15a6b3e9a0042 --- /dev/null +++ b/libs/h2o/patches/200-libh2o-evloop_wslay-link.patch @@ -0,0 +1,43 @@ +From f7d5cb83826c7e2b1a3dc618b434d85df130a4d5 Mon Sep 17 00:00:00 2001 +From: James Taylor +Date: Tue, 10 Dec 2019 21:58:45 +1100 +Subject: [PATCH] Explicitly link against WSLAY when available + +When other libraries attempt to link against libh2o and libh2o-evloop that was +compiled with libwslay available, there are errors from missing symbols +associated with code which makes use of the wslay library. To rectify this, +explicitly link against libwslay during the build process. + +Fixes #2105 + +Signed-off-by: James Taylor +--- + CMakeLists.txt | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -386,13 +386,21 @@ SET_TARGET_PROPERTIES(libh2o PROPERTIES + OUTPUT_NAME h2o + VERSION ${LIBRARY_VERSION} + SOVERSION ${LIBRARY_SOVERSION}) +-TARGET_LINK_LIBRARIES(libh2o ${LIBUV_LIBRARIES} ${EXTRA_LIBS}) ++IF (WSLAY_FOUND) ++ TARGET_LINK_LIBRARIES(libh2o ${WSLAY_LIBRARIES} ${LIBUV_LIBRARIES} ${EXTRA_LIBS}) ++ELSE () ++ TARGET_LINK_LIBRARIES(libh2o ${LIBUV_LIBRARIES} ${EXTRA_LIBS}) ++ENDIF (WSLAY_FOUND) + SET_TARGET_PROPERTIES(libh2o-evloop PROPERTIES + OUTPUT_NAME h2o-evloop + COMPILE_FLAGS "-DH2O_USE_LIBUV=0" + VERSION ${LIBRARY_VERSION} + SOVERSION ${LIBRARY_SOVERSION}) +-TARGET_LINK_LIBRARIES(libh2o-evloop ${EXTRA_LIBS}) ++IF (WSLAY_FOUND) ++ TARGET_LINK_LIBRARIES(libh2o-evloop ${WSLAY_LIBRARIES} ${EXTRA_LIBS}) ++ELSE () ++ TARGET_LINK_LIBRARIES(libh2o-evloop ${EXTRA_LIBS}) ++ENDIF (WSLAY_FOUND) + + IF (OPENSSL_FOUND) + TARGET_INCLUDE_DIRECTORIES(libh2o PUBLIC ${OPENSSL_INCLUDE_DIR}) diff --git a/libs/h2o/patches/300-picotls-chacha-detect.patch b/libs/h2o/patches/300-picotls-chacha-detect.patch new file mode 100644 index 00000000000000..5fc7932850422e --- /dev/null +++ b/libs/h2o/patches/300-picotls-chacha-detect.patch @@ -0,0 +1,17 @@ +--- a/deps/picotls/include/picotls/openssl.h ++++ b/deps/picotls/include/picotls/openssl.h +@@ -26,11 +26,14 @@ + #include + #include + #include ++#include + #include "../picotls.h" + + #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) ++#if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) + #define PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 + #endif ++#endif + + extern ptls_key_exchange_algorithm_t ptls_openssl_secp256r1; + extern ptls_key_exchange_algorithm_t *ptls_openssl_key_exchanges[]; diff --git a/libs/h2o/patches/400-backtrace-detection.patch b/libs/h2o/patches/400-backtrace-detection.patch new file mode 100644 index 00000000000000..d74937f17be947 --- /dev/null +++ b/libs/h2o/patches/400-backtrace-detection.patch @@ -0,0 +1,70 @@ +From 03dbd6757d043581b5d250107b6f1cda6ae203a9 Mon Sep 17 00:00:00 2001 +From: Frederik Deweerdt +Date: Wed, 25 Oct 2017 13:52:28 -0700 +Subject: [PATCH] Autodetect backtrace and backtrace_symbols_fd + +--- + CMakeLists.txt | 13 +++++++++++++ + src/main.c | 10 ++++++---- + 2 files changed, 19 insertions(+), 4 deletions(-) + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -58,6 +58,19 @@ __sync_add_and_fetch(&a, 1); + return 0; + }" ARCH_SUPPORTS_64BIT_ATOMICS) + ++CHECK_C_SOURCE_COMPILES(" ++#include ++int main(void) { ++void *p[10]; ++int ret = backtrace(p, 10); ++backtrace_symbols_fd(p, ret, 2); ++return 0; ++}" LIBC_HAS_BACKTRACE) ++ ++IF (LIBC_HAS_BACKTRACE) ++ ADD_DEFINITIONS("-DLIBC_HAS_BACKTRACE") ++ENDIF () ++ + SET(WITH_BUNDLED_SSL_DEFAULT "ON") + IF ((NOT UNIX) OR CYGWIN) + SET(WITH_BUNDLED_SSL_DEFAULT "OFF") +--- a/src/main.c ++++ b/src/main.c +@@ -48,7 +48,7 @@ + #include + #include + #include +-#ifdef __GLIBC__ ++#ifdef LIBC_HAS_BACKTRACE + #include + #endif + #if H2O_USE_PICOTLS +@@ -1436,7 +1436,8 @@ static void on_sigterm(int signo) + notify_all_threads(); + } + +-#ifdef __GLIBC__ ++#ifdef LIBC_HAS_BACKTRACE ++ + static int popen_crash_handler(void) + { + char *cmd_fullpath = h2o_configurator_get_cmd_path(conf.crash_handler), *argv[] = {cmd_fullpath, NULL}; +@@ -1488,13 +1489,14 @@ static void on_sigfatal(int signo) + + raise(signo); + } +-#endif ++ ++#endif /* LIBC_HAS_BACKTRACE */ + + static void setup_signal_handlers(void) + { + h2o_set_signal_handler(SIGTERM, on_sigterm); + h2o_set_signal_handler(SIGPIPE, SIG_IGN); +-#ifdef __GLIBC__ ++#ifdef LIBC_HAS_BACKTRACE + if ((crash_handler_fd = popen_crash_handler()) == -1) + crash_handler_fd = 2; + h2o_set_signal_handler(SIGABRT, on_sigfatal); diff --git a/libs/h2o/patches/500-openssl.patch b/libs/h2o/patches/500-openssl.patch new file mode 100644 index 00000000000000..609077ee235eed --- /dev/null +++ b/libs/h2o/patches/500-openssl.patch @@ -0,0 +1,96 @@ +--- a/deps/neverbleed/neverbleed.c ++++ b/deps/neverbleed/neverbleed.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + #include + #include +--- a/deps/picotls/lib/openssl.c ++++ b/deps/picotls/lib/openssl.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -941,7 +942,7 @@ int ptls_openssl_encrypt_ticket(ptls_buf + + Exit: + if (cctx != NULL) +- EVP_CIPHER_CTX_cleanup(cctx); ++ EVP_CIPHER_CTX_reset(cctx); + if (hctx != NULL) + HMAC_CTX_free(hctx); + return ret; +@@ -1011,7 +1012,7 @@ int ptls_openssl_decrypt_ticket(ptls_buf + + Exit: + if (cctx != NULL) +- EVP_CIPHER_CTX_cleanup(cctx); ++ EVP_CIPHER_CTX_reset(cctx); + if (hctx != NULL) + HMAC_CTX_free(hctx); + return ret; +--- a/src/main.c ++++ b/src/main.c +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1827,7 +1828,7 @@ static h2o_iovec_t on_extra_status(void + " \"listeners\": %zu,\n" + " \"worker-threads\": %zu,\n" + " \"num-sessions\": %lu", +- SSLeay_version(SSLEAY_VERSION), current_time, restart_time, (uint64_t)(now - conf.launch_time), generation, ++ OpenSSL_version(OPENSSL_VERSION), current_time, restart_time, (uint64_t)(now - conf.launch_time), generation, + num_connections(0), conf.max_connections, conf.num_listeners, conf.num_threads, num_sessions(0)); + assert(ret.len < BUFSIZE); + +@@ -2008,7 +2009,7 @@ int main(int argc, char **argv) + break; + case 'v': + printf("h2o version " H2O_VERSION "\n"); +- printf("OpenSSL: %s\n", SSLeay_version(SSLEAY_VERSION)); ++ printf("OpenSSL: %s\n", OpenSSL_version(OPENSSL_VERSION)); + #if H2O_USE_MRUBY + printf( + "mruby: YES\n"); /* TODO determine the way to obtain the version of mruby (that is being linked dynamically) */ +--- a/src/ssl.c ++++ b/src/ssl.c +@@ -911,6 +911,7 @@ void ssl_setup_session_resumption(SSL_CT + #endif + } + ++#if OPENSSL_VERSION_NUMBER < 0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER) + static pthread_mutex_t *mutexes; + + static void lock_callback(int mode, int n, const char *file, int line) +@@ -937,9 +938,11 @@ static int add_lock_callback(int *num, i + + return __sync_add_and_fetch(num, amount); + } ++#endif + + void init_openssl(void) + { ++#if OPENSSL_VERSION_NUMBER < 0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER) + int nlocks = CRYPTO_num_locks(), i; + mutexes = h2o_mem_alloc(sizeof(*mutexes) * nlocks); + for (i = 0; i != nlocks; ++i) +@@ -953,6 +956,7 @@ void init_openssl(void) + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); ++#endif + + cache_init_defaults(); + #if H2O_USE_SESSION_TICKETS diff --git a/libs/h2o/patches/600-engine.patch b/libs/h2o/patches/600-engine.patch new file mode 100644 index 00000000000000..90f677d97bbf0a --- /dev/null +++ b/libs/h2o/patches/600-engine.patch @@ -0,0 +1,28 @@ +--- a/deps/neverbleed/neverbleed.c ++++ b/deps/neverbleed/neverbleed.c +@@ -1486,6 +1486,7 @@ int neverbleed_init(neverbleed_t *nb, ch + close(pipe_fds[0]); + pipe_fds[0] = -1; + ++#ifndef OPENSSL_NO_ENGINE + /* setup engine */ + if ((nb->engine = ENGINE_new()) == NULL || !ENGINE_set_id(nb->engine, "neverbleed") || + !ENGINE_set_name(nb->engine, "privilege separation software engine") || !ENGINE_set_RSA(nb->engine, rsa_method) +@@ -1497,6 +1498,7 @@ int neverbleed_init(neverbleed_t *nb, ch + goto Fail; + } + ENGINE_add(nb->engine); ++#endif + + /* setup thread key */ + pthread_key_create(&nb->thread_key, dispose_thread_data); +@@ -1515,7 +1517,9 @@ Fail: + if (listen_fd != -1) + close(listen_fd); + if (nb->engine != NULL) { ++#ifndef OPENSSL_NO_ENGINE + ENGINE_free(nb->engine); ++#endif + nb->engine = NULL; + } + return -1; diff --git a/libs/libcap/Makefile b/libs/libcap/Makefile index 8372297284463c..e71b798c7f06ca 100644 --- a/libs/libcap/Makefile +++ b/libs/libcap/Makefile @@ -71,6 +71,8 @@ define Build/InstallDev $(CP) $(PKG_INSTALL_DIR)/usr/include/* $(1)/usr/include/ $(INSTALL_DIR) $(1)/usr/lib/ $(CP) $(PKG_INSTALL_DIR)/lib/* $(1)/usr/lib/ + $(INSTALL_DIR) $(1)/usr/lib/pkgconfig + $(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/libcap.pc $(1)/usr/lib/pkgconfig/ endef define Package/libcap/install diff --git a/libs/tinycdb/Makefile b/libs/tinycdb/Makefile index c633c62ab9e9eb..05d56ca041428a 100644 --- a/libs/tinycdb/Makefile +++ b/libs/tinycdb/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=tinycdb -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE_URL:=http://www.corpit.ru/mjt/tinycdb/ PKG_VERSION:=0.78 PKG_HASH:=50678f432d8ada8d69f728ec11c3140e151813a7847cf30a62d86f3a720ed63c @@ -17,23 +17,47 @@ PKG_LICENSE:=NLPL PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_INSTALL:=1 + include $(INCLUDE_DIR)/package.mk +# Pass CPPFLAGS in the CFLAGS as otherwise the build system will +# ignore them. +TARGET_CFLAGS+=$(TARGET_CPPFLAGS) + +MAKE_FLAGS+= \ + CFLAGS="$(TARGET_CFLAGS)" \ + LDFLAGS="$(TARGET_LDFLAGS)" + +CDB_INST_STRING:=prefix=/usr install install-sharedlib install-piclib + define Package/tinycdb SECTION:=libs CATEGORY:=Libraries TITLE:=a Constant DataBase URL:=http://www.corpit.ru/mjt/tinycdb.html + ABI_VERSION=1 endef define Package/tinycdb/description TinyCDB is a very fast and simple package for creating and reading constant data bases endef -define Build/InstallDev - cd $(PKG_BUILD_DIR); $(MAKE) DESTDIR=$(1) prefix=/usr install +define Package/tinycdb/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libcdb.so.$(ABI_VERSION) $(1)/usr/lib endef +define Build/Compile + $(call Build/Compile/Default,shared staticlib piclib) +endef + +define Build/Install + $(call Build/Install/Default,$(CDB_INST_STRING)) +endef +define Build/InstallDev + cd $(PKG_BUILD_DIR); $(MAKE) DESTDIR=$(1) $(CDB_INST_STRING) +endef $(eval $(call BuildPackage,tinycdb)) diff --git a/net/dnsdist-maindns/Makefile b/net/dnsdist-maindns/Makefile new file mode 100644 index 00000000000000..bf0c74644f2969 --- /dev/null +++ b/net/dnsdist-maindns/Makefile @@ -0,0 +1,57 @@ +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=dnsdist-maindns +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +PKG_MAINTAINER:=Peter van Dijk + +define Package/dnsdist-maindns + SECTION:=net + CATEGORY:=Network + TITLE:=This package disables dnsmasq so dnsdist can become the main DNS on the CPE + URL:=http://dnsdist.org + DEPENDS:=dnsdist odhcpd + PKGARCH:=all +endef + +define Package/dnsdist-maindns/description + This package disables dnsmasq so dnsdist can become the main DNS on the CPE +endef + +define Build/Compile + echo nothing to do here +endef + +define Package/dnsdist-maindns/install +# $(INSTALL_DIR) $(1)/etc +# touch $(1)/etc/dnsdist-maindns-installed +endef + +define Package/dnsdist-maindns/postinst +#!/bin/sh + +uci set 'dhcp.@dnsmasq[0].port=5353' +uci set 'dhcp.@dnsmasq[0].disabled=1' +uci set dhcp.odhcpd.maindhcp=1 +uci set dhcp.lan.dhcpv4=server +uci commit dhcp +uci set dnsdist.general.enabled=1 +uci commit dnsdist +/etc/init.d/dnsmasq disable +/etc/init.d/dnsdist enable || true + +echo +echo dnsmasq disabled, odhcpd set up, dnsdist enabled +echo Please reboot +echo + +endef + +$(eval $(call BuildPackage,dnsdist-maindns)) diff --git a/net/dnsdist/Makefile b/net/dnsdist/Makefile index 5947a4db505412..4a8997bde07a27 100644 --- a/net/dnsdist/Makefile +++ b/net/dnsdist/Makefile @@ -1,22 +1,22 @@ include $(TOPDIR)/rules.mk PKG_NAME:=dnsdist -PKG_VERSION:=1.3.3 -PKG_RELEASE:=4 +PKG_VERSION:=1.8.0-alpha0.1940.master.g8dedccd5d +PKG_RELEASE:=$(AUTORELEASE) PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 -PKG_SOURCE_URL:=https://downloads.powerdns.com/releases/ -PKG_HASH:=9fb24f9032025955169f3c6e9b0a05b6aa9d6441ec47da08d22de1c1aa23e8cf +PKG_SOURCE_URL:=https://downloads.powerdns.com/autobuilt/dnsdist/1.8.0-alpha0.1940.master.g8dedccd5d/ +PKG_HASH:=22932115c7f061f92ab4e70e680df71b43ba355a3b342057c11d4c7bda76c2f2 -PKG_MAINTAINER:=James Taylor +PKG_MAINTAINER:=Peter van Dijk PKG_LICENSE:=GPL-2.0-only PKG_LICENSE_FILES:=COPYING +PKG_CPE_ID:=cpe:/a:powerdns:dnsdist PKG_INSTALL:=1 PKG_BUILD_PARALLEL:=1 -PKG_ASLR_PIE:=0 -PKG_BUILD_DEPENDS:=protobuf/host +PKG_BUILD_DEPENDS:=boost PKG_CONFIG_DEPENDS:= \ CONFIG_DNSDIST_GNUTLS \ @@ -25,22 +25,208 @@ PKG_CONFIG_DEPENDS:= \ include $(INCLUDE_DIR)/package.mk define Package/dnsdist/config -comment "SSL support" +menu "Configuration" + depends on PACKAGE_dnsdist + + comment "SSL Support" + choice + prompt "Selected SSL library" + default DNSDIST_OPENSSL + + config DNSDIST_OPENSSL + bool "OpenSSL" + + config DNSDIST_GNUTLS + bool "GnuTLS" + + config DNSDIST_NOSSL + bool "No SSL support" + + endchoice + + comment "DNS over HTTPS/TLS Support" + depends on !DNSDIST_NOSSL + + config DNSDIST_DNS_OVER_HTTPS + depends on DNSDIST_OPENSSL + depends on !DNSDIST_NOSSL + bool "DNS over HTTPS Support" + help + "Enables DNS over HTTPS Support for dnsdist" + default y + + config DNSDIST_DNS_OVER_HTTPS_OUTGOING + depends on DNSDIST_OPENSSL + depends on !DNSDIST_NOSSL + bool "Outgoing DNS over HTTPS Support" + help + "Enables Outgoing DNS over HTTPS Support for dnsdist" + default y + + config DNSDIST_DNS_OVER_TLS + depends on !DNSDIST_NOSSL + bool "DNS over TLS Support" + help + "Enabled DNS over TLS Support for dnsdist" + default y + + config DNSDIST_CARBON + bool "CARBON support" + help + "Enable Carbon (Graphite) support for dnsdist" + default n + + config DNSDIST_CDB + bool "CDB support" + help + "Enable CDB support for dnsdist" + default n + + config DNSDIST_COMPLETION + bool "Console completion support" + help + "Enable console completion for dnsdist" + default n + + config DNSDIST_DEBUG_SYMBOLS + bool "Debug symbols" + help + "Enable debug symbols in the dnsdist binary" + default n + + config DNSDIST_DELAY_PIPE + bool "Delay action support" + help + "Enable delay action support for dnsdist" + default n + + config DNSDIST_DYNBLOCKS + bool "Dynamic blocks support" + help + "Enable dynamic blocks support for dnsdist" + default n + + config DNSDIST_DNSTAP + bool "DNSTAP support" + help + "Enable DNSTAP support for dnsdist" + default n + + config DNSDIST_EBPF + bool "eBPF support" + help + "Enable eBPF support for dnsdist" + default n + + config DNSDIST_ECS_ACTIONS + bool "ECS actions" + help + "Enable actions that control EDNS Client Subnet support for dnsdist" + default n + + config DNSDIST_HASHED + bool "Hashed credentials" + help + "Enable credentials hashing support for dnsdist" + default n + + config DNSDIST_IPCIPHER + bool "IP cipher support" + help + "Enable IP cipher support for dnsdist" + default n -choice - prompt "Selected SSL library" - default DNSDIST_OPENSSL + config DNSDIST_LIBEDIT + bool "Build with libedit" + help + "Build with libedit - for completion, history and line editing" + default n - config DNSDIST_OPENSSL - bool "OpenSSL" + config DNSDIST_LMDB + bool "LMDB support" + help + "Enable LMDB support for dnsdist" + default n - config DNSDIST_GNUTLS - bool "GnuTLS" + config DNSDIST_LUA_ADVANCED + bool "Lua advanced bindings" + help + "Enable Lua bindings for queries and responses manipulation in dnsdist" + default n - config DNSDIST_NOSSL - bool "No SSL support" + config DNSDIST_MAC + bool "MAC address support" + help + "Enable MAC address in ring buffers support for dnsdist" + default y -endchoice + config DNSDIST_NET_SNMP + bool "Net-SNMP support" + help + "Enable Net-SNMP support for dnsdist" + default n + + config DNSDIST_OCSP_STAPLING + bool "OCSP Stapling support" + help + "Enable OCSP Stapling support for dnsdist" + default n + + config DNSDIST_PIE + bool "Position Independent Executable" + help + "Build dnsdist as a Position-Independent executable. This is required to benefit from ASLR, but significantly increases the required disk space and memory usage" + default y + + config DNSDIST_PROTOBUF + bool "Protobuf support" + help + "Enable exporting queries and responses over Protocol Buffer for dnsdist" + default n + + config DNSDIST_PROMETHEUS + bool "Prometheus support" + help + "Enable Prometheus support for dnsdist" + default n + + config DNSDIST_RE2 + bool "RE2 support" + help + "Enable RE2 support for dnsdist" + default n + + config DNSDIST_RULES_ALTER + bool "Rules altering queries" + help + "Enable rules altering queries for dnsdist" + default n + + config DNSDIST_SECPOLL + bool "Security polling" + help + "Enable security polling support for dnsdist" + default n + + config DNSDIST_SODIUM + bool "Build with libsodium" + help + "Build with libsodium - for encrypted console connections, and DNSCrypt" + default n + + config DNSDIST_TOP_N + bool "Top N bindings" + help + "Enable bindings to get the top N queries and responses in dnsdist" + default n + + config DNSDIST_WEB + bool "Internal web server" + help + "Enable support for the internal web server in dnsdist" + default n + +endmenu endef define Package/dnsdist @@ -48,7 +234,28 @@ define Package/dnsdist CATEGORY:=Network SUBMENU:=IP Addresses and Names TITLE:=dnsdist DNS-, DOS- and abuse-aware loadbalancer - DEPENDS:=+DNSDIST_OPENSSL:libopenssl +DNSDIST_GNUTLS:libgnutls +protobuf +re2 +libedit +libfstrm +libsodium +lua +boost +libnetsnmp +libatomic + USERID:=dnsdist:dnsdist + DEPENDS:= \ + +ca-bundle \ + +libatomic \ + +libcap \ + +libstdcpp \ + +libubox-lua \ + +libubus-lua \ + +libuci-lua \ + +luajit \ + +luafilesystem \ + +DNSDIST_LIBEDIT:libedit \ + +DNSDIST_DNSTAP:libfstrm \ + +DNSDIST_GNUTLS:libgnutls \ + +DNSDIST_DNS_OVER_HTTPS:libh2o-evloop-tiny \ + +DNSDIST_NET_SNMP:libnetsnmp \ + +DNSDIST_DNS_OVER_HTTPS_OUTGOING:libnghttp2 \ + +DNSDIST_OPENSSL:libopenssl \ + +DNSDIST_SODIUM:libsodium \ + +DNSDIST_LMDB:lmdb \ + +DNSDIST_CDB:tinycdb \ + +DNSDIST_RE2:re2 URL:=https://dnsdist.org/ endef @@ -64,27 +271,97 @@ define Package/dnsdist/conffiles /etc/init.d/dnsdist endef +# not everything groks --disable-nls +DISABLE_NLS:= + +# disable PIE for this package, see CONFIG_DNSDIST_PIE +PKG_ASLR_PIE:=0 + +ifneq ($(CONFIG_DNSDIST_DEBUG_SYMBOLS),) + RSTRIP:=: + STRIP:=: +endif + +# OpenWRT's setting of CXX destroys dnsdist's -std=c++17 +# --with-re2 compensates for that because it compensates for a bug in re2.pc that also destroys it +# so this addition is for the --without-re2 case +# +# none of this is pretty +TARGET_CXX+=-std=c++17 +TARGET_CFLAGS+=-Os -fvisibility=hidden -flto -fno-ipa-cp -DNDEBUG +TARGET_CXXFLAGS+=-Os -fvisibility=hidden -flto -fno-ipa-cp -DNDEBUG \ + -DDISABLE_DEPRECATED_DYNBLOCK -DDISABLE_RECVMMSG -DDISABLE_NPN -DDISABLE_FALSE_SHARING_PADDING -DUSE_SINGLE_ACCEPTOR_THREAD -DOPENSSL_NO_ENGINE -DDISABLE_OPENSSL_ERROR_STRINGS \ + $(if $(CONFIG_DNSDIST_CARBON),,-DDISABLE_CARBON) \ + $(if $(CONFIG_DNSDIST_COMPLETION),,-DDISABLE_COMPLETION) \ + $(if $(CONFIG_DNSDIST_DEBUG_SYMBOLS),-g3,) \ + $(if $(CONFIG_DNSDIST_DELAY_PIPE),,-DDISABLE_DELAY_PIPE) \ + $(if $(CONFIG_DNSDIST_DYNBLOCKS),,-DDISABLE_DYNBLOCKS) \ + $(if $(CONFIG_DNSDIST_ECS_ACTIONS),,-DDISABLE_ECS_ACTIONS) \ + $(if $(CONFIG_DNSDIST_HASHED),,-DDISABLE_HASHED_CREDENTIALS) \ + $(if $(CONFIG_DNSDIST_LUA_ADVANCED),,-DDISABLE_NON_FFI_DQ_BINDINGS -DDISABLE_POLICIES_BINDINGS -DDISABLE_DOWNSTREAM_BINDINGS -DDISABLE_DNSHEADER_BINDINGS -DDISABLE_COMBO_ADDR_BINDINGS -DDISABLE_QPS_LIMITER_BINDINGS -DDISABLE_PACKETCACHE_BINDINGS -DDISABLE_CLIENT_STATE_BINDINGS -DDISABLE_DNSPACKET_BINDINGS -DDISABLE_LUA_BINDINGS_RINGS) \ + $(if $(CONFIG_DNSDIST_MAC),-DDNSDIST_RINGS_WITH_MACADDRESS,) \ + $(if $(CONFIG_DNSDIST_OCSP_STAPLING),,-DDISABLE_OCSP_STAPLING) \ + $(if $(CONFIG_DNSDIST_PROTOBUF),,-DDISABLE_PROTOBUF) \ + $(if $(CONFIG_DNSDIST_PROMETHEUS),,-DDISABLE_PROMETHEUS) \ + $(if $(CONFIG_DNSDIST_RULES_ALTER),,-DDISABLE_RULES_ALTERING_QUERIES) \ + $(if $(CONFIG_DNSDIST_SECPOLL),,-DDISABLE_SECPOLL) \ + $(if $(CONFIG_DNSDIST_TOP_N),,-DDISABLE_TOP_N_BINDINGS) \ + $(if $(CONFIG_DNSDIST_WEB),,-DDISABLE_WEB_CONFIG -DDISABLE_BUILTIN_HTML -DDISABLE_LUA_WEB_HANDLERS) + CONFIGURE_ARGS+= \ - --enable-dnscrypt \ - $(if $(CONFIG_DNSDIST_NOSSL),,--enable-dns-over-tls) \ - --enable-fstrm \ - --enable-libsodium \ - --enable-protobuf \ - --enable-re2 \ - --with-lua=lua \ - --with-net-snmp \ - $(if $(CONFIG_DNSDIST_GNUTLS),--enable,--disable)-gnutls \ - $(if $(CONFIG_DNSDIST_OPENSSL),--enable,--disable)-libssl + --with-pic \ + --with-lua=luajit \ + --with-libcap \ + $(if $(CONFIG_DNSDIST_PIE),,--disable-hardening) \ + $(if $(CONFIG_DNSDIST_SODIUM),--enable-dnscrypt --with-libsodium,--disable-dnscrypt --without-libsodium) \ + $(if $(CONFIG_DNSDIST_DNSTAP),--enable-dnstap=yes,--enable-dnstap=no) \ + $(if $(CONFIG_DNSDIST_RE2),--with,--without)-re2 \ + $(if $(CONFIG_DNSDIST_NET_SNMP),--with,--without)-net-snmp \ + $(if $(CONFIG_DNSDIST_GNUTLS),--with,--without)-gnutls \ + $(if $(CONFIG_DNSDIST_OPENSSL),--with,--without)-libssl \ + $(if $(CONFIG_DNSDIST_DNS_OVER_TLS),--enable-dns-over-tls,) \ + $(if $(CONFIG_DNSDIST_CDB),--with,--without)-cdb \ + $(if $(CONFIG_DNSDIST_LMDB),--with,--without)-lmdb \ + $(if $(CONFIG_DNSDIST_LIBEDIT),--with,--without)-libedit \ + $(if $(CONFIG_DNSDIST_IPCIPHER),--enable,--disable)-ipcipher \ + $(if $(CONFIG_DNSDIST_EBPF),--with,--without)-ebpf \ + $(if $(CONFIG_DNSDIST_DNS_OVER_HTTPS),--enable-dns-over-https,) \ + $(if $(CONFIG_DNSDIST_DNS_OVER_HTTPS_OUTGOING),--with,--without)-nghttp2 define Package/dnsdist/install $(INSTALL_DIR) $(1)/etc + $(INSTALL_DIR) $(1)/etc/dnsdist.conf.d $(INSTALL_CONF) ./files/dnsdist.conf $(1)/etc/dnsdist.conf - $(INSTALL_DIR) $(1)/etc/config - $(INSTALL_CONF) ./files/dnsdist.config $(1)/etc/config/dnsdist $(INSTALL_DIR) $(1)/etc/init.d $(INSTALL_BIN) ./files/dnsdist.init $(1)/etc/init.d/dnsdist $(INSTALL_DIR) $(1)/usr/bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/dnsdist $(1)/usr/bin/ + $(INSTALL_DIR) $(1)/usr/share/acl.d + $(INSTALL_DATA) ./files/dnsdist_acl.json $(1)/usr/share/acl.d + $(INSTALL_DIR) $(1)/usr/share/lua/dnsdist + $(INSTALL_DIR) $(1)/usr/share/dnsdist + $(CP) ./files/common.lua $(1)/usr/share/lua/dnsdist/ + $(CP) ./files/configuration.lua $(1)/usr/share/lua/dnsdist/ + $(CP) ./files/local-domains.lua $(1)/usr/share/lua/dnsdist/ + $(CP) ./files/os.lua $(1)/usr/share/lua/dnsdist/ + $(CP) ./files/dnsdist-odhcpd.lua $(1)/usr/share/lua/dnsdist/ + $(CP) ./files/start.lua $(1)/usr/share/lua/dnsdist/ + $(CP) ./files/sample.uci.conf $(1)/usr/share/dnsdist/sample.uci.conf + $(CP) ./files/dnsdist.config $(1)/usr/share/dnsdist/simple.uci.conf + $(INSTALL_BIN) ./files/diag.sh $(1)/usr/share/dnsdist/diag.sh endef +define Package/dnsdist/postinst +#!/bin/sh +# if we are on a "real" system, IPKG_INSTROOT will be empty, +# otherwise we are in the process of building an image and +# thus on the host system +local root="$${IPKG_INSTROOT}" + +if [ ! -e "$${root}/etc/config/dnsdist" ]; then + cp "$${root}/usr/share/dnsdist/simple.uci.conf" "$${root}/etc/config/dnsdist" +fi +endef + + $(eval $(call BuildPackage,dnsdist)) diff --git a/net/dnsdist/files/common.lua b/net/dnsdist/files/common.lua new file mode 100644 index 00000000000000..1ca204066e7308 --- /dev/null +++ b/net/dnsdist/files/common.lua @@ -0,0 +1,54 @@ +local M = {} + +M.poolName = '' + +local maintenanceHookRegistrations = {} +local configurationParsedHookRegistrations = {} +local configurationDoneHookRegistrations = {} + +local string_find = string.find + +function M.addrIsIPv6(addr) + return string_find(addr, ':') +end + +local function insertIntoTableIfNotExists(tab, value) + for _, v in ipairs(tab) do + if v == value then + return + end + end + table.insert(tab, value) +end + +function M.registerMaintenanceHook(callback) + insertIntoTableIfNotExists(maintenanceHookRegistrations, callback) +end + +function M.registerConfigurationParsedHook(callback) + insertIntoTableIfNotExists(configurationParsedHookRegistrations, callback) +end + +function M.registerConfigurationDoneHook(callback) + insertIntoTableIfNotExists(configurationDoneHookRegistrations, callback) +end + +function M.runMaintenanceHooks() + for _, callback in ipairs(maintenanceHookRegistrations) do + callback() + end +end + +function M.runConfigurationParsedHooks(config, cursor) + for _, callback in ipairs(configurationParsedHookRegistrations) do + callback(config, cursor) + end +end + +function M.runConfigurationDoneHooks(config) + for _, callback in ipairs(configurationDoneHookRegistrations) do + callback(config) + end +end + +return M diff --git a/net/dnsdist/files/configuration.lua b/net/dnsdist/files/configuration.lua new file mode 100644 index 00000000000000..48da29f5213d96 --- /dev/null +++ b/net/dnsdist/files/configuration.lua @@ -0,0 +1,796 @@ +local M = {} + +local uci = require 'uci' + +local localDomains = require 'dnsdist/local-domains' +local common = require 'dnsdist/common' +local os = require 'dnsdist/os' + +local configurationCheckInterval = 0 +local configurationCheckInterfacePatterns = {} + +function M.getGeneralUCIOption(cursor, key, default) + local value, err = cursor:get('dnsdist', 'general', key) + if err == nil and value ~= nil then + return value + else + return default + end +end + +function M.parseDNSRecordType(recordType) + local idx = string.find(recordType, 'TYPE') + if idx == 1 then + recordType = string.sub(recordType, 5, -1) + return tonumber(recordType) + else + if DNSQType[recordType] ~= nil then + return DNSQType[recordType] + end + end + return nil +end + +local function getTLSMaterial(config) + local cert = config['tls_cert'] + local key = config['tls_key'] + local skip = false + + if config['tls_cert_is_password_protected'] == '1' and config['tls_cert_password'] ~= nil then + key = '' + if os.fileExists(cert) then + cert = newTLSCertificate(config['tls_cert'], { password = config['tls_cert_password'] }) + else + skip = true + end + else + if not os.fileExists(cert) or not os.fileExists(key) then + skip = true + end + end + return cert, key, skip +end + +local function buildSentinelDomainNames() + local sentinelDomainNames = {} + + local cursor = uci.cursor() + cursor:foreach('dnsdist', 'domainlist', function (entry) + local sentinelDomainName = entry['.name'] + local domainList = {} + for _, v in pairs(entry['entry']) do + local name, itf = string.match(v, '([^%s]+)%s([^%s]+)') + if domainList[itf] == nil then + domainList[itf] = {} + end + table.insert(domainList[itf], name) + end + local itfRules = {} + for itfName, config in pairs(domainList) do + local rules = { suffixes = nil, exact = nil } + for _, name in pairs(config) do + if string.sub(name, 1, 1) == '*' then + if rules['suffixes'] == nil then + rules['suffixes'] = newSuffixMatchNode() + end + rules['suffixes']:add(string.sub(name, 3, -1)) + else + if rules['exact'] == nil then + rules['exact'] = newDNSNameSet() + end + rules['exact']:add(newDNSName(name)) + end + end + if rules['suffixes'] == nil and rules['exact'] ~= nil then + itfRules[itfName] = QNameSetRule(rules['exact']) + elseif rules['suffixes'] ~= nil and rules['exact'] == nil then + itfRules[itfName] = SuffixMatchNodeRule(rules['suffixes']) + elseif rules['suffixes'] ~= nil and rules['exact'] ~= nil then + itfRules[itfName] = OrRule({QNameSetRule(rules['exact']), SuffixMatchNodeRule(rules['suffixes'])}) + end + end + sentinelDomainNames[sentinelDomainName] = itfRules + end) + return sentinelDomainNames +end + +function M.isInterfaceEnabled(includePatterns, excludePatterns, interfaceName) + if #includePatterns > 0 then + local enabled = false + for _, pattern in ipairs(includePatterns) do + if string.match(interfaceName, pattern) ~= nil then + enabled = true + break + end + end + if not enabled then + return false + end + end + + if #excludePatterns > 0 then + for _, pattern in ipairs(excludePatterns) do + if string.match(interfaceName, pattern) ~= nil then + return false + end + end + end + + return true +end + +function M.isBridge() + local cursor = uci.cursor() + local value, err = cursor:get("network", "wan", "type") + if err == nil and value == "bridge" then + return true + else + return false + end +end + +function M.enabled(config) + if config['enabled'] == '0' then + return false + end + return true +end + +function M.apply(config) + if not M.enabled(config) then + return + end + + -- logging + if config['verbose_mode'] == 1 then + setVerbose(true) + if config['verbose_log_destination'] ~= nil and #config['verbose_log_destination'] > 0 then + vinfolog('Directing verbose-level log messages to '..config['verbose_log_destination']) + setVerboseLogDestination(config['verbose_log_destination']) + end + end + + -- cache + if config['domain_cache_size'] ~= nil and tonumber(config['domain_cache_size']) > 0 then + + setCacheCleaningDelay(tonumber(config['domain_cleanup_interval'])) + + local cacheOptions = { maxTTL = tonumber(config['domain_ttl_cap']) } + local pc = newPacketCache(tonumber(config['domain_cache_size']), cacheOptions) + + getPool(common.poolName):setCache(pc) + if config['auto_upgraded_backends_pool'] ~= nil then + getPool(config['auto_upgraded_backends_pool']):setCache(pc) + end + else + -- make sure that the upgraded backend pool is always created, even when the cache is disabled + if config['auto_upgraded_backends_pool'] ~= nil then + getPool(config['auto_upgraded_backends_pool']) + end + end + + -- web server + if config['api-port'] ~= nil then + webserver("127.0.0.1:"..tonumber(config['api-port'])) + setWebserverConfig({apiRequiresAuthentication=false, acl="127.0.0.0/8"}) + end + + -- we need to retain the CAP_NET_RAW capability to be able to use a + -- specific source interface for our backends + local capabilitiesToRetain = {} + local serversCount = 0 + for _, v in pairs(config['servers']) do + serversCount = serversCount + 1 + end + local serverIdx = 0 + for _, v in pairs(config['servers']) do + if serverIdx >= config['max_upstream_resolvers'] then + warnlog('Skipping upstream resolver '..v['address']..' because we already have '..config['max_upstream_resolvers']..' resolvers') + else + serverIdx = serverIdx + 1 + if v['source'] ~= nil then + table.insert(capabilitiesToRetain, 'CAP_NET_RAW') + end + -- if we have only one upstream resolver, mark it UP as we have no other + -- choice anyway + if serversCount == 1 then + infolog("Marking the only downstream server as 'UP'") + v['healthCheckMode'] = 'up' + end + newServer(v) + end + end + + if #capabilitiesToRetain > 0 then + addCapabilitiesToRetain(capabilitiesToRetain) + end + + -- network interfaces + local interfaces = getListOfNetworkInterfaces() + + local localDomainsDests = newNMG() + local allAddresses = {} + + local ACL = { + '127.0.0.0/8', + '::1/128', + } + + local sentinelRules = buildSentinelDomainNames() + + for _, itf in ipairs(interfaces) do + if M.isInterfaceEnabled(config['interfaces-include'], config['interfaces-exclude'], itf) then + local conf = config['interfaces'][itf] + if conf == nil then + conf = config['interfaces']['default_interface'] + end + + if conf ~= nil then + if conf['enabled'] == '1' then + local interfaceDests = newNMG() + local mainAddr = nil + local additionalAddrs = {} + local itfAddrs = {} + local v4Addrs = {} + local v6Addrs = {} + local addresses = getListOfAddressesOfNetworkInterface(itf) + + for _,v in ipairs(getListOfRangesOfNetworkInterface(itf)) do + table.insert(ACL, v) + end + + for _, addr in ipairs(addresses) do + if common.addrIsIPv6(addr) and string.sub(addr, 1, 1) ~= '[' then + addr = '['..addr..']' + if mainAddr == nil then + mainAddr = addr + else + table.insert(additionalAddrs, addr) + end + table.insert(v6Addrs, addr) + else + if mainAddr == nil then + mainAddr = addr + else + table.insert(additionalAddrs, addr) + end + table.insert(v4Addrs, addr) + end + table.insert(itfAddrs, addr) + table.insert(allAddresses, addr) + + if conf['do53'] == '1' then + local parameters = { + maxConcurrentTCPConnections = tonumber(config['concurrent_incoming_connections_per_device']) + } + addLocal(addr..':'..config['do53_port'], parameters) + end + + if conf['local_resolution'] == '1' then + localDomainsDests:addMask(addr) + end + + interfaceDests:addMask(addr) + end + + if conf['dot'] == '1' and mainAddr ~= nil then + -- we set maxConcurrentTCPConnections here but see also + -- setMaxTCPConnectionsPerClient() below for TCP/DoT + local dotParameters = { + ignoreTLSConfigurationErrors = true, + maxConcurrentTCPConnections = tonumber(config['concurrent_incoming_connections_per_device']), + numberOfStoredSessions = 0, + internalPipeBufferSize = 0, + minTLSVersion = config['tls_min_version'], + ciphers = config['tls_ciphers_incoming'], + ciphersTLS13 = config['tls_ciphers13_incoming'] + } + if next(additionalAddrs) ~= nil then + dotParameters['additionalAddresses'] = {} + for _, v in ipairs(additionalAddrs) do + table.insert(dotParameters['additionalAddresses'], v..':'..config['dot_port']) + end + end + + local cert, key, skip = getTLSMaterial(config) + if not skip then + addTLSLocal(mainAddr..':'..config['dot_port'], cert, key, dotParameters) + else + warnlog('Not listening for DoT queries on '..mainAddr..':'..config['dot_port']..' because the certificate or key is missing') + end + end + + if conf['doh'] == '1' and mainAddr ~= nil then + local dohParameters = { + ignoreTLSConfigurationErrors = true, + maxConcurrentTCPConnections = tonumber(config['concurrent_incoming_connections_per_device']), + numberOfStoredSessions = 0, + internalPipeBufferSize = 0, + minTLSVersion = config['tls_min_version'], + ciphers = config['tls_ciphers_incoming'], + ciphersTLS13 = config['tls_ciphers13_incoming'] + } + if next(additionalAddrs) ~= nil then + dohParameters['additionalAddresses'] = {} + for _, v in ipairs(additionalAddrs) do + table.insert(dohParameters['additionalAddresses'], v..':'..config['doh_port']) + end + end + + local cert, key, skip = getTLSMaterial(config) + if not skip then + addDOHLocal(mainAddr..':'..config['doh_port'], cert, key, '/dns-query', dohParameters) + else + warnlog('Not listening for DoH queries on '..mainAddr..':'..config['doh_port']..' because the certificate or key is missing') + end + end + + if interfaceDests:size() > 0 then + local itfNMGRule = NetmaskGroupRule(interfaceDests, false) + -- sentinel domains + if conf['sentinel_domains'] ~= nil and interfaceDests:size() > 0 then + local sentinelItfs = sentinelRules[conf['sentinel_domains']] + for sentinelItf, rule in pairs(sentinelItfs) do + local addresses = getListOfAddressesOfNetworkInterface(sentinelItf) + local addressesList = {} + for _, addr in ipairs(addresses) do + if common.addrIsIPv6(addr) then + if string.find(addr, '%%') == nil then + if string.sub(addr, 1, 1) ~= '[' then + addr = '['..addr..']' + end + table.insert(addressesList, addr) + end + else + table.insert(addressesList, addr) + end + end + if #addressesList > 0 then + addAction(AndRule{itfNMGRule, rule}, SpoofAction(addressesList, { ttl=config['sentinel_domains_ttl'] })) + end + end + end + + -- Advertise DoT /DoH via SVCB + if conf['advertise'] == '1' then + local namedResolver = config['advertise_for_domain_name'] + local targetName = '_dns.resolver.arpa.' + if namedResolver ~= nil and #namedResolver > 0 then + targetName = namedResolver + namedResolver = '_dns.'..namedResolver + end + local svc = {} + if conf['dot'] == '1' then + table.insert(svc, newSVCRecordParameters(1, targetName, { mandatory={"port"}, alpn={ "dot" }, noDefaultAlpn=true, port=config['dot_port'], ipv4hint=v4Addrs, ipv6hint=v6Addrs })) + end + if conf['doh'] == '1' then + table.insert(svc, newSVCRecordParameters(2, targetName, { mandatory={"port"}, alpn={ "h2" }, port=config['doh_port'], ipv4hint=v4Addrs, ipv6hint=v6Addrs, key7='/dns-query' })) + end + if #svc > 0 then + local nameRule = nil + if namedResolver ~= nil and #namedResolver > 0 then + nameRule = OrRule{QNameRule('_dns.resolver.arpa.'), QNameRule(namedResolver)} + else + nameRule = QNameRule('_dns.resolver.arpa.') + end + addAction(AndRule{QTypeRule(DNSQType.SVCB), itfNMGRule, nameRule}, SpoofSVCAction(svc)) + if config['advertise_for_domain_name'] ~= nil and #config['advertise_for_domain_name'] > 0 then + -- basically an automatic sentinel domain rule + addAction(AndRule{itfNMGRule, QNameRule(config['advertise_for_domain_name'])}, SpoofAction(itfAddrs, { ttl=config['sentinel_domains_ttl'] })) + end + -- reply with NODATA (NXDOMAIN would deny all types at that name and below, including SVC) for other types + -- but only for _dns.resolver.arpa., the advertised name might have other types + addAction(AndRule{itfNMGRule, QNameRule('_dns.resolver.arpa.')}, NegativeAndSOAAction(false, '_dns.resolver.arpa.', 3600, 'fake.resolver.arpa.', 'fake.resolver.arpa.', 1, 1800, 900, 604800, 86400)) + end + end + end + end + end + end + end + + -- allow queries from all subnets on chosen interfaces + setACL(ACL) + + -- tuning + if config['concurrent_incoming_connections_per_device'] ~= nil then + setMaxTCPConnectionsPerClient(tonumber(config['concurrent_incoming_connections_per_device'])) + end + if config['max_idle_doh_connections_per_downstream'] ~= nil then + setMaxIdleDoHConnectionsPerDownstream(tonumber(config['max_idle_doh_connections_per_downstream'])) + end + if config['max_idle_tcp_connections_per_downstream'] ~= nil then + setMaxCachedTCPConnectionsPerDownstream(tonumber(config['max_idle_tcp_connections_per_downstream'])) + end + + -- local domains interfaces + localDomains.destinations = localDomainsDests + for suffix in string.gmatch(config['local_domains_suffix'], '([^%s]+)') do + table.insert(localDomains.lanSuffixes, suffix) + end + localDomains.ttl = tonumber(config['local_domains_ttl']) + + -- watch for configuration changes + configurationCheckInterval = config['configuration-check-interval'] + + if configurationCheckInterval > 0 then + configurationCheckInterfacePatterns['lan-interfaces-include'] = config['interfaces-include'] + configurationCheckInterfacePatterns['lan-interfaces-exclude'] = config['interfaces-exclude'] + configurationCheckInterfacePatterns['wan-interfaces-include'] = config['wan-interfaces-include'] + end + + -- route queries to a DoT / DoH backend, if available, and to the default pool otherwise + if config['auto_upgraded_backends_pool'] ~= nil then + addAction(PoolAvailableRule(config['auto_upgraded_backends_pool']), ContinueAction(PoolAction(config['auto_upgraded_backends_pool']))) + end + + common.registerMaintenanceHook(M.maintenance) +end + +function M.loadFromUCI() + local config = {} + local cursor = uci.cursor() + + -- Load these values even if dnsdist is not enabled, + -- we need them to know how often to check if that changed + config['configuration-check-interval'] = tonumber(M.getGeneralUCIOption(cursor, 'configuration_check_interval', 60)) + + config['enabled'] = M.getGeneralUCIOption(cursor, 'enabled', '0') + if config['enabled'] == '0' then + return config + end + + config['do53_port'] = M.getGeneralUCIOption(cursor, 'do53_port', 53) + config['dot_port'] = M.getGeneralUCIOption(cursor, 'dot_port', 853) + config['doh_port'] = M.getGeneralUCIOption(cursor, 'doh_port', 443) + config['tls_cert'] = M.getGeneralUCIOption(cursor, 'tls_cert', '') + config['tls_key'] = M.getGeneralUCIOption(cursor, 'tls_key', '') + config['tls_cert_is_password_protected'] = M.getGeneralUCIOption(cursor, 'tls_cert_is_password_protected', '0') + config['tls_ciphers_incoming'] = M.getGeneralUCIOption(cursor, 'tls_ciphers_incoming', '') + config['tls_ciphers13_incoming'] = M.getGeneralUCIOption(cursor, 'tls_ciphers13_incoming', '') + config['tls_ciphers_outgoing'] = M.getGeneralUCIOption(cursor, 'tls_ciphers_outgoing', '') + config['tls_ciphers13_outgoing'] = M.getGeneralUCIOption(cursor, 'tls_ciphers13_outgoing', '') + config['concurrent_incoming_connections_per_device'] = tonumber(M.getGeneralUCIOption(cursor, 'concurrent_incoming_connections_per_device', '10')) + config['default_check_interval'] = tonumber(M.getGeneralUCIOption(cursor, 'default_check_interval', '5')) + config['default_check_timeout'] = tonumber(M.getGeneralUCIOption(cursor, 'default_check_timeout', '1000')) + config['default_max_check_failures'] = tonumber(M.getGeneralUCIOption(cursor, 'default_max_check_failures', '2')) + config['default_max_upstream_concurrent_tcp_connections'] = tonumber(M.getGeneralUCIOption(cursor, 'default_max_upstream_concurrent_tcp_connections', '0')) + config['max_idle_tcp_connections_per_downstream'] = tonumber(M.getGeneralUCIOption(cursor, 'max_idle_tcp_connections_per_downstream', '2')) + config['max_idle_doh_connections_per_downstream'] = tonumber(M.getGeneralUCIOption(cursor, 'max_idle_doh_connections_per_downstream', '2')) + config['outgoing_udp_sockets_per_downstream'] = tonumber(M.getGeneralUCIOption(cursor, 'outgoing_udp_sockets_per_downstream', '100')) + config['max_upstream_resolvers'] = tonumber(M.getGeneralUCIOption(cursor, 'max_upstream_resolvers', '5')) + + -- lazy health-checks + config['health_checks_sample_size'] = tonumber(M.getGeneralUCIOption(cursor, 'health_checks_sample_size', 100)) + config['health_checks_min_sample_count'] = tonumber(M.getGeneralUCIOption(cursor, 'health_checks_min_sample_count', 10)) + config['health_checks_threshold'] = tonumber(M.getGeneralUCIOption(cursor, 'health_checks_threshold', 20)) + config['health_checks_failed_interval']= tonumber(M.getGeneralUCIOption(cursor, 'health_checks_failed_interval', 30)) + config['health_checks_mode']= M.getGeneralUCIOption(cursor, 'health_checks_mode', 'TimeoutOrServFail') + config['health_checks_exponential_backoff']= tonumber(M.getGeneralUCIOption(cursor, 'health_checks_exponential_backoff', 1)) + config['health_checks_max_backoff']= tonumber(M.getGeneralUCIOption(cursor, 'health_checks_max_backoff', 3600)) + + -- logging + config['verbose_mode'] = tonumber(M.getGeneralUCIOption(cursor, 'verbose_mode', 0)) + config['verbose_log_destination'] = M.getGeneralUCIOption(cursor, 'verbose_log_destination', '') + + -- enable verbose logging very early if necessary + -- we'll apply verbose_log_destination after startup + if config['verbose_mode'] == 1 then + setVerbose(true) + end + + -- cache + config['domain_cache_size'] = M.getGeneralUCIOption(cursor, 'domain_cache_size', '100') + config['domain_ttl_cap'] = M.getGeneralUCIOption(cursor, 'domain_ttl_cap', '600') + config['domain_cleanup_interval'] = M.getGeneralUCIOption(cursor, 'domain_cleanup_interval', '60') + + -- DoT / DoH advertisement + config['advertise_for_domain_name'] = M.getGeneralUCIOption(cursor, 'advertise_for_domain_name', '') + + -- auto upgrade of discovered servers + config['auto_upgrade_discovered_backends'] = M.getGeneralUCIOption(cursor, 'auto_upgrade_discovered_backends', '0') + config['keep_auto_upgraded_backends'] = M.getGeneralUCIOption(cursor, 'keep_auto_upgraded_backends', '1') + config['auto_upgraded_backends_pool'] = M.getGeneralUCIOption(cursor, 'auto_upgraded_backends_pool', 'upgraded-to-dox') + + config['local_domains_suffix'] = M.getGeneralUCIOption(cursor, 'local_domains_suffix', 'lan') + config['local_domains_ttl'] = tonumber(M.getGeneralUCIOption(cursor, 'local_domains_ttl', '1')) + config['sentinel_domains_ttl'] = tonumber(M.getGeneralUCIOption(cursor, 'sentinel_domains_ttl', '60')) + + -- web server + config['api-port'] = M.getGeneralUCIOption(cursor, 'web_server_port', '9080') + + config['servers'] = {} + cursor:foreach('dnsdist', 'server', function (entry) + local server = { + healthCheckMode = 'lazy', + lazyHealthCheckSampleSize = config['health_checks_sample_size'], + lazyHealthCheckMinSampleCount = config['health_checks_min_sample_count'], + lazyHealthCheckThreshold = config['health_checks_threshold'], + lazyHealthCheckFailedInterval = config['health_checks_failed_interval'], + lazyHealthCheckMode = config['health_checks_mode'], + lazyHealthCheckUseExponentialBackOff = (config['health_checks_exponential_backoff'] == 1), + lazyHealthCheckMaxBackOff = config['health_checks_max_backoff'], + lazyHealthCheckWhenUpgraded = true + } + local invalid = false + server['name'] = entry['.name'] + if entry['adn'] ~= nil then + server['subjectAltName'] = entry['adn'] + end + local type = entry['type'] + if type == 'doh' or type == 'dot' then + server['tls'] = 'openssl' + end + if entry['port'] ~= nil then + server['address'] = entry['addr']..':'..entry['port'] + else + server['address'] = entry['addr']..':53' + end + if entry['upstreamWANInterface'] ~= nil and #entry['upstreamWANInterface'] > 0 then + local itfAddrs = getListOfAddressesOfNetworkInterface(entry['upstreamWANInterface']) + if #itfAddrs > 0 then + infolog('Setting WAN interface affinity for upstream resolver '..server['name']..' to '..entry['upstreamWANInterface']) + server['source'] = entry['upstreamWANInterface'] + else + errlog('Discarding upstream resolver '..server['name']..' because the requested interface affinity ('..entry['upstreamWANInterface']..') cannot be satisfied') + invalid = true + end + end + if entry['path'] then + server['dohPath'] = entry['path'] + end + if entry['validate'] == '0' then + server['validateCertificates'] = false + end + if entry['maxInFlight'] ~= nil then + server['maxInFlight'] = entry['maxInFlight'] + end + if entry['maxConcurrentTCPConnections'] ~= nil then + server['maxConcurrentTCPConnections'] = entry['maxConcurrentTCPConnections'] + else + server['maxConcurrentTCPConnections'] = config['default_max_upstream_concurrent_tcp_connections'] + end + if entry['maxCheckFailures'] ~= nil then + server['maxCheckFailures'] = entry['maxCheckFailures'] + else + server['maxCheckFailures'] = config['default_max_check_failures'] + end + if entry['checkInterval'] ~= nil then + server['checkInterval'] = entry['checkInterval'] + else + server['checkInterval'] = config['default_check_interval'] + end + if entry['checkTimeout'] ~= nil then + server['checkTimeout'] = entry['checkTimeout'] + else + server['checkTimeout'] = config['default_check_timeout'] + end + if config['tls_ciphers_outgoing'] ~= nil and config['tls_ciphers_outgoing'] then + server['ciphers'] = config['tls_ciphers_outgoing'] + end + if config['tls_ciphers13_outgoing'] ~= nil and config['tls_ciphers13_outgoing'] then + server['ciphers13'] = config['tls_ciphers13_outgoing'] + end + if config['outgoing_udp_sockets_per_downstream'] ~= nil then + server['sockets'] = tonumber(config['outgoing_udp_sockets_per_downstream']) + end + if entry['autoUpgrade'] ~= nil then + if entry['autoUpgrade'] == true or entry['autoUpgrade'] == '1' then + entry['autoUpgrade'] = true + elseif entry['autoUpgrade'] == '0' then + entry['autoUpgrade'] = false + end + server['autoUpgrade'] = entry['autoUpgrade'] + if entry['autoUpgradeInterval'] ~= nil then + server['autoUpgradeInterval'] = tonumber(entry['autoUpgradeInterval']) + end + if entry['autoUpgradeKeep'] ~= nil then + server['autoUpgradeKeep'] = entry['autoUpgradeKeep'] + end + if entry['autoUpgradePool'] ~= nil then + server['autoUpgradePool'] = entry['autoUpgradePool'] + end + if entry['autoUpgradeDoHKey'] ~= nil then + server['autoUpgradeDoHKey'] = tonumber(entry['autoUpgradeDoHKey']) + end + end + + if not invalid then + config['servers'][server['name']] = server + end + end) + + if next(config['servers']) == nil then + -- no servers found in the configuration, let's see if we learned something from DHCP + local resolvConfFile = '/tmp/resolv.conf.d/resolv.conf.auto' + if not os.fileExists(resolvConfFile) then + resolvConfFile = '/tmp/resolv.conf.auto' + end + + if os.fileExists(resolvConfFile) then + vinfolog("Reading upstream resolvers from "..resolvConfFile..":") + vinfolog(string.format('[[%q]]', io.open(resolvConfFile):read('a*'))) + else + vinfolog("Not reading upstream resolvers from "..resolvConfFile.." as the file does not exist") + end + + local resolvers = getResolvers(resolvConfFile) + local numresolvers = 0 + for _, resolver in ipairs(resolvers) do + local server = { + healthCheckMode = 'lazy', + lazyHealthCheckSampleSize = config['health_checks_sample_size'], + lazyHealthCheckMinSampleCount = config['health_checks_min_sample_count'], + lazyHealthCheckThreshold = config['health_checks_threshold'], + lazyHealthCheckFailedInterval = config['health_checks_failed_interval'], + lazyHealthCheckMode = config['health_checks_mode'], + lazyHealthCheckUseExponentialBackOff = (config['health_checks_exponential_backoff'] == 1), + lazyHealthCheckMaxBackOff = config['health_checks_max_backoff'], + lazyHealthCheckWhenUpgraded = true + } + server['address'] = resolver + if config['auto_upgrade_discovered_backends'] == '1' then + server['autoUpgrade'] = true + if config['keep_auto_upgraded_backends'] == '1' then + server['autoUpgradeKeep'] = true + end + if config['auto_upgraded_backends_pool'] ~= nil then + server['autoUpgradePool'] = config['auto_upgraded_backends_pool'] + end + -- these settings will be inherited in case of DoT/DoH upgrade + if config['tls_ciphers_outgoing'] ~= nil and config['tls_ciphers_outgoing'] then + server['ciphers'] = config['tls_ciphers_outgoing'] + end + if config['tls_ciphers13_outgoing'] ~= nil and config['tls_ciphers13_outgoing'] then + server['ciphers13'] = config['tls_ciphers13_outgoing'] + end + end + if config['default_max_upstream_concurrent_tcp_connections'] ~= nil then + server['maxConcurrentTCPConnections'] = config['default_max_upstream_concurrent_tcp_connections'] + end + if config['default_max_check_failures'] ~= nil then + server['maxCheckFailures'] = config['default_max_check_failures'] + end + if config['default_check_interval'] ~= nil then + server['checkInterval'] = config['default_check_interval'] + end + if config['default_check_timeout'] ~= nil then + server['checkTimeout'] = config['default_check_timeout'] + end + if config['outgoing_udp_sockets_per_downstream'] ~= nil then + server['sockets'] = tonumber(config['outgoing_udp_sockets_per_downstream']) + end + + config['servers'][resolver] = server + numresolvers = numresolvers + 1 + end + + infolog(string.format("Read %s, learned %s upstream resolvers", resolvConfFile, numresolvers)) + end + + config['interfaces'] = {} + cursor:foreach('dnsdist', 'interface', function (itf) + local conf = {} + local name = itf['name'] + if name ~= nil then + conf['enabled'] = itf['enabled'] + conf['do53'] = itf['do53'] + conf['dot'] = itf['dot'] + conf['doh'] = itf['doh'] + conf['advertise'] = itf['advertise'] + conf['policy_engine'] = itf['policy_engine'] + conf['local_resolution'] = itf['local_resolution'] + conf['sentinel_domains'] = itf['sentinel_domains'] + config['interfaces'][name] = conf + end + end) + + config['interfaces-include'] = {} + config['interfaces-exclude'] = {} + for pattern in string.gmatch(M.getGeneralUCIOption(cursor, 'network_interface_include', ''), '([^%s]+)') do + table.insert(config['interfaces-include'], pattern) + end + for pattern in string.gmatch(M.getGeneralUCIOption(cursor, 'network_interface_exclude', ''), '([^%s]+)') do + table.insert(config['interfaces-exclude'], pattern) + end + config['wan-interfaces-include'] = {} + for pattern in string.gmatch(M.getGeneralUCIOption(cursor, 'network_interface_wan_include', ''), '([^%s]+)') do + table.insert(config['wan-interfaces-include'], pattern) + end + + collectgarbage() + common.runConfigurationParsedHooks(config, cursor) + collectgarbage() + + return config +end + +local nextConfigurationCheck = 0 +local lastConfigurationModificationTime = 0 +local lastNetworkConfiguration = nil + +local function isInterfaceWatched(interfaceName) + if #configurationCheckInterfacePatterns['wan-interfaces-include'] > 0 then + for _, pattern in ipairs(configurationCheckInterfacePatterns['wan-interfaces-include']) do + if string.match(interfaceName, pattern) ~= nil then + return true + end + end + end + + return M.isInterfaceEnabled(configurationCheckInterfacePatterns['lan-interfaces-include'], configurationCheckInterfacePatterns['lan-interfaces-exclude'], interfaceName) +end + +local function configurationChanged() + local mtime = os.getFileModificationTime('/etc/config/dnsdist') + if mtime ~= 0 then + if lastConfigurationModificationTime ~= 0 and lastConfigurationModificationTime ~= mtime then + return true + end + lastConfigurationModificationTime = mtime + end + + local networkConfiguration = {} + local interfaces = getListOfNetworkInterfaces() + for _, itf in ipairs(interfaces) do + if isInterfaceWatched(itf) then + local addresses = getListOfAddressesOfNetworkInterface(itf) + networkConfiguration[itf] = addresses + end + end + + if lastNetworkConfiguration ~= nil then + for itf, addresses in pairs(lastNetworkConfiguration) do + local newAddresses = networkConfiguration[itf] + -- if the interface had no addresses, we do not care + if #addresses > 0 then + if newAddresses == nil or #newAddresses ~= #addresses then + infolog('Addresses changed on interface '..itf) + return true + end + local found = false + -- we have the same number of addresses, so if all the existing ones + -- are still there we should be fine + for _, addr in ipairs(addresses) do + for _, newAddr in ipairs(newAddresses) do + if addr == newAddr then + found = true + end + end + if not found then + infolog('Addresses changed on interface '..itf) + return true + end + end + elseif newAddresses ~= nil and #newAddresses > 0 then + infolog('Addresses changed on interface '..itf) + return true + end + end + -- now make sure that we do not have a new interface with + -- addresses + for itf, addresses in pairs(networkConfiguration) do + local oldAddresses = lastNetworkConfiguration[itf] + -- if the interface has no addresses, we do not care + if oldAddresses == nil and #addresses > 0 then + infolog('New interface with addresses: '..itf) + return true + end + end + end + + lastNetworkConfiguration = networkConfiguration + return false +end + +function M.maintenance() + if configurationCheckInterval > 0 and os.time() >= nextConfigurationCheck then + if configurationChanged() then + warnlog("Configuration has been modified, exiting") + os.exit(1) + end + nextConfigurationCheck = os.time() + configurationCheckInterval + end +end + +return M diff --git a/net/dnsdist/files/diag.sh b/net/dnsdist/files/diag.sh new file mode 100755 index 00000000000000..e886014cc7be24 --- /dev/null +++ b/net/dnsdist/files/diag.sh @@ -0,0 +1,38 @@ +#!/bin/sh +DIR=$(mktemp -d) + +[ -d $DIR ] || exit 1 + +cd $DIR + +logread > $DIR/logread.txt +wget -q -O dnsdist.statistics.txt http://127.0.0.1:9080/api/v1/servers/localhost +PIDS=$(pidof dnsdist) + +echo "dnsdist pids: $PIDS" > dnsdist.pids.txt +cp /etc/dnsdist.conf dnsdist.conf.txt +cp /etc/config/dnsdist dnsdist.uci.txt + +ps > ps.txt + +dmesg > dmesg.txt + +netstat -rn > netstat-rn.txt + +netstat -pan > netstat-pan.txt + +for pid in $(pidof dnsdist) +do + ls -al /proc/$pid/fd > dnsdist.pid.$pid.fd.txt + for f in limits maps smaps smaps_rollup stat statm status + do + [ -e /proc/$pid/$f ] && cat /proc/$pid/$f > dnsdist.pid.$pid.$f.txt + done +done + +TARF=$DIR/dnsdist.diagnostics.$(date +%s).tar + +tar -cf $TARF *.txt +rm -f *.txt + +echo Diagnostics stored as $TARF diff --git a/net/dnsdist/files/dnsdist-odhcpd.lua b/net/dnsdist/files/dnsdist-odhcpd.lua new file mode 100644 index 00000000000000..ff2766763a5ddf --- /dev/null +++ b/net/dnsdist/files/dnsdist-odhcpd.lua @@ -0,0 +1,190 @@ +if not submitToMainThread then + -- stub for testing outside of dnsdist + function submitToMainThread(cmd, data) + print("cmd",cmd) + for k,v in pairs(data) do + print(k,v) + end + end +end + +-- # ubus call dhcp ipv4leases +-- { +-- "device": { +-- "br-lan": { +-- "leases": [ +-- { +-- "mac": "dca632cd93a6", +-- "hostname": "archimedes", +-- "accept-reconf-nonce": false, +-- "reqopts": "1,2,6,12,15,26,28,121,3,33,40,41,42,119,249,252,17", +-- "flags": [ +-- "bound" +-- ], +-- "address": "192.168.52.124", +-- "valid": 43160 +-- } +-- ] +-- } +-- } +-- } + +-- # ubus call dhcp ipv6leases +-- { +-- "device": { +-- "br-lan": { +-- "leases": [ +-- { +-- "duid": "00030001dca632c0d5f0", +-- "iaid": 0, +-- "hostname": "", +-- "accept-reconf": false, +-- "assigned": 2589, +-- "flags": [ +-- "bound" +-- ], +-- "ipv6-addr": [ +-- { +-- "address": "fdc0:b385:de66::a1d", +-- "preferred-lifetime": -1, +-- "valid-lifetime": -1 +-- } +-- ], +-- "valid": 56 +-- } +-- ] +-- } +-- } +-- } + + +local ubus = require 'ubus' -- opkg install libubus-lua +local uloop = require 'uloop' -- opkg install libubox-lua + +uloop.init() + +local conn = ubus.connect() +if not conn then + error("Failed to connect to ubusd") +end + +local byName4 = {} -- key: hostname. value: {ip=.., expire=..} - expire is a timestamp, not a TTL +local byIP4 = {} -- key: IP. value: hostname as string. + +local byName6 = {} -- key: hostname. value: {ip=.., expire=..} - expire is a timestamp, not a TTL +local byIP6 = {} -- key: IP. value: hostname as string. + +local leasetime = 12*3600 -- TODO: fetch from uci + +local now = os.time() + +local function send(cmd, name, ip, proto) + submitToMainThread(cmd, {ip=ip, name=name, proto=proto}) +end + +local function delEntry(name, ip, proto, byName, byIP) + byName[name] = nil + byIP[ip] = nil + send('del', name, ip, proto) +end + +local function setEntry(name, ip, ttl, proto, byName, byIP) + now = os.time() + if byName[name] and byName[name].ip == ip then + -- just update the expiry + byName[name].expire = now + ttl + return + end + + if byName[name] then + -- the name exists, but with the wrong IP + delEntry(name, byName[name].ip, proto, byName, byIP) + end + + if byIP[ip] then + -- the IP exists, but with the wrong name + delEntry(byIP[ip], ip, proto, byName, byIP) + end + + -- we are ready to register the entry + + byName[name] = { ip=ip, expire=now+ttl} + byIP[ip] = name + send('add', name, ip, proto) +end + +local function handleEvent(msg, name) + -- { "dhcp.ack": {"mac":"dc:a6:32:cd:93:a6","ip":"192.168.52.124","name":"archimedes","interface":"br-lan"} } + -- odhcpd only sends v4 events + if name == 'dhcp.ack' and #msg.name > 0 then + setEntry(msg.name, msg.ip, leasetime, 'v4', byName4, byIP4) + end +end + +local function _getLeases(cmd, proto) + local status = conn:call("dhcp", cmd, {}) + + for _,intfv in pairs(status.device) do + for _,lease in ipairs(intfv.leases) do + if #lease.hostname > 0 + then + if proto == 'v4' then + setEntry(lease.hostname, lease.address, lease.valid, proto, byName4, byIP4) + elseif proto == 'v6' and lease['ipv6-addr'] ~= nil then + setEntry(lease.hostname, lease['ipv6-addr'][1].address, lease.valid, proto, byName6, byIP6) + end + end + end + end +end + +local function getLeases() + _getLeases("ipv4leases", 'v4') + _getLeases("ipv6leases", 'v6') +end + +local function _expireLeases(proto, byName, byIP) + now = os.time() + + for k,v in pairs(byName) do + if v.expire < now then + delEntry(k, v.ip, proto, byName, byIP) + end + end +end + +local function expireLeases() + _expireLeases('v4', byName4, byIP4) + _expireLeases('v6', byName6, byIP6) +end + +local interval = 60*1000 -- milliseconds + +local timer +local function maintenance() + -- print("maint") + getLeases() + expireLeases() + timer:set(interval) + collectgarbage() + collectgarbage() +end +timer = uloop.timer(maintenance) +timer:set(interval) + +local sub = { + notify = function(msg,name) + handleEvent(msg, name) + end, +} + +conn:subscribe("dhcp", sub) + +getLeases() + +uloop.run() + +-- Close connection +conn:close() +collectgarbage() +collectgarbage() diff --git a/net/dnsdist/files/dnsdist.conf b/net/dnsdist/files/dnsdist.conf index e69de29bb2d1d6..dd43415a0cfa43 100644 --- a/net/dnsdist/files/dnsdist.conf +++ b/net/dnsdist/files/dnsdist.conf @@ -0,0 +1 @@ +dofile('/usr/share/lua/dnsdist/start.lua') diff --git a/net/dnsdist/files/dnsdist.config b/net/dnsdist/files/dnsdist.config index bc9c1d3a3333f9..25ed9f6069ac1a 100644 --- a/net/dnsdist/files/dnsdist.config +++ b/net/dnsdist/files/dnsdist.config @@ -1,2 +1,148 @@ -config 'dnsdist' 'general' - option enabled '0' +config dnsdist general + option 'enabled' '0' + # Number of entries in the domain cache + option 'domain_cache_size' '100' + # Cap the TTL of cached records to the supplied number of seconds. Default is 600 (10 minutes), minimum is 1, useful range is up to 86400. + # Setting this value to the highest possible value for a TTL, 2^32-1, means that TTLs will not be capped. + option 'domain_ttl_cap' '600' + # Set how often the cache is scanned to expunge expired entries, in seconds. Default is 60s (every minute), minimum value is 0, useful range is up to 86400 + option 'domain_cleanup_interval' '60' + # The TTL used for local domain responses. The values allowed by the DNS protocol range from 1 to 2^31-1 + option 'local_domains_ttl' '1' + # The DNS suffix, or list of suffixes, identifying local domain names + option 'local_domains_suffix' 'lan' + # The TTL used for sentinel domains responses + option 'sentinel_domains_ttl' '60' + # Number of concurrent connections per client device. 0 means unlimited, the maximum value is 2^64-1. + option 'concurrent_incoming_connections_per_device' '10' + # The DNS engine will consider to accept queries on interfaces whose names match this pattern, + # or list of patterns. When a list of patterns is provided, interfaces matching any pattern will + # be considered. If this value is left empty, the DNS engine will consider all interfaces. + # The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, + # not the ones from the netifd configuration, also called 'logical' interface names. + # The default is the 'br' pattern as OpenWrt defaults to one bridge for local interfaces. + option 'network_interface_include' 'br' + # The DNS engine will NOT consider to accept queries on interfaces whose names match this pattern + # or list of patterns. When a list of patterns is provided, interfaces matching any pattern will + # not be considered. If this value is left empty, no interface will be excluded. + # The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, + # not the ones from the netifd configuration, also called 'logical' interface names. + # The default is to exclude interfaces matching the 'wan' and 'eth0.2' patterns, + # as by default on most routers the wan interface is the second VLAN on the single network interface (eth0), + # but it is sometimes named 'wan' on container hosts. + option 'network_interface_exclude' 'wan eth0.2' + # Pattern or list of patterns identifying WAN interfaces. If 'configuration_check_interval' is + # set, only changes to the interfaces matching these patterns, plus the ones matching + # 'network_interface_include' but not 'network_interface_exclude', will be considered + # configuration changes. When a list of patterns is provided, matching any pattern is + # enough to be considered. If this value is left empty, only the interfaces matching + # 'network_interface_include' but not 'network_interface_exclude' will be considered. + # The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, + # not the ones from the netifd configuration, also called 'logical' interface names. + # Refer to 'network_interface_exclude' for the default value. + option 'network_interface_wan_include' 'wan' + # Port on which to accept Do53 queries on (UDP and TCP) + option 'do53_port' '53' + # Port on which to accept DoT queries on + option 'dot_port' '853' + # Port on which to accept DoH queries on + option 'doh_port' '443' + # Interval between health-check queries to an upstream resolver, in seconds + option 'default_check_interval' '5' + # Health check timeout to an upstream resolver, in milliseconds + option 'default_check_timeout' '1000' + # Number of failed consecutive health-checks to mark an upstream resolver down + option 'default_max_check_failures' '2' + # Whether upstream resolvers learned via the system should be checked for DoT/DoH support + option 'auto_upgrade_discovered_backends' '1' + # Whether the Do53 version of auto-upgraded resolver should be kept as fallback + option 'keep_auto_upgraded_backends' '1' + # In which internal pool should upgraded resolver be placed. The pool will be created + # if it does not exist. If this value is left empty upgraded resolvers are placed into + # the 'upgraded-to-dox' pool + option 'auto_upgraded_backends_pool' 'upgraded-to-dox' + # How often should the configuration be checked for changes, in seconds. 0 means disabled + option 'configuration_check_interval' '60' + # Domain name for DoT / DoH advertisement, if any. The default is to advertise only for the opportunistic '_dns.resolver.arpa.' name. + option 'advertise_for_domain_name' '' + # User to switch to, after the configuration has been set up. Default is to run as user 'root' + option 'user' 'dnsdist' + # Group to switch to, after the configuration has been set up. Default is to run as group 'root' + option 'group' 'dnsdist' + # Whether to enable 'verbose' logging ('1') or not ('0') + option 'verbose_mode' '0' + # Log verbose-level messages to this file instead of syslog and stdout (the default) + option 'verbose_log_destination' '' + # Maximum number of TCP connections to an upstream resolver. 0 means no limit + option 'default_max_upstream_concurrent_tcp_connections' '0' + # Maximum number of cached idle TCP connections to an upstream resolver. Keeping more connections improves latency but uses memory and increases the load on the upstream resolver + option 'max_idle_tcp_connections_per_downstream' '2' + # Maximum number of cached idle DoH connections to an upstream resolver. Keeping more connections improves latency but uses memory and increases the load on the upstream resolver + option 'max_idle_doh_connections_per_downstream' '2' + # Number of outgoing UDP sockets to an upstream resolver. Higher values reduce the risk of a successful UDP spoofing attempt, but increase the memory usage. The minimum value is 1, and a sane maximum value is 32767, as higher values might require too many sockets. The default is 100 + option 'outgoing_udp_sockets_per_downstream' '100' + # Maximum number of resolvers that can be used at the same time, either from the configuration + # or learned via the system. Additional resolvers, if any, will be skipped. + # Minimum value is 0, maximum is 100, default is 5. + option 'max_upstream_resolvers' '5' + # The maximum size of the sample of queries to record and consider for the + # health-checking mechanism. Higher values use slightly more memory. + # Minimum is 1, maximum is 10000, default is 100. + option 'health_checks_sample_size' '100' + # The minimum amount of regular queries that should have been recorded before the + # health-check threshold can be applied. Minimum is 1, maximum is equal to 'health_checks_sample_size', + # default is 10. + option 'health_checks_min_sample_count' '10' + # The threshold, as a percentage, of queries that should fail for the health-check + # to be triggered. The default is 20 which means 20% of the last 'health_checks_sample_size' queries + # should fail for a health-check to be triggered. Minimum is 1, maximum is 100. + option 'health_checks_threshold' '20' + # The interval, in seconds, between health-check queries. Note that when 'health_checks_exponential_backoff' is set to 1, + # the interval doubles between every queries. These queries are only sent when a threshold of failing regular queries has + # been reached, and until the backend is available again. Minimum is 1, maximum is 86400, default is 30 seconds. + option 'health_checks_failed_interval' '30' + # This setting controls which responses are considered a failure: 'TimeoutOnly' means that only timeout and I/O errors of + # regular queries will be considered for the health-check threshold, while 'TimeoutOrServFail' will also consider 'Server Failure' + # answers. Default is 'TimeoutOrServFail'. + option 'health_checks_mode' 'TimeoutOrServFail' + # Whether the health-check should use an exponential back-off instead of a fixed value, between health-check probes. If set to '0', + # after a backend has been moved to the 'down' state health-check probes are sent every 'health_checks_failed_interval' seconds. + # When set to '1', the delay between each probe starts at 'health_checks_failed_interval' seconds and doubles between every probe, + # capped at 'health_checks_max_backoff' seconds. Default is '1'. + option 'health_checks_exponential_backoff' '1' + # This value, in seconds, caps the time between two health-check queries when 'health_checks_exponential_backoff' is set to '1'. + # The default is 3600 which means that at most one hour will pass between two health-check queries. Minimum is 1, maximum is 86400. + option 'health_checks_max_backoff' '3600' + +# sentinel domains +config domainlist sentinel_domains + # Exact matching of the name will be done unless it starts with '*.' in which case suffix matching will be done + # IPv4 addresses configured on the configured interface will be returned to A queries, IPv6 to AAAA queries. + # The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, + # not the ones from the netifd configuration, also called 'logical' interface names. + list entry 'my.router br-lan' + list entry '*.dohresolver br-lan' + list entry '*.dotresolver br-lan' + +# configuration for a given network interfaces. +# The configuration of the special name 'default_interface' will be applied +# to all interfaces that do not have a specific configuration entry, provided +# that they match 'network_interface_include' and do not match 'network_interface_exclude'. +# The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, +# not the ones from the netifd configuration, also called 'logical' interface names. +config interface + option name 'default_interface' + # Whether dnsdist will listen on this interface + option enabled 1 + # Whether dnsdist will accept Do53 queries, UDP and TCP, on this interface + option do53 1 + # Whether dnsdist will accept DoT queries on this interface + option dot 0 + # Whether dnsdist will accept DoH queries on this interface + option doh 0 + # Whether local domain resolution will be enabled for queries received on this interface + option local_resolution 1 + # Whether sentinel domain resolution will be enabled for queries received on this interface + option sentinel_domains 'sentinel_domains' + # Whether dnsdist will advertise DoT and/or DoH support, if available, in response to DDR queries received on this interface + option advertise 1 diff --git a/net/dnsdist/files/dnsdist.init b/net/dnsdist/files/dnsdist.init index c17d48f8de3830..0b4d5560c6b693 100644 --- a/net/dnsdist/files/dnsdist.init +++ b/net/dnsdist/files/dnsdist.init @@ -6,15 +6,30 @@ USE_PROCD=1 start_service() { config_load dnsdist local cfg=general - local enabled + local user + local group - config_get_bool enabled "$cfg" 'enabled' 1 - - [ $enabled -gt 0 ] || return 1 + config_get user "$cfg" 'user' + config_get group "$cfg" 'group' procd_open_instance - procd_set_param command dnsdist --supervised -C /etc/dnsdist.conf + if [ ! -z "$user" -a ! -z "$group" ]; then + procd_set_param command dnsdist --supervised -C /etc/dnsdist.conf -u "$user" -g "$group" + else + procd_set_param command dnsdist --supervised -C /etc/dnsdist.conf + fi procd_set_param file /etc/dnsdist.conf - procd_set_param respawn + procd_set_param respawn 60 0 20 procd_close_instance } + +service_stopped() { + # disable DNS interception if needed + config_load firewall + local intercept + config_get intercept 'dns_int' 'name' '' + if [ ! -z "$intercept" ]; then + uci delete firewall.dns_int + /etc/init.d/firewall restart >/dev/null + fi +} diff --git a/net/dnsdist/files/dnsdist_acl.json b/net/dnsdist/files/dnsdist_acl.json new file mode 100644 index 00000000000000..02d941f6f8c483 --- /dev/null +++ b/net/dnsdist/files/dnsdist_acl.json @@ -0,0 +1,9 @@ +{ + "user": "dnsdist", + "access": { + "dhcp": { + "methods": [ "ipv4leases", "ipv6leases" ] + } + }, + "subscribe": [ "dhcp" ] +} diff --git a/net/dnsdist/files/local-domains.lua b/net/dnsdist/files/local-domains.lua new file mode 100644 index 00000000000000..4bda28c14858ef --- /dev/null +++ b/net/dnsdist/files/local-domains.lua @@ -0,0 +1,117 @@ +local M = {} + +M.lanSuffixes = { 'lan' } +M.ttl = 1 +M.destinations = nil + +local ffi = require 'ffi' +local C = ffi.C + +local byName4 = {} +local byName6 = {} +local dhcpScriptPath = '/usr/share/lua/dnsdist/dnsdist-odhcpd.lua' + +local qname_ret_ptr = ffi.new("char *[1]") +local qname_ret_ptr_param = ffi.cast("const char **", qname_ret_ptr) +local qname_ret_size = ffi.new("size_t[1]") +local qname_ret_size_param = ffi.cast("size_t*", qname_ret_size) + +local ffi_copy = ffi.copy +local ffi_string = ffi.string +local string_sub = string.sub +local string_byte = string.byte +local table_insert = table.insert + +local lanSuffixesMatchNode = nil + +local function lanaction(dq) + if lanSuffixesMatchNode == nil then + return DNSAction.None + end + -- warnlog(string.format("checking type and name, dq=%s, dq.qtype=%s", dq, dq.qtype)) + local qtype = C.dnsdist_ffi_dnsquestion_get_qtype(dq) + local byName + + if qtype == DNSQType.A then + byName = byName4 + elseif qtype == DNSQType.AAAA then + byName = byName6 + else + -- queries for non-address types get an empty NOERROR + C.dnsdist_ffi_dnsquestion_set_rcode(dq, DNSRCode.NOERROR) + return DNSAction.Spoof + end + + C.dnsdist_ffi_dnsquestion_get_qname_raw(dq, qname_ret_ptr_param, qname_ret_size_param) + + local qname = ffi_string(qname_ret_ptr[0], qname_ret_size[0]) + qname = newDNSNameFromRaw(qname) + local suffix = lanSuffixesMatchNode:getBestMatch(qname) + if suffix == nil then + return DNSAction.None + end + if qname == suffix then + -- queries for one of the exact suffixes (i.e `lan.`) get NOERROR + C.dnsdist_ffi_dnsquestion_set_rcode(dq, DNSRCode.NOERROR) + return DNSAction.Spoof + end + qname = qname:makeRelative(suffix) + + local ip = byName[qname:toStringNoDot()] + if ip then + local buf = ffi.new("char[?]", #ip + 1) + ffi_copy(buf, ip) + C.dnsdist_ffi_dnsquestion_set_result(dq, buf, #ip) +-- C.dnsdist_ffi_dnsquestion_set_max_returned_ttl(dq, M.ttl) + return DNSAction.Spoof + else + C.dnsdist_ffi_dnsquestion_set_rcode(dq, DNSRCode.NXDOMAIN) + return DNSAction.Nxdomain + end +end + +function threadmessage(cmd, data) + local name=data.name + local ip=data.ip + local proto=data.proto + local byName + if proto == 'v4' then + byName = byName4 + elseif proto == 'v6' then + byName = byName6 + else + return + end + if name and ip + then + if cmd == 'add' + then + byName[name] = ip + elseif cmd == 'del' + then + byName[name] = nil + else + warnlog(string.format("got unknown command '%s' from odhcpd thread", cmd)) + end + end + -- for k,v in pairs(msg) do warnlog(k..'/'..v) end +end + +function M.run(_) + if M.destinations == nil or M.destinations:size() == 0 or M.lanSuffixes == nil or #M.lanSuffixes == 0 then + return + end + + lanSuffixesMatchNode = newSuffixMatchNode() + lanSuffixesMatchNode:add(M.lanSuffixes) + + local script = io.open(dhcpScriptPath) + if script == nil then + return + end + newThread(script:read("*a")) + local nmgRule = NetmaskGroupRule(M.destinations, false) + addAction(AndRule{nmgRule, SuffixMatchNodeRule(lanSuffixesMatchNode)}, LuaFFIAction(lanaction)) +end + +return M diff --git a/net/dnsdist/files/os.lua b/net/dnsdist/files/os.lua new file mode 100644 index 00000000000000..81d1c0354b1414 --- /dev/null +++ b/net/dnsdist/files/os.lua @@ -0,0 +1,126 @@ +local M = {} + +local ffi = require 'ffi' +local lfs = require 'lfs' +local C = ffi.C + +ffi.cdef[[ +typedef struct timespec { + long tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +} timespec_t; + +int clock_gettime(int clk_id, timespec_t *tp); +typedef unsigned int socklen_t; +const char *inet_ntop(int af, const void *restrict src, + char *restrict dst, socklen_t size); +int inet_pton(int af, const char *restrict src, void *restrict dst); + +void _exit(int status); + +unsigned int sleep(unsigned int seconds); +]] + +local IS_MAC = (ffi.os == "OSX") +local IS_LINUX = (ffi.os == "Linux") + +local CLOCK_MONOTONIC +local AF_INET +local AF_INET6 +if IS_LINUX then + CLOCK_MONOTONIC = 1 + AF_INET = 2 + AF_INET6 = 10 +elseif IS_MAC then + CLOCK_MONOTONIC = 6 + AF_INET = 2 + AF_INET6 = 10 +else + errlog('OS not supported: '..ffi.os) +end + +local tv = ffi.new("struct timespec[?]", 1) + +function M.getTimeUsec() + C.clock_gettime(CLOCK_MONOTONIC, tv) + return tv[0].tv_sec * 1000000 + tv[0].tv_nsec / 1000 +end + +local pton_dst = ffi.new("char[?]", 16) +local inet_buffer = ffi.new("char[?]", 256) + +function M.convertIPv4ToBinary(str) + C.inet_pton(AF_INET, str, pton_dst) + return ffi.string(pton_dst, 4) +end + +function M.convertIPv6ToBinary(str) + C.inet_pton(AF_INET6, str, pton_dst) + return ffi.string(pton_dst, 16) +end + +function M.binaryIPv4ToString(addr) + local str = C.inet_ntop(2, addr, inet_buffer, 256) + return str ~= nil and ffi.string(str) or '' +end + +function M.binaryIPv6ToString(addr) + local str = C.inet_ntop(10, addr, inet_buffer, 256) + return str ~= nil and ffi.string(str) or '' +end + +function M.getCommandReturnCode(command) + return os.execute(command) +end + +function M.getCommandOutput(command) + local desc = io.popen(command) + if desc == nil then + return nil + end + desc:flush() + local output = desc:read('*all') + desc:close() + return output +end + +function M.getFileContent(file) + local desc = io.open(file, "rb") + if desc == nil then + return nil + end + local content = desc:read("*all") + desc:close() + return content +end + +function M.getFileModificationTime(path) + local stats = lfs.attributes(path) + if stats ~= nil then + return stats.modification + end + return 0 +end + +function M.fileExists(path) + local stats = lfs.attributes(path) + if stats ~= nil then + return true + end + return false +end + +function M.exit(code) + C._exit(code) +end + +local os_time = os.time +function M.time() + return os_time() +end + +function M.sleep(seconds) + return C.sleep(seconds) +end + +return M diff --git a/net/dnsdist/files/sample.uci.conf b/net/dnsdist/files/sample.uci.conf new file mode 100644 index 00000000000000..404fed77408205 --- /dev/null +++ b/net/dnsdist/files/sample.uci.conf @@ -0,0 +1,227 @@ +config dnsdist general + option 'enabled' '1' + # private key to use for incoming DoT and DoH + option 'tls_key' '/etc/dnsdist.key' + # certificate to use for incoming DoT and DoH + option 'tls_cert' '/etc/dnsdist.pem' + # the fingerprint of the certificate, so we know we need to reload it + # Example: '37:4F:E4:F7:F2:23:C8:2A:25:F6:43:80:52:42:E2:C9:50:2F:AD:16' + option 'tls_cert_fingerprint' '' + # minimum TLS version to accept ('tls1.0', 'tls1.1', 'tls1.2', 'tls1.3') + option 'tls_min_version' 'tls1.2' + # TLS ciphers used for incoming connections, for TLS < 1.3 (if this value is empty the OpenSSL defaults will be used) + option 'tls_ciphers_incoming' 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305' + # TLS ciphers used for incoming connections, for TLS 1.3 (if this value is empty the OpenSSL defaults will be used) + option 'tls_ciphers13_incoming' '' + # TLS ciphers used for outgoing connections, for TLS < 1.3 (if this value is empty the OpenSSL defaults will be used) + option 'tls_ciphers_outgoing' 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305' + # TLS ciphers used for outgoing connections, for TLS 1.3 (if this value is empty the OpenSSL defaults will be used) + option 'tls_ciphers13_outgoing' '' + # Number of entries in the domain cache. 0 means caching is disabled, the maximum value is 2^32-1 entries + # but is likely limited by the amount of RAM available. + option 'domain_cache_size' '100' + # Cap the TTL of cached records to the supplied number of seconds. Default is 600 (10 minutes), minimum is 1, useful range is up to 86400. + # Setting this value to the highest possible value for a TTL, 2^32-1, means that TTLs will not be capped. + option 'domain_ttl_cap' '600' + # Set how often the cache is scanned to expunge expired entries, in seconds. Default is 60s (every minute), minimum value is 0, useful range is up to 86400 + option 'domain_cleanup_interval' '60' + # The TTL used for local domain responses. The values allowed by the DNS protocol range from 1 to 2^31-1 + option 'local_domains_ttl' '1' + # The DNS suffix, or list of suffixes, identifying local domain names + option 'local_domains_suffix' 'lan' + # The TTL used for sentinel domains responses + option 'sentinel_domains_ttl' '60' + # Number of concurrent connections per client device. 0 means unlimited, the maximum value is 2^64-1. + option 'concurrent_incoming_connections_per_device' '10' + # The DNS engine will consider to accept queries on interfaces whose names match this pattern, + # or list of patterns. When a list of patterns is provided, interfaces matching any pattern will + # be considered. If this value is left empty, the DNS engine will consider all interfaces. + # The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, + # not the ones from the netifd configuration, also called 'logical' interface names. + # The default is the 'br' pattern as OpenWrt defaults to one bridge for local interfaces. + option 'network_interface_include' 'br' + # The DNS engine will NOT consider to accept queries on interfaces whose names match this pattern + # or list of patterns. When a list of patterns is provided, interfaces matching any pattern will + # not be considered. If this value is left empty, no interface will be excluded. + # The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, + # not the ones from the netifd configuration, also called 'logical' interface names. + # The default is to exclude interfaces matching the 'wan' and 'eth0.2' patterns, + # as by default on most routers the wan interface is the second VLAN on the single network interface (eth0), + # but it is sometimes named 'wan' on container hosts. + option 'network_interface_exclude' 'wan eth0.2' + # Pattern or list of patterns identifying WAN interfaces. If 'configuration_check_interval' is + # set, only changes to the interfaces matching these patterns, plus the ones matching + # 'network_interface_include' but not 'network_interface_exclude', will be considered + # configuration changes. When a list of patterns is provided, matching any pattern is + # enough to be considered. If this value is left empty, only the interfaces matching + # 'network_interface_include' but not 'network_interface_exclude' will be considered. + # The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, + # not the ones from the netifd configuration, also called 'logical' interface names. + # Refer to 'network_interface_exclude' for the default value. + option 'network_interface_wan_include' 'wan eth0.2' + # Port on which to accept API queries + option 'web_server_port' '9080' + # Port on which to accept Do53 queries on (UDP and TCP) + option 'do53_port' '53' + # Port on which to accept DoT queries on + option 'dot_port' '853' + # Port on which to accept DoH queries on + option 'doh_port' '443' + # Interval between health-check queries to an upstream resolver, in seconds + option 'default_check_interval' '5' + # Health check timeout to an upstream resolver, in milliseconds + option 'default_check_timeout' '1000' + # Number of failed consecutive health-checks to mark an upstream resolver down + option 'default_max_check_failures' '2' + # Whether upstream resolvers learned via the system should be checked for DoT/DoH support + option 'auto_upgrade_discovered_backends' '0' + # Whether the Do53 version of auto-upgraded resolver should be kept as fallback + option 'keep_auto_upgraded_backends' '1' + # In which internal pool should upgraded resolver be placed. The pool will be created + # if it does not exist. If this value is left empty upgraded resolvers are placed into + # the 'upgraded-to-dox' pool + option 'auto_upgraded_backends_pool' 'upgraded-to-dox' + # How often should the configuration be checked for changes, in seconds. 0 means disabled + option 'configuration_check_interval' '60' + # Domain name for DoT / DoH advertisement, if any. The default is to advertise only for the opportunistic '_dns.resolver.arpa.' name. + option 'advertise_for_domain_name' '' + # User to switch to, after the configuration has been set up. Default is to run as user 'root' + option 'user' 'dnsdist' + # Group to switch to, after the configuration has been set up. Default is to run as group 'root' + option 'group' 'dnsdist' + # Whether to enable 'verbose' logging ('1') or not ('0') + option 'verbose_mode' '0' + # Log verbose-level messages to this file instead of syslog and stdout (the default) + option 'verbose_log_destination' '' + # Maximum number of TCP connections to an upstream resolver. 0 means no limit + option 'default_max_upstream_concurrent_tcp_connections' '0' + # Maximum number of cached idle TCP connections to an upstream resolver. Keeping more connections improves latency but uses memory and increases the load on the upstream resolver + option 'max_idle_tcp_connections_per_downstream' '2' + # Maximum number of cached idle DoH connections to an upstream resolver. Keeping more connections improves latency but uses memory and increases the load on the upstream resolver + option 'max_idle_doh_connections_per_downstream' '2' + # Number of outgoing UDP sockets to an upstream resolver. Higher values reduce the risk of a successful UDP spoofing attempt, but increase the memory usage. The minimum value is 1, and a sane maximum value is 32767, as higher values might require too many sockets. The default is 100 + option 'outgoing_udp_sockets_per_downstream' '100' + # Maximum number of resolvers that can be used at the same time, either from the configuration + # or learned via the system. Additional resolvers, if any, will be skipped. + # Minimum value is 0, maximum is 100, default is 5. + option 'max_upstream_resolvers' '5' + # The maximum size of the sample of queries to record and consider for the + # health-checking mechanism. Higher values use slightly more memory. + # Minimum is 1, maximum is 10000, default is 100. + option 'health_checks_sample_size' '100' + # The minimum amount of regular queries that should have been recorded before the + # health-check threshold can be applied. Minimum is 1, maximum is equal to 'health_checks_sample_size', + # default is 10. + option 'health_checks_min_sample_count' '10' + # The threshold, as a percentage, of queries that should fail for the health-check + # to be triggered. The default is 20 which means 20% of the last 'health_checks_sample_size' queries + # should fail for a health-check to be triggered. Minimum is 1, maximum is 100. + option 'health_checks_threshold' '20' + # The interval, in seconds, between health-check queries. Note that when 'health_checks_exponential_backoff' is set to 1, + # the interval doubles between every queries. These queries are only sent when a threshold of failing regular queries has + # been reached, and until the backend is available again. Minimum is 1, maximum is 86400, default is 30 seconds. + option 'health_checks_failed_interval' '30' + # This setting controls which responses are considered a failure: 'TimeoutOnly' means that only timeout and I/O errors of + # regular queries will be considered for the health-check threshold, while 'TimeoutOrServFail' will also consider 'Server Failure' + # answers. Default is 'TimeoutOrServFail'. + option 'health_checks_mode' 'TimeoutOrServFail' + # Whether the health-check should use an exponential back-off instead of a fixed value, between health-check probes. If set to '0', + # after a backend has been moved to the 'down' state health-check probes are sent every 'health_checks_failed_interval' seconds. + # When set to '1', the delay between each probe starts at 'health_checks_failed_interval' seconds and doubles between every probe, + # capped at 'health_checks_max_backoff' seconds. Default is '1'. + option 'health_checks_exponential_backoff' '1' + # This value, in seconds, caps the time between two health-check queries when 'health_checks_exponential_backoff' is set to '1'. + # The default is 3600 which means that at most one hour will pass between two health-check queries. Minimum is 1, maximum is 86400. + option 'health_checks_max_backoff' '3600' + +# sentinel domains +config domainlist sentinel_domains + # Exact matching of the name will be done unless it starts with '*.' in which case suffix matching will be done + # IPv4 addresses configured on the configured interface will be returned to A queries, IPv6 to AAAA queries. + # The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, + # not the ones from the netifd configuration, also called 'logical' interface names. + list entry 'my.router br-lan' + list entry '*.dohresolver br-lan' + list entry '*.dotresolver br-lan' + +# configuration for a given network interfaces. +# The configuration of the special name 'default_interface' will be applied +# to all interfaces that do not have a specific configuration entry, provided +# that they match 'network_interface_include' and do not match 'network_interface_exclude'. +# The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, +# not the ones from the netifd configuration, also called 'logical' interface names. +config interface + option name 'default_interface' + # Whether dnsdist will listen on this interface + option enabled 1 + # Whether dnsdist will accept Do53 queries, UDP and TCP, on this interface + option do53 1 + # Whether dnsdist will accept DoT queries on this interface + option dot 1 + # Whether dnsdist will accept DoH queries on this interface + option doh 1 + # Whether local domain resolution will be enabled for queries received on this interface + option local_resolution 1 + # Whether sentinel domain resolution will be enabled for queries received on this interface + option sentinel_domains 'sentinel_domains' + # Whether dnsdist will advertise DoT and/or DoH support, if available, in response to DDR queries received on this interface + option advertise 0 + +# Configuration of resolvers +# If no resolver is defined, dnsdist will try to learn +# the addresses of resolvers from /tmp/resolv.conf.auto +#config server quad9doh +# For DoT and DoH servers, the name to use as Server Name Indication +# in the TLS handshake, and against which the certificate presented +# by the server should be validated +# option adn 'dns.quad9.net' +# Type of server: do53, dot or doh +# option type 'doh' +# IP address of the server +# option addr '9.9.9.9' +# Port of the server (default is 53) +# option port 443 +# For DoH servers, the HTTP path to use +# option path '/dns-query' +# Maximum value of concurrent queries to send to this server (TCP and DoT). +# Possible values are 1 (default) to 65535. +# Higher values yield better performance, but only if the server supports it. +# option maxInFlight 1 +# Whether to validate the TLS certificate provided by the server, for +# DoT and DoH ones. Default is true. +# option validate 1 +# How many consecutive health-checks have to fail before this server is +# no longer considered working. The default and minimum value is 1, the +# maximum value is 255 +# option maxCheckFailures 1 +# Frequency of health-check queries, in seconds. The default and minimum value +# is 1, the maximum value is 65535 +# option checkInterval 1 +# Timeout of health-check queries, in milliseconds. Default is 1000 which is one +# second. Supported values are from 1 to 65535. +# option checkTimeout 1000 +# If set, this server will be contacted using this network interface only. +# If this condition cannot be satisfied because the interface is not valid, +# this server will not be used. +# The interface names used are the device names as seen by the kernel, 'physical' and 'virtual' interface names, +# not the ones from the netifd configuration, also called 'logical' interface names. +# option upstreamWANInterface 'wlan0' +# The maximum number of concurrent TCP connections that should be opened +# to this server. The default is 0, which means unlimited. The maximum value +# is 2^32-1. +# option maxConcurrentTCPConnections 0 +# Whether to use the Discovery of Designated Resolvers mechanism to check if +# this server can be upgrade to DoT or DoH +# option autoUpgrade 0 +# If auto-upgrade is enabled, how often should we check if an upgrade is possible, in seconds. +# Default is 3600 which is once every hour. The minimum value is 1 second, the maximum is 2^32-1. +# option autoUpgradeInterval 3600 +# Whether to keep the Do53 version of this backend as a fall-back after a +# successful upgrade to DoT or DoH. Default is false. +# option autoUpgradeKeep false +# The pool to place this server into after a succesfull upgrade. The pool will be +# created if it does not exist +# option autoUpgradePool '' +# The code of the DoH option to use in Discovery of Designated Resolvers exchanges. +# Default is 7, which is what all servers should use. Possible values are 0 to 65535. +# option autoUpgradeDoHKey '7' diff --git a/net/dnsdist/files/start.lua b/net/dnsdist/files/start.lua new file mode 100644 index 00000000000000..638e18f7fca5a0 --- /dev/null +++ b/net/dnsdist/files/start.lua @@ -0,0 +1,78 @@ +local useUCI = true + +includeDirectory('/etc/dnsdist.conf.d/') + +collectgarbage() +local common = require 'dnsdist/common' +local localDomains = require 'dnsdist/local-domains' +local dnsdistOs = require 'dnsdist/os' +common.registerConfigurationDoneHook(localDomains.run) +collectgarbage() +collectgarbage() +collectgarbage("setpause", 100) + +-- fixed configuration options +setMaxUDPOutstanding(50) +setMaxTCPClientThreads(1) +setOutgoingDoHWorkerThreads(1) +setRingBuffersSize(300, 1) +setRingBuffersOptions({recordResponses=false}) +setMaxTCPQueuedConnections(10) +setOutgoingTLSSessionsCacheMaxTicketsPerBackend(5) +setTCPInternalPipeBufferSize(0) +setMaxTCPQueuedConnections(100) +setRandomizedIdsOverUDP(true) +setRandomizedOutgoingSockets(true) + +if useUCI then + local status, _ = pcall(require, 'uci') + if status then + local configuration = require 'dnsdist/configuration' + local config = configuration.loadFromUCI() + collectgarbage() + local loggedonce = false + + while not configuration.enabled(config) or configuration.isBridge() do + local waitTime = tonumber(config['configuration-check-interval']) + if not configuration.enabled(config) then + if not loggedonce then + errlog('Not starting up yet - we are disabled in the configuration') + end + vinfolog('Currently disabled by configuration, next check in '..waitTime..' seconds') + else + if not loggedonce then + errlog('Not starting up yet - the router is in bridge mode') + end + vinfolog('The router is currently in bridge mode, next check in '..waitTime..' seconds') + end + + loggedonce = true + + local ret = dnsdistOs.sleep(tonumber(waitTime)) + if ret > 0 then + -- sleep was interrupted + errlog('Sleep was interrupted - exiting') + os.exit(1) + end + config = configuration.loadFromUCI() + collectgarbage() + collectgarbage() + end + + configuration.apply(config) + + common.runConfigurationDoneHooks(config) + + function maintenance() + common.runMaintenanceHooks() + end + + config = nil + collectgarbage() + collectgarbage() + return + else + errlog('Loading of the configuration from UCI requested but UCI support is not available') + return + end +end diff --git a/net/dnsdist/patches/100-net-snmp-config-Use-netsnmp_agent_libs-instead-of-agent_libs.patch b/net/dnsdist/patches/100-net-snmp-config-Use-netsnmp_agent_libs-instead-of-agent_libs.patch deleted file mode 100644 index 49d72a9739b61a..00000000000000 --- a/net/dnsdist/patches/100-net-snmp-config-Use-netsnmp_agent_libs-instead-of-agent_libs.patch +++ /dev/null @@ -1,23 +0,0 @@ -From d73bc006c62e4340ab56dd4baba5bc8eb8e1db49 Mon Sep 17 00:00:00 2001 -From: Remi Gacogne -Date: Mon, 13 May 2019 16:01:06 +0200 -Subject: [PATCH] SNMP: Use net-snmp-config --netsnmp-agent-libs instead of - --agent-libs - ---- - m4/pdns_with_net_snmp.m4 | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/m4/pdns_with_net_snmp.m4 b/m4/pdns_with_net_snmp.m4 -index 8040672e6c..2da80c75fc 100644 ---- a/m4/pdns_with_net_snmp.m4 -+++ b/m4/pdns_with_net_snmp.m4 -@@ -10,7 +10,7 @@ AC_DEFUN([PDNS_WITH_NET_SNMP], [ - AS_IF([test "x$with_net_snmp" != "xno"], [ - AS_IF([test "x$with_net_snmp" = "xyes" -o "x$with_net_snmp" = "xauto"], [ - AC_CHECK_PROG([NET_SNMP_CFLAGS], [net-snmp-config], [`net-snmp-config --cflags`]) -- AC_CHECK_PROG([NET_SNMP_LIBS], [net-snmp-config], [`net-snmp-config --agent-libs`]) -+ AC_CHECK_PROG([NET_SNMP_LIBS], [net-snmp-config], [`net-snmp-config --netsnmp-agent-libs`]) - AC_CHECK_DECLS([snmp_select_info2], [ : ], [ : ], - [AC_INCLUDES_DEFAULT - #include diff --git a/net/dnsdist/patches/200-libatomic-detect.patch b/net/dnsdist/patches/200-libatomic-detect.patch deleted file mode 100644 index 29881cc4fc47b7..00000000000000 --- a/net/dnsdist/patches/200-libatomic-detect.patch +++ /dev/null @@ -1,34 +0,0 @@ ---- a/m4/pdns_check_os.m4 -+++ b/m4/pdns_check_os.m4 -@@ -35,16 +35,21 @@ - AM_CONDITIONAL([HAVE_LINUX], [test "x$have_linux" = "xyes"]) - AM_CONDITIONAL([HAVE_SOLARIS], [test "x$have_solaris" = "xyes"]) - -- case "$host" in -- mips* | powerpc-* ) -- AC_MSG_CHECKING([whether the linker accepts -latomic]) -- LDFLAGS="-latomic $LDFLAGS" -- AC_LINK_IFELSE([m4_default([],[AC_LANG_PROGRAM()])], -- [AC_MSG_RESULT([yes])], -- [AC_MSG_ERROR([Unable to link against libatomic, cannot continue])] -- ) -- ;; -- esac -+ AC_MSG_CHECKING([whether -latomic is needed for __atomic builtins]) -+ AC_LINK_IFELSE( -+ [AC_LANG_PROGRAM([[#include ]], -+ [[uint64_t val = 0; __atomic_add_fetch(&val, 1, __ATOMIC_RELAXED);]] -+ )], -+ [AC_MSG_RESULT([no])], -+ [LIBS="$LIBS -latomic" -+ AC_LINK_IFELSE( -+ [AC_LANG_PROGRAM([[#include ]], -+ [[uint64_t val = 0; __atomic_add_fetch(&val, 1, __ATOMIC_RELAXED);]] -+ )], -+ [AC_MSG_RESULT([yes])], -+ [AC_MSG_FAILURE([libatomic needed, but linking with -latomic failed, cannot continue])] -+ )] -+ ) - - AC_SUBST(THREADFLAGS) - AC_SUBST([DYNLINKFLAGS], [-export-dynamic]) diff --git a/net/dnsdist/patches/300-openssl-deprecated.patch b/net/dnsdist/patches/300-openssl-deprecated.patch deleted file mode 100644 index ff80ee255089c7..00000000000000 --- a/net/dnsdist/patches/300-openssl-deprecated.patch +++ /dev/null @@ -1,29 +0,0 @@ ---- a/tcpiohandler.cc -+++ b/tcpiohandler.cc -@@ -369,8 +369,10 @@ public: - } - - if (s_users.fetch_add(1) == 0) { -+#if OPENSSL_VERSION_NUMBER < 0x10100000L - ERR_load_crypto_strings(); - OpenSSL_add_ssl_algorithms(); -+#endif - openssl_thread_setup(); - - s_ticketsKeyIndex = SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); -@@ -439,6 +441,7 @@ public: - d_tlsCtx.reset(); - - if (s_users.fetch_sub(1) == 1) { -+#if OPENSSL_VERSION_NUMBER < 0x10100000L - ERR_free_strings(); - - EVP_cleanup(); -@@ -448,6 +451,7 @@ public: - CONF_modules_unload(1); - - CRYPTO_cleanup_all_ex_data(); -+#endif - openssl_thread_cleanup(); - } - }