summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in7
-rw-r--r--RELNOTES12
-rw-r--r--aclocal.m41
-rwxr-xr-xconfigure159
-rw-r--r--configure.ac31
-rw-r--r--doc/Makefile.in7
-rw-r--r--doc/reference.texi14
-rw-r--r--doc/running.texi74
-rw-r--r--m4/ax_recvmmsg.m496
-rw-r--r--samples/Makefile.in7
-rw-r--r--samples/knot.full.conf8
-rw-r--r--src/Makefile.in7
-rw-r--r--src/common/log.c11
-rw-r--r--src/common/log.h9
-rw-r--r--src/knot.conf.52
-rw-r--r--src/knot/conf/conf.c4
-rw-r--r--src/knot/conf/conf.h2
-rw-r--r--src/knot/ctl/knotc_main.c106
-rw-r--r--src/knot/ctl/remote.c324
-rw-r--r--src/knot/ctl/remote.h12
-rw-r--r--src/knot/main.c24
-rw-r--r--src/knot/server/rrl.c269
-rw-r--r--src/knot/server/rrl.h54
-rw-r--r--src/knot/server/server.c2
-rw-r--r--src/knotc.85
-rw-r--r--src/knotd.82
-rw-r--r--src/libknot/packet/packet.c3
-rw-r--r--src/tests/knot/rrl_tests.c111
28 files changed, 1049 insertions, 314 deletions
diff --git a/Makefile.in b/Makefile.in
index 21d7df3..4135b77 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -58,9 +58,10 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \
$(top_srcdir)/m4/ax_ext.m4 \
$(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \
- $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
- $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
- $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/ax_recvmmsg.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
diff --git a/RELNOTES b/RELNOTES
index 9060ba0..af80626 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -1,3 +1,15 @@
+v1.2.0-rc4 - Mar 22, 2013
+-------------------------
+
+Features:
+ * knotc 'zonestatus' command
+
+Bugfixes:
+ * Changing logfile ownership before dropping privileges
+ * knotc respects 'control' section from configuration
+ * RRL: resolved bucket collisions
+ * RRL: updated bucket mapping to conform RRL technical memo
+
v1.2.0-rc3 - Mar 1, 2013
------------------------
diff --git a/aclocal.m4 b/aclocal.m4
index a220341..f6f3c23 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1022,6 +1022,7 @@ AC_SUBST([am__untar])
m4_include([m4/ax_check_compiler_flags.m4])
m4_include([m4/ax_ext.m4])
m4_include([m4/ax_gcc_x86_cpuid.m4])
+m4_include([m4/ax_recvmmsg.m4])
m4_include([m4/libtool.m4])
m4_include([m4/ltoptions.m4])
m4_include([m4/ltsugar.m4])
diff --git a/configure b/configure
index 3dfabbd..c70bff3 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for knot 1.2.0-rc3.
+# Generated by GNU Autoconf 2.69 for knot 1.2.0-rc4.
#
# Report bugs to <knot-dns@labs.nic.cz>.
#
@@ -590,8 +590,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='knot'
PACKAGE_TARNAME='knot'
-PACKAGE_VERSION='1.2.0-rc3'
-PACKAGE_STRING='knot 1.2.0-rc3'
+PACKAGE_VERSION='1.2.0-rc4'
+PACKAGE_STRING='knot 1.2.0-rc4'
PACKAGE_BUGREPORT='knot-dns@labs.nic.cz'
PACKAGE_URL=''
@@ -1319,7 +1319,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures knot 1.2.0-rc3 to adapt to many kinds of systems.
+\`configure' configures knot 1.2.0-rc4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1389,7 +1389,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of knot 1.2.0-rc3:";;
+ short | recursive ) echo "Configuration of knot 1.2.0-rc4:";;
esac
cat <<\_ACEOF
@@ -1509,7 +1509,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-knot configure 1.2.0-rc3
+knot configure 1.2.0-rc4
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2062,7 +2062,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by knot $as_me 1.2.0-rc3, which was
+It was created by knot $as_me 1.2.0-rc4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2889,7 +2889,7 @@ fi
# Define the identity of the package.
PACKAGE='knot'
- VERSION='1.2.0-rc3'
+ VERSION='1.2.0-rc4'
cat >>confdefs.h <<_ACEOF
@@ -13425,28 +13425,155 @@ $as_echo "#define DEBUG_ENABLE_BRIEF 1" >>confdefs.h
fi
+
+
+
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for recv_mmsg support" >&5
+$as_echo_n "checking for recv_mmsg support... " >&6; }
+if ${ax_cv_have_msg_waitforone+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ if test "$cross_compiling" = yes; then :
+ ax_cv_have_msg_waitforone=no
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+#ifndef _GNU_SOURCE
+ #define _GNU_SOURCE
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+volatile int _intr = 0;
+void sighandle(int s) {
+ _intr = 1;
+}
+
+int
+main ()
+{
+
+#ifndef MSG_WAITFORONE
+ return 3; /* Not supported. */
+#else
+ int port = 35353;
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) return 1;
+ struct mmsghdr msgs[2];
+ struct iovec iovecs[2];
+ char bufs[2][64];
+ unsigned i = 0;
+ memset(msgs, 0, sizeof(msgs));
+ for (i = 0; i < 2; i++) {
+ iovecs[i].iov_base = bufs[i];
+ iovecs[i].iov_len = 64;
+ msgs[i].msg_hdr.msg_iov = &iovecs[i];
+ msgs[i].msg_hdr.msg_iovlen = 1;
+ }
+
+ struct sockaddr_in sa; /* Bind to socket. */
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sa.sin_port = htons(port); /* Find free port. */
+ while (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
+ if (errno == EADDRINUSE) sa.sin_port = ++port;
+ else break;
+ }
+
+ int cfd = socket(AF_INET, SOCK_DGRAM, 0); /* Send datagram. */
+ char pkt[32] = { '\xdf' };
+ sendto(cfd, pkt, sizeof(pkt), 0, (struct sockaddr*)&sa, sizeof(sa));
+
+ /* Broken implementation doesn't respect recvmmsg timeout. */
+ struct sigaction aset;
+ memset(&aset, 0, sizeof(struct sigaction));
+ aset.sa_handler = sighandle;
+ sigaction(SIGALRM, &aset, NULL);
+ alarm(1);
+
+ int ret = recvmmsg(fd, msgs, 2, MSG_WAITFORONE, NULL);
+ close(cfd);
+ close(fd);
+ if (ret < 0) { /* Completely failed. */
+ return 2;
+ }
+
+ return _intr; /* OK if not interrupted. */
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ax_cv_have_msg_waitforone=yes
+else
+ ax_cv_have_msg_waitforone=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_msg_waitforone" >&5
+$as_echo "$ax_cv_have_msg_waitforone" >&6; }
+
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
# recvmmsg() (valgrind doesn't support it, so disable for debugging)
# Check whether --enable-recvmmsg was given.
if test "${enable_recvmmsg+set}" = set; then :
enableval=$enable_recvmmsg; case "${enableval}" in
yes)
+ if test "$ax_cv_have_msg_waitforone" = "yes"; then
$as_echo "#define ENABLE_RECVMMSG 1" >>confdefs.h
- ;;
+ recvmmsg=true
+ else
+ recvmmsg=false
+ fi
+ ;;
no)
- recvmmsg=false
- ;;
+ recvmmsg=false
+ ;;
*)
- as_fn_error $? "bad value ${enableval} for --enable-recvmmsg" "$LINENO" 5
- ;;
+ as_fn_error $? "bad value ${enableval} for --enable-recvmmsg" "$LINENO" 5
+ ;;
esac
else
+ if test "$ax_cv_have_msg_waitforone" = "yes"; then
$as_echo "#define ENABLE_RECVMMSG 1" >>confdefs.h
- recvmmsg=true
+ recvmmsg=true
+ else
+ recvmmsg=false
+ fi
fi
@@ -15342,7 +15469,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by knot $as_me 1.2.0-rc3, which was
+This file was extended by knot $as_me 1.2.0-rc4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -15408,7 +15535,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-knot config.status 1.2.0-rc3
+knot config.status 1.2.0-rc4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index f593068..23ff7c0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
# -*- Autoconf -*-
AC_PREREQ([2.60])
-AC_INIT([knot], [1.2.0-rc3], [knot-dns@labs.nic.cz])
+AC_INIT([knot], [1.2.0-rc4], [knot-dns@labs.nic.cz])
AM_INIT_AUTOMAKE([gnu -Wall -Werror])
AC_CONFIG_SRCDIR([src/knot/main.c])
AC_CONFIG_HEADERS([src/config.h])
@@ -103,22 +103,32 @@ AC_ARG_ENABLE([debuglevel],
;;
esac], [])
+AX_MSG_WAITFORONE
# recvmmsg() (valgrind doesn't support it, so disable for debugging)
AC_ARG_ENABLE([recvmmsg],
AS_HELP_STRING([--enable-recvmmsg=yes|no], [enable recvmmsg() network API under Linux (kernel support required) (set to 'no' if you have trouble running server under valgrind) [default=yes]]),
[case "${enableval}" in
yes)
- AC_DEFINE([ENABLE_RECVMMSG], [1], [recvmmsg enabled])
- ;;
+ if test "$ax_cv_have_msg_waitforone" = "yes"; then
+ AC_DEFINE([ENABLE_RECVMMSG], [1], [recvmmsg enabled])
+ recvmmsg=true
+ else
+ recvmmsg=false
+ fi
+ ;;
no)
- recvmmsg=false
- ;;
+ recvmmsg=false
+ ;;
*)
- AC_MSG_ERROR([bad value ${enableval} for --enable-recvmmsg])
- ;;
+ AC_MSG_ERROR([bad value ${enableval} for --enable-recvmmsg])
+ ;;
esac], [
- AC_DEFINE([ENABLE_RECVMMSG], [1], [recvmmsg enabled])
- recvmmsg=true
+ if test "$ax_cv_have_msg_waitforone" = "yes"; then
+ AC_DEFINE([ENABLE_RECVMMSG], [1], [recvmmsg enabled])
+ recvmmsg=true
+ else
+ recvmmsg=false
+ fi
])
# Check for link time optimizations support and predictive commoning
@@ -128,8 +138,7 @@ AC_ARG_ENABLE([lto],
yes) AX_CHECK_COMPILER_FLAGS("-flto", [CFLAGS="$CFLAGS -flto"], []) ;;
no) ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-lto]) ;;
- esac], [
- ])
+ esac])
AX_CHECK_COMPILER_FLAGS("-fpredictive-commoning", [CFLAGS="$CFLAGS -fpredictive-commoning"], [])
diff --git a/doc/Makefile.in b/doc/Makefile.in
index 779e241..3d391d2 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -57,9 +57,10 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \
$(top_srcdir)/m4/ax_ext.m4 \
$(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \
- $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
- $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
- $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/ax_recvmmsg.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
diff --git a/doc/reference.texi b/doc/reference.texi
index 2d9ac7e..bb2bf39 100644
--- a/doc/reference.texi
+++ b/doc/reference.texi
@@ -201,10 +201,12 @@ Default value: @kbd{0 (disabled)}
@vindex rate-limit-size
Option controls the size of a hashtable of buckets. The larger the hashtable, the lesser probability of a hash collision, but
-at the expense of additional memory costs. Each bucket is estimated roughly to 16B.
+at the expense of additional memory costs. Each bucket is estimated roughly to 32B.
Size should be selected as a reasonably large prime due to the better hash function distribution properties.
+Hash table is internally chained and works well up to a fill rate of 90%, general rule of thumb is to select a prime near
+@kbd{1.2 * maximum_qps}.
-Default value: @kbd{1572869}
+Default value: @kbd{393241}
@node rate-limit-slip
@subsubsection rate-limit-slip
@@ -664,7 +666,9 @@ Remotes are defined in @code{remotes} section of configuration file (@pxref{remo
@vindex semantic-checks
@code{semantic-checks} statement turns on optional semantic checks for this particular zone.
-See @ref{zones List of zone semantic checks} for more information. Possible values are @code{on} and @code{off}.
+See @ref{zones List of zone semantic checks} for more information.
+
+Possible values are @code{on} and @code{off}.
Most checks are disabled by default.
@node ixfr-from-differences
@@ -673,7 +677,9 @@ Most checks are disabled by default.
EXPERIMENTAL: option @code{ixfr-from-differences} is only relevant if you are running Knot DNS as a master for this zone.
By turning the feature on you tell Knot to create differences from changes you made to a zone file upon server reload.
-See @ref{Controlling running daemon} for more information. Possible values are @code{on} and @code{off}. Disabled by default.
+See @ref{Controlling running daemon} for more information.
+
+Possible values are @code{on} and @code{off}. Disabled by default.
@node disable-any
@subsubsection disable-any
diff --git a/doc/running.texi b/doc/running.texi
index 628cd7d..bee48f3 100644
--- a/doc/running.texi
+++ b/doc/running.texi
@@ -13,49 +13,45 @@ effort, so each time the zone file changes you need to compile it.
@example
$ knotc -c knot.conf compile
@end example
-Or alternatively, you can compile automatically using the @code{-a} flag.
+Knot DNS automatically compiles zones on some actions (@code{start|restart}).
@example
-$ knotc -a -c knot.conf start|reload|restart
+$ knotc -c knot.conf start|reload|restart
@end example
The tool @code{knotc} is designed as a front-end for user, making it easier
to do everything from zone compilation to controlling the server daemon.
-To communicate with the binary, it reads the PID from the @emph{PID file} specified in the configuration @code{pidfile} and sends POSIX signals to it (@pxref{pidfile}).
-If you want to control the daemon directly, use @code{SIGINT} to quit the process or @code{SIGHUP} to reload configuration. Signal @code{SIGUSR2} is currently used to refresh slave zones.
+If you want to control the daemon directly, use @code{SIGINT} to quit the process or @code{SIGHUP} to reload configuration.
@example
-Usage: knotc [parameters] start|stop|restart|reload|running|compile
+Usage: knotc [parameters] <action> [action_args]
+
Parameters:
- -c [file], --config=[file]
- Select configuration file.
- -j [num], --jobs=[num]
- Number of parallel tasks to run when compiling.
- -f, --force
- Force operation - override some checks.
- -v, --verbose
- Verbose mode - additional runtime information.
- -V, --version
- Print knot server version.
- -w, --wait
- Wait for the server to finish start/stop operations.
- -i, --interactive
- Interactive mode (do not daemonize).
- -a, --auto
- Enable automatic recompilation (start or reload).
- -h, --help
- Print help and usage.
+ -c [file], --config=[file] Select configuration file.
+ -j [num], --jobs=[num] Number of parallel tasks to run when compiling.
+ -s [server] Remote server address (default 127.0.0.1)
+ -p [port] Remote server port (default 5553)
+ -y [hmac:]name:key] Use key_id specified on the command line.
+ -k [file] Use key file (as in config section 'keys').
+ f.e. echo "knotc-key hmac-md5 Wg==" > knotc.key
+ -f, --force Force operation - override some checks.
+ -v, --verbose Verbose mode - additional runtime information.
+ -V, --version Print knot server version.
+ -w, --wait Wait for the server to finish start/stop operations.
+ -i, --interactive Interactive mode (do not daemonize).
+ -h, --help Print help and usage.
Actions:
- start Start knot server zone (no-op if running).
- stop Stop knot server (no-op if not running).
- restart Stops and then starts knot server.
- reload Reload knot configuration and compiled zones.
- refresh Refresh all slave zones.
- running Check if server is running.
- checkconf Check server configuration.
- checkzone Check zones (accepts specific zones,
- e.g. 'knotc checkzone example1.com example2.com').
- compile Compile zones (accepts specific zones, see above).
+ start Start server (no-op if running).
+ stop Stop server (no-op if running).
+ restart Restarts server (no-op if running).
+ reload Reloads configuration and changed zones.
+ refresh Refresh slave zones (all if not specified).
+ flush Flush journal and update zone files.
+ status Check if server is running.
+ zonestatus Show status of configured zones.
+ checkconf Check server configuration.
+ checkzone Check specified zone files.
+ compile Compile zone files (all if not specified).
@end example
If you want to run Knot DNS daemon directly, you can use @code{knotd} binary
@@ -129,7 +125,8 @@ When the server is running, you can control the daemon, see @ref{Controlling run
@section Running a master server
Knot DNS first needs to compile the zones before it can load them, therefore you need to
-compile them with the @code{knotc compile} action or use flag @code{-a} to compile the zones automatically.
+compile them with the @code{knotc compile} action.
+Some actions like @code{start} or @code{restart} compile zones automatically.
If you want to just check the zone files first before starting,
you can use @code{knotc checkzone} action.
@@ -146,7 +143,7 @@ $ knotc -w -c master.conf start
Or you can compile it automatically:
@example
$ knotc -c master.conf checkconf # check configuration
-$ knotc -a -w -c master.conf start
+$ knotc -w -c master.conf start
@end example
@node Controlling running daemon
@@ -162,12 +159,7 @@ $ knotc -c master.conf compile # compile updated zones
$ knotc -c master.conf reload # reconfigure and load updated zones
@end example
-Or use the @code{-a} again.
-@example
-$ knotc -a -c master.conf reload # compile zones and reconfigure
-@end example
-
-If you want @emph{IXFR-out} differences created from changes you make to a zone file, enable @code{ixfr-from-differences}
+If you want @emph{IXFR-out} differences created from changes you make to a zone file, enable @ref{ixfr-from-differences}
in @code{zones} statement, then compile the zone and reload your server as seen above.
If @emph{SOA}'s @emph{serial} is not changed no differences will be created. Please note
that this feature is in @emph{experimental} stage and should be used with care.
diff --git a/m4/ax_recvmmsg.m4 b/m4/ax_recvmmsg.m4
new file mode 100644
index 0000000..eb5c4d6
--- /dev/null
+++ b/m4/ax_recvmmsg.m4
@@ -0,0 +1,96 @@
+dnl @synopsis AX_MSG_WAITFORONE
+dnl @summary Test if the libc/kernel have working recvmmsg MSG_WAITFORONE implementation
+dnl @category Misc
+dnl
+dnl We need recvmmsg to support MSG_WAITFORONE. RHEL 6 (and derivates)
+dnl are know for broken implementation
+dnl
+dnl @version 2013-03-12
+dnl @license GPL
+dnl @author Ondřej Surý <ondrej@sury.org> and Marek Vavruša <marek@vavrusa.com>
+
+AC_DEFUN([AX_MSG_WAITFORONE],
+[
+ AC_REQUIRE([AC_PROG_CC])
+
+ AC_LANG_PUSH([C])
+
+ AC_CACHE_CHECK([for recv_mmsg support], [ax_cv_have_msg_waitforone],
+ [
+ AC_RUN_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+#ifndef _GNU_SOURCE
+ #define _GNU_SOURCE
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+volatile int _intr = 0;
+void sighandle(int s) {
+ _intr = 1;
+}
+ ]],[[
+#ifndef MSG_WAITFORONE
+ return 3; /* Not supported. */
+#else
+ int port = 35353;
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) return 1;
+ struct mmsghdr msgs[2];
+ struct iovec iovecs[2];
+ char bufs[2][64];
+ unsigned i = 0;
+ memset(msgs, 0, sizeof(msgs));
+ for (i = 0; i < 2; i++) {
+ iovecs[i].iov_base = bufs[i];
+ iovecs[i].iov_len = 64;
+ msgs[i].msg_hdr.msg_iov = &iovecs[i];
+ msgs[i].msg_hdr.msg_iovlen = 1;
+ }
+
+ struct sockaddr_in sa; /* Bind to socket. */
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sa.sin_port = htons(port); /* Find free port. */
+ while (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
+ if (errno == EADDRINUSE) sa.sin_port = ++port;
+ else break;
+ }
+
+ int cfd = socket(AF_INET, SOCK_DGRAM, 0); /* Send datagram. */
+ char pkt[32] = { '\xdf' };
+ sendto(cfd, pkt, sizeof(pkt), 0, (struct sockaddr*)&sa, sizeof(sa));
+
+ /* Broken implementation doesn't respect recvmmsg timeout. */
+ struct sigaction aset;
+ memset(&aset, 0, sizeof(struct sigaction));
+ aset.sa_handler = sighandle;
+ sigaction(SIGALRM, &aset, NULL);
+ alarm(1);
+
+ int ret = recvmmsg(fd, msgs, 2, MSG_WAITFORONE, NULL);
+ close(cfd);
+ close(fd);
+ if (ret < 0) { /* Completely failed. */
+ return 2;
+ }
+
+ return _intr; /* OK if not interrupted. */
+#endif
+ ]])],
+ [ax_cv_have_msg_waitforone=yes],
+ [ax_cv_have_msg_waitforone=no],
+ [ax_cv_have_msg_waitforone=no])])
+
+ AC_LANG_POP([C])
+])
diff --git a/samples/Makefile.in b/samples/Makefile.in
index 44b12ae..b307eda 100644
--- a/samples/Makefile.in
+++ b/samples/Makefile.in
@@ -55,9 +55,10 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \
$(top_srcdir)/m4/ax_ext.m4 \
$(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \
- $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
- $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
- $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/ax_recvmmsg.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
diff --git a/samples/knot.full.conf b/samples/knot.full.conf
index f6a9d89..8c02d3c 100644
--- a/samples/knot.full.conf
+++ b/samples/knot.full.conf
@@ -70,10 +70,10 @@ system {
# Number of hashtable buckets, set to reasonable value as default.
# We chose a reasonably large prime number as it's used for hashtable size,
# it is recommended to do so as well due to better distribution.
- # Tweak if you experience a lot of hash collisions, estimated memory overhead
- # is approx. 16B per bucket
- # Default: 1572869
- rate-limit-size 1572869;
+ # Rule of thumb is to set it to about 1.2 * (maximum_qps)
+ # Memory cost is approx. 32B per bucket
+ # Default: 393241
+ rate-limit-size 393241;
# Rate limit SLIP
# Each Nth blocked response will be sent as truncated, this is a way to allow
diff --git a/src/Makefile.in b/src/Makefile.in
index 4991a28..61a7298 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -66,9 +66,10 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \
$(top_srcdir)/m4/ax_ext.m4 \
$(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \
- $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
- $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
- $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/ax_recvmmsg.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
diff --git a/src/common/log.c b/src/common/log.c
index 5267d2d..ff6475e 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -331,3 +331,14 @@ void hex_log(int source, const char *data, int length)
log_msg(source, LOG_DEBUG, "%s\n", lbuf);
}
}
+
+int log_update_privileges(int uid, int gid)
+{
+ for (unsigned i = 0; i < LOG_FDS_OPEN; ++i) {
+ if (fchown(fileno(LOG_FDS[i]), uid, gid) < 0) {
+ return KNOT_ERROR;
+ }
+
+ }
+ return KNOT_EOK;
+}
diff --git a/src/common/log.h b/src/common/log.h
index 8b9e311..639957c 100644
--- a/src/common/log.h
+++ b/src/common/log.h
@@ -203,6 +203,15 @@ void hex_log(int source, const char *data, int length);
#define log_zone_info(msg...) log_msg(LOG_ZONE, LOG_INFO, msg)
#define log_zone_debug(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg)
+/*!
+ * \brief Update open files ownership.
+ * \param uid New owner id.
+ * \param gid New group id.
+ * \retval KNOT_EOK if success
+ * \retval KNOT_ERROR on error
+ */
+int log_update_privileges(int uid, int gid);
+
#endif /* _KNOTD_LOG_H_ */
/*! @} */
diff --git a/src/knot.conf.5 b/src/knot.conf.5
index 734c812..ccd2960 100644
--- a/src/knot.conf.5
+++ b/src/knot.conf.5
@@ -1,4 +1,4 @@
-.TH "knot.conf" "5" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc3"
+.TH "knot.conf" "5" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc4"
.SH "NAME"
.LP
.B knot.conf
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index f93eefe..28d33e0 100644
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -357,8 +357,8 @@ void __attribute__ ((constructor)) conf_init()
/* Create default interface. */
conf_iface_t * iface = malloc(sizeof(conf_iface_t));
memset(iface, 0, sizeof(conf_iface_t));
- iface->name = strdup("any");
- iface->address = strdup("0.0.0.0");
+ iface->name = strdup("localhost");
+ iface->address = strdup("127.0.0.1");
iface->port = CONFIG_DEFAULT_PORT;
add_tail(&s_config->ifaces, &iface->n);
++s_config->ifaces_count;
diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h
index f56d92d..380d9df 100644
--- a/src/knot/conf/conf.h
+++ b/src/knot/conf/conf.h
@@ -51,7 +51,7 @@
#define CONFIG_HANDSHAKE_WD 10 /*!< [secs] for connection to make a request.*/
#define CONFIG_IDLE_WD 60 /*!< [secs] of allowed inactivity between requests */
#define CONFIG_RRL_SLIP 2 /*!< Default slip value. */
-#define CONFIG_RRL_SIZE 1572869 /*!< Htable default size. */
+#define CONFIG_RRL_SIZE 393241 /*!< Htable default size. */
/*!
* \brief Configuration for the interface
diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c
index 3881ab9..3354cae 100644
--- a/src/knot/ctl/knotc_main.c
+++ b/src/knot/ctl/knotc_main.c
@@ -86,6 +86,7 @@ static int cmd_reload(int argc, char *argv[], unsigned flags, int jobs);
static int cmd_refresh(int argc, char *argv[], unsigned flags, int jobs);
static int cmd_flush(int argc, char *argv[], unsigned flags, int jobs);
static int cmd_status(int argc, char *argv[], unsigned flags, int jobs);
+static int cmd_zonestatus(int argc, char *argv[], unsigned flags, int jobs);
static int cmd_checkconf(int argc, char *argv[], unsigned flags, int jobs);
static int cmd_checkzone(int argc, char *argv[], unsigned flags, int jobs);
static int cmd_compile(int argc, char *argv[], unsigned flags, int jobs);
@@ -99,6 +100,7 @@ knot_cmd_t knot_cmd_tbl[] = {
{"refresh", &cmd_refresh,"Refresh slave zones (all if not specified).",0},
{"flush", &cmd_flush, "\tFlush journal and update zone files.",0},
{"status", &cmd_status, "\tCheck if server is running.",0},
+ {"zonestatus",&cmd_zonestatus, "Show status of configured zones.",0},
{"checkconf", &cmd_checkconf, "Check server configuration.",1},
{"checkzone", &cmd_checkzone, "Check specified zone files.",1},
{"compile", &cmd_compile, "Compile zone files (all if not specified).",1},
@@ -281,12 +283,7 @@ static int cmd_remote_print_reply(const knot_rrset_t *rr)
}
/* Parse TXT. */
- char* txt = remote_parse_txt(rd);
- if (txt) {
- log_server_info("Server reply: %s\n", txt);
- }
- free(txt);
-
+ remote_print_txt(rd);
rd = knot_rrset_rdata_next(rr, rd);
}
@@ -362,7 +359,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[])
break;
case KNOT_RRTYPE_TXT:
default:
- rd = remote_create_txt(argv[i]);
+ rd = remote_create_txt(argv[i], strlen(argv[i]));
break;
}
knot_rrset_add_rdata(rr, rd);
@@ -390,8 +387,11 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[])
/* Wait for reply. */
if (rc == 0) {
- int ret = cmd_remote_reply(s);
- if (ret != KNOT_EOK) {
+ int ret = KNOT_EOK;
+ while (ret != KNOT_ECONN) {
+ ret = cmd_remote_reply(s);
+ }
+ if (ret != KNOT_EOK && ret != KNOT_ECONN) {
log_server_warning("Remote command reply: %s\n",
knot_strerror(ret));
rc = 1;
@@ -399,6 +399,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[])
}
/* Cleanup. */
+ printf("\n");
knot_rrset_deep_free(&rr, 1, 1, 1);
/* Close connection. */
@@ -556,8 +557,10 @@ static int tsig_parse_file(knot_key_t *k, const char *f)
static void tsig_key_cleanup(knot_key_t *k)
{
- knot_dname_free(&k->name);
- free(k->secret);
+ if (k) {
+ knot_dname_free(&k->name);
+ free(k->secret);
+ }
}
int main(int argc, char **argv)
@@ -567,16 +570,18 @@ int main(int argc, char **argv)
unsigned jobs = 1;
unsigned flags = F_NULL;
char *config_fn = NULL;
+ char *default_config = conf_find_default();
/* Remote server descriptor. */
int ret = KNOT_EOK;
- const char *r_addr = "127.0.0.1";
- int r_port = REMOTE_DPORT;
+ const char *r_addr = NULL;
+ int r_port = -1;
knot_key_t r_key;
memset(&r_key, 0, sizeof(knot_key_t));
/* Initialize. */
log_init();
+ log_levels_set(LOG_SYSLOG, LOG_ANY, 0);
/* Long options. */
struct option opts[] = {
@@ -647,6 +652,8 @@ int main(int argc, char **argv)
optarg);
help(argc, argv);
log_close();
+ free(config_fn);
+ free(default_config);
return 1;
}
@@ -654,12 +661,16 @@ int main(int argc, char **argv)
case 'V':
printf("%s, version %s\n", "Knot DNS", PACKAGE_VERSION);
log_close();
+ free(config_fn);
+ free(default_config);
return 0;
case 'h':
case '?':
default:
help(argc, argv);
log_close();
+ free(config_fn);
+ free(default_config);
return 1;
}
}
@@ -669,6 +680,8 @@ int main(int argc, char **argv)
help(argc, argv);
tsig_key_cleanup(&r_key);
log_close();
+ free(config_fn);
+ free(default_config);
return 1;
}
@@ -684,38 +697,33 @@ int main(int argc, char **argv)
/* Command not found. */
if (!cmd->name) {
log_server_error("Invalid command: '%s'\n", argv[optind]);
- free(config_fn);
tsig_key_cleanup(&r_key);
log_close();
+ free(config_fn);
+ free(default_config);
return 1;
}
/* Open config, allow if not exists. */
- if (cmd->need_conf) {
- if (!config_fn) {
- config_fn = conf_find_default();
- }
- if (conf_open(config_fn) != KNOT_EOK) {
+ if (conf_open(config_fn) != KNOT_EOK) {
+ if(conf_open(default_config) != KNOT_EOK) {
flags |= F_NOCONF;
}
- free(config_fn);
- config_fn = NULL;
}
- /* Discard remote interface. */
+ /* Create remote iface if not present in config. */
conf_iface_t *ctl_if = conf()->ctl.iface;
- conf_free_iface(ctl_if);
-
- /* Update remote interface. */
- ctl_if = malloc(sizeof(conf_iface_t));
- if (ctl_if) {
+ if (!ctl_if) {
+ ctl_if = malloc(sizeof(conf_iface_t));
+ assert(ctl_if);
+ conf()->ctl.iface = ctl_if;
memset(ctl_if, 0, sizeof(conf_iface_t));
- ctl_if->address = strdup(r_addr);
- ctl_if->port = r_port;
- ctl_if->family = AF_INET;
- if (strchr(r_addr, ':')) { /* Dumb way to check for v6 addr. */
- ctl_if->family = AF_INET6;
- }
+
+ /* Fill defaults. */
+ if (!r_addr) r_addr = "127.0.0.1";
+ if (r_port < 0) r_port = REMOTE_DPORT;
+
+ /* Create empty key. */
if (r_key.name) {
ctl_if->key = malloc(sizeof(knot_key_t));
if (ctl_if->key) {
@@ -723,7 +731,22 @@ int main(int argc, char **argv)
}
}
}
- conf()->ctl.iface = ctl_if;
+
+ /* Override from command line. */
+ if (r_addr) {
+ free(ctl_if->address);
+ ctl_if->address = strdup(r_addr);
+ ctl_if->family = AF_INET;
+ if (strchr(r_addr, ':')) { /* Dumb way to check for v6 addr. */
+ ctl_if->family = AF_INET6;
+ }
+ }
+ if (r_port > -1) ctl_if->port = r_port;
+ if (r_key.name != NULL) {
+ tsig_key_cleanup(ctl_if->key);
+ ctl_if->key = &r_key;
+ }
+
/* Verbose mode. */
if (has_flag(flags, F_VERBOSE)) {
@@ -737,6 +760,8 @@ int main(int argc, char **argv)
/* Finish */
tsig_key_cleanup(&r_key); /* Not cleaned by config deinit. */
log_close();
+ free(config_fn);
+ free(default_config);
return rc;
}
@@ -749,6 +774,10 @@ static int cmd_start(int argc, char *argv[], unsigned flags, int jobs)
return 1;
}
+ /* Alter privileges. */
+ log_update_privileges(conf()->uid, conf()->gid);
+ proc_update_privileges(conf()->uid, conf()->gid);
+
/* Fetch PID. */
char *pidfile = pid_filename();
pid_t pid = pid_read(pidfile);
@@ -847,6 +876,10 @@ static int cmd_stop(int argc, char *argv[], unsigned flags, int jobs)
return 1;
}
+ /* Alter privileges. */
+ log_update_privileges(conf()->uid, conf()->gid);
+ proc_update_privileges(conf()->uid, conf()->gid);
+
/* Fetch PID. */
char *pidfile = pid_filename();
pid_t pid = pid_read(pidfile);
@@ -930,6 +963,11 @@ static int cmd_status(int argc, char *argv[], unsigned flags, int jobs)
return cmd_remote("status", KNOT_RRTYPE_TXT, 0, NULL);
}
+static int cmd_zonestatus(int argc, char *argv[], unsigned flags, int jobs)
+{
+ return cmd_remote("zonestatus", KNOT_RRTYPE_TXT, 0, NULL);
+}
+
static int cmd_checkconf(int argc, char *argv[], unsigned flags, int jobs)
{
/* Check config. */
diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c
index ac1e527..ac8bc25 100644
--- a/src/knot/ctl/remote.c
+++ b/src/knot/ctl/remote.c
@@ -32,19 +32,22 @@
#define KNOT_CTL_REALM "knot."
#define KNOT_CTL_REALM_EXT ("." KNOT_CTL_REALM)
#define KNOT_CTL_REALM_LEN 5
+#define CMDARGS_BUFLEN (1024*1024) /* 1M */
/*! \brief Remote command structure. */
typedef struct remote_cmdargs_t {
const knot_rrset_t **arg;
unsigned argc;
knot_rcode_t rc;
+ char resp[CMDARGS_BUFLEN];
+ size_t rlen;
} remote_cmdargs_t;
/*! \brief Callback prototype for remote commands. */
typedef int (*remote_cmdf_t)(server_t*, remote_cmdargs_t*);
/*! \brief Callback prototype for per-zone operations. */
-typedef int (remote_zonef_t)(server_t*, knot_zone_t *);
+typedef int (remote_zonef_t)(server_t*, const knot_zone_t *);
/*! \brief Remote command table item. */
typedef struct remote_cmd_t {
@@ -56,6 +59,7 @@ typedef struct remote_cmd_t {
static int remote_c_reload(server_t *s, remote_cmdargs_t* a);
static int remote_c_refresh(server_t *s, remote_cmdargs_t* a);
static int remote_c_status(server_t *s, remote_cmdargs_t* a);
+static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a);
static int remote_c_flush(server_t *s, remote_cmdargs_t* a);
/*! \brief Table of remote commands. */
@@ -63,6 +67,7 @@ struct remote_cmd_t remote_cmd_tbl[] = {
{ "reload", &remote_c_reload },
{ "refresh", &remote_c_refresh },
{ "status", &remote_c_status },
+ { "zonestatus",&remote_c_zonestatus },
{ "flush", &remote_c_flush },
{ NULL, NULL }
};
@@ -112,7 +117,7 @@ static int remote_rdata_apply(server_t *s, remote_cmdargs_t* a, remote_zonef_t *
}
/*! \brief Zone refresh callback. */
-static int remote_zone_refresh(server_t *s, knot_zone_t *z)
+static int remote_zone_refresh(server_t *s, const knot_zone_t *z)
{
if (!s || !z) {
return KNOT_EINVAL;
@@ -136,7 +141,7 @@ static int remote_zone_refresh(server_t *s, knot_zone_t *z)
}
/*! \brief Zone flush callback. */
-static int remote_zone_flush(server_t *s, knot_zone_t *z)
+static int remote_zone_flush(server_t *s, const knot_zone_t *z)
{
if (!s || !z) {
return KNOT_EINVAL;
@@ -160,7 +165,7 @@ static int remote_zone_flush(server_t *s, knot_zone_t *z)
}
/*! \brief Helper to build RDATA from RDATA item. */
-knot_rdata_t* remote_build_rdata(knot_rdata_item_t *i)
+knot_rdata_t* remote_build_rdata(knot_rdata_item_t *i, unsigned c)
{
/* Create RDATA. */
knot_rdata_t *rd = knot_rdata_new();
@@ -169,7 +174,7 @@ knot_rdata_t* remote_build_rdata(knot_rdata_item_t *i)
}
/* Set RDATA items. */
- int ret = knot_rdata_set_items(rd, i, 1);
+ int ret = knot_rdata_set_items(rd, i, c);
if (ret != KNOT_EOK) {
knot_rdata_free(&rd);
return NULL;
@@ -192,7 +197,7 @@ static int remote_c_reload(server_t *s, remote_cmdargs_t* a)
/*!
* \brief Remote command 'status' handler.
*
- * QNAME: refresh
+ * QNAME: status
* DATA: NONE
*/
static int remote_c_status(server_t *s, remote_cmdargs_t* a)
@@ -203,6 +208,108 @@ static int remote_c_status(server_t *s, remote_cmdargs_t* a)
}
/*!
+ * \brief Remote command 'zonestatus' handler.
+ *
+ * QNAME: zonestatus
+ * DATA: NONE
+ */
+static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a)
+{
+ dbg_server("remote: %s\n", __func__);
+ char *dst = a->resp;
+ size_t rb = sizeof(a->resp) - 1;
+
+ int ret = KNOT_EOK;
+ rcu_read_lock();
+ knot_nameserver_t *ns = s->nameserver;
+ const knot_zone_t **zones = knot_zonedb_zones(ns->zone_db);
+ for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) {
+ zonedata_t *zd = (zonedata_t *)zones[i]->data;
+
+ /* Fetch latest serial. */
+ const knot_rrset_t *soa_rrs = 0;
+ const knot_rdata_t *soa_rr = 0;
+ uint32_t serial = 0;
+ knot_zone_contents_t *contents = knot_zone_get_contents(zones[i]);
+ if (contents) {
+ soa_rrs = knot_node_rrset(knot_zone_contents_apex(contents),
+ KNOT_RRTYPE_SOA);
+ assert(soa_rrs != NULL);
+
+ soa_rr = knot_rrset_rdata(soa_rrs);
+
+ int64_t serial_ret = knot_rdata_soa_serial(soa_rr);
+ if (serial_ret > 0) {
+ serial = (uint32_t)serial_ret;
+ }
+ }
+
+ /* Evalute zone type. */
+ const char *state = NULL;
+ if (serial == 0) {
+ state = "bootstrap";
+ } else if (zd->xfr_in.has_master) {
+ state = "xfer";
+ }
+
+ /* Evaluate zone state. */
+ char *when = NULL;
+ int locked = pthread_mutex_trylock(&zd->xfr_in.lock);
+ if (locked == 0) pthread_mutex_unlock(&zd->xfr_in.lock);
+ if (locked != 0) {
+ when = strdup("pending");
+ } else if (zd->xfr_in.scheduled) {
+ when = strdup("scheduled");
+ } else if (zd->xfr_in.timer) {
+ struct timeval now, dif;
+ gettimeofday(&now, 0);
+ timersub(&zd->xfr_in.timer->tv, &now, &dif);
+ when = malloc(64);
+ if (when == NULL) {
+ ret = KNOT_ENOMEM;
+ break;
+ }
+ if (snprintf(when, 64, "in %luh%lum%lus",
+ dif.tv_sec/3600,
+ (dif.tv_sec % 3600)/60,
+ dif.tv_sec % 60) < 0) {
+ free(when);
+ ret = KNOT_ESPACE;
+ break;
+ }
+ } else {
+ when = strdup("idle");
+ }
+
+ /* Workaround, some platforms ignore 'size' with snprintf() */
+ char buf[256];
+ int n = snprintf(buf, sizeof(buf), "%s\ttype=%s | serial=%u | %s %s\n",
+ zd->conf->name,
+ zd->xfr_in.has_master ? "slave" : "master",
+ serial,
+ state ? state : "",
+ when ? when : "");
+ free(when);
+ if (n > rb) {
+ *dst = '\0';
+ ret = KNOT_ESPACE;
+ break;
+ }
+
+ memcpy(dst, buf, n);
+ rb -= n;
+ dst += n;
+
+
+ }
+ rcu_read_unlock();
+ free(zones);
+
+ a->rlen = sizeof(a->resp) - 1 - rb;
+ return ret;
+}
+
+/*!
* \brief Remote command 'refresh' handler.
*
* QNAME: refresh
@@ -234,8 +341,17 @@ static int remote_c_flush(server_t *s, remote_cmdargs_t* a)
/* Flush all. */
dbg_server("remote: %s\n", __func__);
if (a->argc == 0) {
+ int ret = 0;
dbg_server_verb("remote: flushing all zones\n");
- return KNOT_ENOTSUP;
+ rcu_read_lock();
+ knot_nameserver_t *ns = s->nameserver;
+ const knot_zone_t **zones = knot_zonedb_zones(ns->zone_db);
+ for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) {
+ ret = remote_zone_flush(s, zones[i]);
+ }
+ rcu_read_unlock();
+ free(zones);
+ return ret;
}
/* Flush specific zones. */
@@ -334,10 +450,62 @@ int remote_parse(knot_packet_t* pkt, const uint8_t* buf, size_t buflen)
return ret;
}
-int remote_answer(server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t *rlen)
+static int tmp_send(int c, knot_packet_t *pkt, const char* d, uint16_t dlen, uint8_t* rwire, size_t rlen)
{
- if (!s || !pkt || !rwire) {
- *rlen = 0;
+ int ret = KNOT_ERROR;
+ knot_packet_t *resp = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ if (!resp) {
+ return ret;
+ }
+ uint8_t *wire = NULL;
+ size_t len = 0;
+ ret = knot_packet_set_max_size(resp, SOCKET_MTU_SZ);
+ if (ret != KNOT_EOK) {
+ knot_packet_free(&resp);
+ return ret;
+ }
+ ret = knot_response_init_from_query(resp, pkt, 1);
+ if (ret != KNOT_EOK) {
+ knot_packet_free(&resp);
+ return ret;
+ }
+ ret = knot_packet_to_wire(resp, &wire, &len);
+ if (ret != KNOT_EOK) {
+ knot_packet_free(&resp);
+ return ret;
+ }
+ if (len > 0) {
+ memcpy(rwire, wire, len);
+ rlen -= len;
+ }
+ knot_packet_free(&resp);
+ if (len == 0) {
+ return KNOT_ERROR;
+ }
+
+ /* Evaluate output. */
+ int rr_count = 0;
+ knot_rrset_t *rr = remote_build_rr("result.", KNOT_RRTYPE_TXT);
+ knot_rdata_t *rd = remote_create_txt(d, dlen);
+ knot_rrset_add_rdata(rr, rd);
+
+ size_t rrlen = rlen;
+ knot_rrset_to_wire(rr, rwire + len, &rrlen, &rr_count);
+ knot_wire_set_nscount(rwire, rr_count);
+ len += rrlen;
+ rlen -= rrlen;
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+
+ if (len > 0) {
+ return tcp_send(c, rwire, len);
+ }
+
+ return len;
+}
+
+int remote_answer(int fd, server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t rlen)
+{
+ if (fd < 0 || !s || !pkt || !rwire) {
return KNOT_EINVAL;
}
@@ -348,7 +516,6 @@ int remote_answer(server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t *rlen)
const knot_dname_t *qname = knot_packet_qname(pkt);
if (knot_packet_qclass(pkt) != KNOT_CLASS_CH) {
dbg_server("remote: qclass != CH\n");
- *rlen = 0;
return KNOT_EMALF;
}
@@ -357,7 +524,6 @@ int remote_answer(server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t *rlen)
if (!knot_dname_is_subdomain(qname, realm) != 0) {
dbg_server("remote: qname != *%s\n", KNOT_CTL_REALM_EXT);
knot_dname_free(&realm);
- *rlen = 0;
return KNOT_EMALF;
}
knot_dname_free(&realm);
@@ -373,64 +539,43 @@ int remote_answer(server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t *rlen)
* AR: data
*/
int ret = KNOT_EOK;
+ remote_cmdargs_t* args = malloc(sizeof(remote_cmdargs_t));
+ if (!args) {
+ free(cmd);
+ return KNOT_ENOMEM;
+ }
+ memset(args, 0, sizeof(remote_cmdargs_t));
+ args->arg = pkt->authority;
+ args->argc = knot_packet_authority_rrset_count(pkt);
+ args->rc = KNOT_RCODE_NOERROR;
+
remote_cmd_t *c = remote_cmd_tbl;
- remote_cmdargs_t args;
- args.arg = pkt->authority;
- args.argc = knot_packet_authority_rrset_count(pkt);
- args.rc = KNOT_RCODE_NOERROR;
while(c->name != NULL) {
if (strcmp(cmd, c->name) == 0) {
- ret = c->f(s, &args);
+ ret = c->f(s, args);
break;
}
++c;
}
/* Prepare response. */
- size_t remaining = *rlen;
- knot_packet_t *resp = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
- if (!resp) {
- free(cmd);
- return ret;
- }
- uint8_t *wire = NULL;
- size_t len = 0;
- ret = knot_packet_set_max_size(resp, SOCKET_MTU_SZ);
- if (ret != KNOT_EOK) {
- free(cmd);
- knot_packet_free(&resp);
- return ret;
+ if (ret != KNOT_EOK || args->rlen == 0) {
+ args->rlen = strlen(knot_strerror(ret));
+ strncpy(args->resp, knot_strerror(ret), args->rlen);
}
- ret = knot_response_init_from_query(resp, pkt, 1);
- if (ret != KNOT_EOK) {
- free(cmd);
- knot_packet_free(&resp);
- return ret;
+
+ unsigned p = 0;
+ size_t chunk = 16384;
+ for (; p + chunk < args->rlen; p += chunk) {
+ tmp_send(fd, pkt, args->resp + p, chunk, rwire, rlen);
}
- ret = knot_packet_to_wire(resp, &wire, &len);
- if (ret != KNOT_EOK) {
- free(cmd);
- knot_packet_free(&resp);
- return ret;
- }
- if (len > 0) {
- memcpy(rwire, wire, len);
- *rlen = len;
+ unsigned r = args->rlen - p;
+ if (r > 0) {
+ tmp_send(fd, pkt, args->resp + p, r, rwire, rlen);
}
- knot_packet_free(&resp);
- /* Evaluate output. */
- int rr_count = 0;
- knot_rrset_t *rr = remote_build_rr("result.", KNOT_RRTYPE_TXT);
- knot_rdata_t *rd = remote_create_txt(knot_strerror(ret));
- knot_rrset_add_rdata(rr, rd);
-
- remaining -= *rlen;
- knot_rrset_to_wire(rr, rwire + *rlen, &remaining, &rr_count);
- knot_wire_set_nscount(rwire, 1);
- *rlen += remaining;
- knot_rrset_deep_free(&rr, 1, 1, 1);
+ free(args);
free(cmd);
return ret;
}
@@ -504,11 +649,7 @@ int remote_process(server_t *s, int r, uint8_t* buf, size_t buflen)
}
/* Answer packet. */
- wire_len = buflen;
- remote_answer(s, pkt, buf, &wire_len);
- if (wire_len > 0) {
- tcp_send(c, buf, wire_len);
- }
+ remote_answer(c, s, pkt, buf, buflen);
}
knot_packet_free(&pkt);
@@ -623,29 +764,44 @@ knot_rrset_t* remote_build_rr(const char *k, uint16_t t)
return rr;
}
-knot_rdata_t* remote_create_txt(const char *v)
+knot_rdata_t* remote_create_txt(const char *v, size_t v_len)
{
if (!v) {
return NULL;
}
+
+ /* Number of chunks. */
+ const size_t K = 255;
+ unsigned chunks = v_len / K + 1;
/* Create raw_data item. */
- size_t v_len = strlen(v);
- knot_rdata_item_t i;
- i.raw_data = malloc(v_len + 3);
- if (!i.raw_data) {
+ knot_rdata_item_t rditem;
+ rditem.raw_data = malloc(sizeof(uint16_t) + chunks + v_len);
+ if (!rditem.raw_data) {
return NULL;
}
- *i.raw_data = v_len + 1;
+ rditem.raw_data[0] = v_len + chunks;
+ uint8_t *raw = (uint8_t*)(rditem.raw_data + 1);
/* Write TXT item. */
- uint8_t *raw_item = (uint8_t*)(i.raw_data + 1);
- *(raw_item++) = v_len;
- memcpy(raw_item, v, v_len);
-
- knot_rdata_t *rd = remote_build_rdata(&i);
+ unsigned p = 0;
+ if (v_len > K) {
+ for (; p + K < v_len; p += K) {
+ *(raw++) = (uint8_t)K;
+ memcpy(raw, v+p, K);
+ raw += K;
+ }
+ }
+ unsigned r = v_len - p;
+ if (r > 0) {
+ *(raw++) = (uint8_t)r;
+ memcpy(raw, v+p, r);
+ raw += K;
+ }
+
+ knot_rdata_t *rd = remote_build_rdata(&rditem, 1);
if (!rd) {
- free(i.raw_data);
+ free(rditem.raw_data);
}
return rd;
@@ -663,7 +819,7 @@ knot_rdata_t* remote_create_cname(const char *d)
i.dname = dn;
/* Build RDATA. */
- knot_rdata_t *rd = remote_build_rdata(&i);
+ knot_rdata_t *rd = remote_build_rdata(&i, 1);
if (!rd) {
knot_dname_release(dn);
}
@@ -671,20 +827,30 @@ knot_rdata_t* remote_create_cname(const char *d)
return rd;
}
-char* remote_parse_txt(const knot_rdata_t *rd)
+int remote_print_txt(const knot_rdata_t *rd)
{
if (!rd || knot_rdata_count(rd) < 1) {
- return NULL;
+ return KNOT_EINVAL;
}
const knot_rdata_item_t *ri = knot_rdata_item(rd, 0);
if (!ri) {
- return NULL;
+ return KNOT_EINVAL;
}
/* Packet parser should have already checked the packet validity. */
- uint8_t item_len = ri->raw_data[1] & 0x00ff;
- return strndup(((const char*)ri->raw_data) + 3, item_len);
+ char buf[256];
+ uint16_t parsed = 0;
+ uint16_t rlen = ri->raw_data[0];
+ uint8_t *p = (uint8_t*)(ri->raw_data + 1);
+ while (parsed < rlen) {
+ memcpy(buf, (const char*)(p+1), *p);
+ buf[*p] = '\0';
+ printf("%s", buf);
+ parsed += *p + 1;
+ p += *p + 1;
+ }
+ return KNOT_EOK;
}
knot_dname_t* remote_dname_fqdn(const char *k)
diff --git a/src/knot/ctl/remote.h b/src/knot/ctl/remote.h
index eb3eb81..55c6d5a 100644
--- a/src/knot/ctl/remote.h
+++ b/src/knot/ctl/remote.h
@@ -94,6 +94,7 @@ int remote_parse(knot_packet_t* pkt, const uint8_t* buf, size_t buflen);
/*!
* \brief Execute command and prepare answer for client.
*
+ * \param fd Remote client
* \param s Server instance.
* \param pkt Parsed RC command.
* \param rwire Buffer for response.
@@ -102,7 +103,7 @@ int remote_parse(knot_packet_t* pkt, const uint8_t* buf, size_t buflen);
* \retval KNOT_EOK on success.
* \retval knot_error else.
*/
-int remote_answer(server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t *rlen);
+int remote_answer(int fd, server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t rlen);
/*!
* \brief Accept new client, receive command, process it and send response.
@@ -174,9 +175,10 @@ knot_rrset_t* remote_build_rr(const char *k, uint16_t t);
/*!
* \brief Create a TXT rdata.
* \param v Text as a string.
+ * \param v_len Text length.
* \return Created rdata or NULL.
*/
-knot_rdata_t* remote_create_txt(const char *v);
+knot_rdata_t* remote_create_txt(const char *v, size_t v_len);
/*!
* \brief Create a CNAME rdata.
@@ -186,11 +188,11 @@ knot_rdata_t* remote_create_txt(const char *v);
knot_rdata_t* remote_create_cname(const char *d);
/*!
- * \brief Parse TXT rdata to string.
+ * \brief Print TXT rdata to stdout.
* \param rd TXT rdata.
- * \return Parsed string or NULL.
+ * \return KNOT_EOK
*/
-char* remote_parse_txt(const knot_rdata_t *rd);
+int remote_print_txt(const knot_rdata_t *rd);
/*!
* \brief Create dname from str and make sure the name is FQDN.
diff --git a/src/knot/main.c b/src/knot/main.c
index 4359194..c433007 100644
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -88,7 +88,7 @@ int main(int argc, char **argv)
int c = 0, li = 0;
int verbose = 0;
int daemonize = 0;
- char* config_fn = 0;
+ char* config_fn = NULL;
/* Long options. */
struct option opts[] = {
@@ -118,6 +118,7 @@ int main(int argc, char **argv)
case 'h':
case '?':
default:
+ free(config_fn);
help(argc, argv);
return 1;
}
@@ -126,6 +127,7 @@ int main(int argc, char **argv)
// Now check if we want to daemonize
if (daemonize) {
if (daemon(1, 0) != 0) {
+ free(config_fn);
fprintf(stderr, "Daemonization failed, "
"shutting down...\n");
return 1;
@@ -157,15 +159,6 @@ int main(int argc, char **argv)
// Initialize pseudorandom number generator
srand(time(0));
- // Create server
- server_t *server = server_create();
-
- // Initialize configuration
- rcu_read_lock();
- conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0);
- conf_add_hook(conf(), CONF_ALL, server_conf_hook, server);
- rcu_read_unlock();
-
// Find implicit configuration file
if (!config_fn) {
config_fn = conf_find_default();
@@ -179,6 +172,7 @@ int main(int argc, char **argv)
if (rpath == NULL) {
log_server_error("Couldn't get absolute path for configuration file '%s' - "
"%s.\n", config_fn, strerror(errno));
+ free(config_fn);
return 1;
} else {
free(config_fn);
@@ -186,6 +180,15 @@ int main(int argc, char **argv)
}
}
+ // Create server
+ server_t *server = server_create();
+
+ // Initialize configuration
+ rcu_read_lock();
+ conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0);
+ conf_add_hook(conf(), CONF_ALL, server_conf_hook, server);
+ rcu_read_unlock();
+
/* POSIX 1003.1e capabilities. */
#ifdef HAVE_CAP_NG_H
@@ -245,6 +248,7 @@ int main(int argc, char **argv)
log_server_info("\n");
/* Alter privileges. */
+ log_update_privileges(conf()->uid, conf()->gid);
proc_update_privileges(conf()->uid, conf()->gid);
/* Load zones and add hook. */
diff --git a/src/knot/server/rrl.c b/src/knot/server/rrl.c
index 70101c9..84b8c90 100644
--- a/src/knot/server/rrl.c
+++ b/src/knot/server/rrl.c
@@ -28,6 +28,8 @@
#include "common/prng.h"
#include "common/errors.h"
+/* Hopscotch defines. */
+#define HOP_LEN (sizeof(unsigned)*8)
/* Limits */
#define RRL_CLSBLK_MAXLEN (4 + 8 + 1 + 256)
/* CIDR block prefix lengths for v4/v6 */
@@ -41,21 +43,6 @@
/* Enable RRL logging. */
#define RRL_ENABLE_LOG
-/* RRL granular locking. */
-static int rrl_lock_mx(rrl_table_t *t, int lk_id)
-{
- assert(lk_id > -1);
- dbg_rrl_verb("%s: locking id '%d'\n", __func__, lk_id);
- return pthread_mutex_lock(&t->lk[lk_id].mx);
-}
-
-static int rrl_unlock_mx(rrl_table_t *t, int lk_id)
-{
- assert(lk_id > -1);
- dbg_rrl_verb("%s: unlocking id '%d'\n", __func__, lk_id);
- return pthread_mutex_unlock(&t->lk[lk_id].mx);
-}
-
/* Classification */
enum {
CLS_NULL = 0 << 0, /* Empty bucket. */
@@ -199,8 +186,11 @@ static int rrl_classify(char *dst, size_t maxlen, const sockaddr_t *a,
blklen += sizeof(nb);
/* Name */
+ uint16_t *nlen = (uint16_t*)(dst + blklen);
+ blklen += sizeof(uint16_t);
int len = rrl_clsname(dst + blklen, maxlen - blklen, cls, p, z);
if (len < 0) return len;
+ *nlen = len;
blklen += len;
/* Seed. */
@@ -212,51 +202,81 @@ static int rrl_classify(char *dst, size_t maxlen, const sockaddr_t *a,
return blklen;
}
-static rrl_item_t* rrl_hash(rrl_table_t *t, const sockaddr_t *a, rrl_req_t *p,
- const knot_zone_t *zone, uint32_t stamp, int *lk)
+static int bucket_free(rrl_item_t *b, uint32_t now) {
+ return b->cls == CLS_NULL || (b->time + 1 < now);
+}
+
+static int bucket_match(rrl_item_t *b, rrl_item_t *m)
{
- char buf[RRL_CLSBLK_MAXLEN];
- int len = rrl_classify(buf, sizeof(buf), a, p, zone, t->seed);
- if (len < 0) {
- return NULL;
+ return b->cls == m->cls &&
+ b->netblk == m->netblk &&
+ b->qname == m->qname;
+}
+
+static int find_free(rrl_table_t *t, unsigned i, uint32_t now)
+{
+ rrl_item_t *np = t->arr + t->size;
+ rrl_item_t *b = NULL;
+ for (b = t->arr + i; b != np; ++b) {
+ if (bucket_free(b, now)) {
+ return b - (t->arr + i);
+ }
}
-
- uint32_t id = hash(buf, len) % t->size;
-
- /* Check locking. */
- *lk = -1;
- if (t->lk_count > 0) {
- *lk = id % t->lk_count;
- rrl_lock_mx(t, *lk);
+ np = t->arr + i;
+ for (b = t->arr; b != np; ++b) {
+ if (bucket_free(b, now)) {
+ return (b - t->arr) + (t->size - i);
+ }
}
- rrl_item_t *b = t->arr + id;
- dbg_rrl("%s: classified pkt as '0x%x' bucket=%p\n", __func__, id, b);
+ /* this happens if table is full... force vacate current elm */
+ dbg_rrl("%s: out of free buckets, freeing bucket %u\n", __func__, i);
+ return i;
+}
- /* Inspect bucket state. */
- uint64_t nprefix = *((uint64_t*)(buf + sizeof(uint8_t)));
- if (b->cls == CLS_NULL) {
- b->cls = *buf; /* Stored as a first byte in clsblock. */
- b->flags = RRL_BF_NULL;
- b->ntok = t->rate;
- b->time = stamp;
- b->pref = nprefix; /* Invalidate */
+static inline unsigned find_match(rrl_table_t *t, uint32_t id, rrl_item_t *m)
+{
+ unsigned f = 0;
+ unsigned d = 0;
+ unsigned match = t->arr[id].hop;
+ while (match != 0) {
+ d = __builtin_ctz(match);
+ f = (id + d) % t->size;
+ if (bucket_match(t->arr + f, m)) {
+ return d;
+ } else {
+ match &= ~(1<<d); /* clear potential match */
+ }
}
- /* Check for collisions. */
- if (b->pref != nprefix) {
- dbg_rrl("%s: collision in bucket '0x%4x'\n", __func__, id);
- if (!(b->flags & RRL_BF_SSTART)) {
- b->pref = nprefix;
- b->cls = *buf;
- b->flags = RRL_BF_NULL; /* Reset flags. */
- b->time = stamp; /* Reset time */
- b->ntok = t->rate / RRL_SSTART;
- b->flags |= RRL_BF_SSTART;
- dbg_rrl("%s: bucket '0x%4x' slow-start\n", __func__, id);
+
+ return HOP_LEN + 1;
+}
+
+static inline unsigned reduce_dist(rrl_table_t *t, unsigned id, unsigned d, unsigned *f)
+{
+ unsigned rd = HOP_LEN - 1;
+ while (rd > 0) {
+ unsigned s = (t->size + *f - rd) % t->size; /* bucket to be vacated */
+ unsigned o = __builtin_ctz(t->arr[s].hop); /* offset of first valid bucket */
+ if (t->arr[s].hop != 0 && o < rd) { /* only offsets in <s, f> are interesting */
+ unsigned e = (s + o) % t->size; /* this item will be displaced to [f] */
+ unsigned keep_hop = t->arr[*f].hop; /* unpredictable padding */
+ memcpy(t->arr + *f, t->arr + e, sizeof(rrl_item_t));
+ t->arr[*f].hop = keep_hop;
+ t->arr[e].cls = CLS_NULL;
+ t->arr[s].hop &= ~(1<<o);
+ t->arr[s].hop |= 1<<rd;
+ *f = e;
+ return d - (rd - o);
}
+ --rd;
}
- return b;
+ assert(rd == 0); /* this happens with p=1/fact(HOP_LEN) */
+ *f = id;
+ d = 0; /* force vacate initial element */
+ dbg_rrl("%s: no potential relocation, freeing bucket %u\n", __func__, id);
+ return d;
}
static void rrl_log_state(const sockaddr_t *a, uint16_t flags, uint8_t cls)
@@ -277,14 +297,16 @@ static void rrl_log_state(const sockaddr_t *a, uint16_t flags, uint8_t cls)
rrl_table_t *rrl_create(size_t size)
{
+ if (size == 0) {
+ return NULL;
+ }
+
const size_t tbl_len = sizeof(rrl_table_t) + size * sizeof(rrl_item_t);
rrl_table_t *t = malloc(tbl_len);
if (!t) return NULL;
-
- memset(t, 0, tbl_len);
- t->rate = 0;
- t->seed = (uint32_t)(tls_rand() * (double)UINT32_MAX);
+ memset(t, 0, sizeof(rrl_table_t));
t->size = size;
+ rrl_reseed(t);
dbg_rrl("%s: created table size '%zu'\n", __func__, t->size);
return t;
}
@@ -303,25 +325,31 @@ uint32_t rrl_rate(rrl_table_t *rrl)
return rrl->rate;
}
-int rrl_setlocks(rrl_table_t *rrl, size_t granularity)
+int rrl_setlocks(rrl_table_t *rrl, unsigned granularity)
{
if (!rrl) return KNOT_EINVAL;
assert(!rrl->lk); /* Cannot change while locks are used. */
+ assert(granularity <= rrl->size / 10); /* Due to int. division err. */
+
+ if (pthread_mutex_init(&rrl->ll, NULL) < 0) {
+ return KNOT_ENOMEM;
+ }
/* Alloc new locks. */
- rrl->lk = malloc(granularity * sizeof(rrl_lock_t));
+ rrl->lk = malloc(granularity * sizeof(pthread_mutex_t));
if (!rrl->lk) return KNOT_ENOMEM;
- memset(rrl->lk, 0, granularity * sizeof(rrl_lock_t));
+ memset(rrl->lk, 0, granularity * sizeof(pthread_mutex_t));
/* Initialize. */
for (size_t i = 0; i < granularity; ++i) {
- if (pthread_mutex_init(&rrl->lk[i].mx, NULL) < 0) break;
+ if (pthread_mutex_init(rrl->lk + i, NULL) < 0) break;
++rrl->lk_count;
}
+
/* Incomplete initialization */
if (rrl->lk_count != granularity) {
for (size_t i = 0; i < rrl->lk_count; ++i) {
- pthread_mutex_destroy(&rrl->lk[i].mx);
+ pthread_mutex_destroy(rrl->lk + i);
}
free(rrl->lk);
rrl->lk_count = 0;
@@ -333,6 +361,71 @@ int rrl_setlocks(rrl_table_t *rrl, size_t granularity)
return KNOT_EOK;
}
+rrl_item_t* rrl_hash(rrl_table_t *t, const sockaddr_t *a, rrl_req_t *p,
+ const knot_zone_t *zone, uint32_t stamp, int *lock)
+{
+ char buf[RRL_CLSBLK_MAXLEN];
+ int len = rrl_classify(buf, sizeof(buf), a, p, zone, t->seed);
+ if (len < 0) {
+ return NULL;
+ }
+
+ uint32_t id = hash(buf, len) % t->size;
+
+ /* Lock for lookup. */
+ pthread_mutex_lock(&t->ll);
+
+ /* Find an exact match in <id, id + HOP_LEN). */
+ uint16_t *qname = (uint16_t*)(buf + sizeof(uint8_t) + sizeof(uint64_t));
+ rrl_item_t match = {
+ 0, *((uint64_t*)(buf + 1)), t->rate, /* hop, netblk, ntok */
+ buf[0], RRL_BF_NULL, /* cls, flags */
+ hash((char*)(qname + 1), *qname), stamp /* qname, time*/
+ };
+
+ unsigned d = find_match(t, id, &match);
+ if (d > HOP_LEN) { /* not an exact match, find free element [f] */
+ d = find_free(t, id, stamp);
+ }
+
+ /* Reduce distance to fit <id, id + HOP_LEN) */
+ unsigned f = (id + d) % t->size;
+ while (d >= HOP_LEN) {
+ d = reduce_dist(t, id, d, &f);
+ }
+
+ /* Assign granular lock and unlock lookup. */
+ *lock = f % t->lk_count;
+ rrl_lock(t, *lock);
+ pthread_mutex_unlock(&t->ll);
+
+ /* found free elm 'k' which is in <id, id + HOP_LEN) */
+ t->arr[id].hop |= (1 << d);
+ rrl_item_t* b = t->arr + f;
+ assert(f == (id+d) % t->size);
+ dbg_rrl("%s: classified pkt as %4x '%u+%u' bucket=%p \n", __func__, f, id, d, b);
+
+ /* Inspect bucket state. */
+ unsigned hop = b->hop;
+ if (b->cls == CLS_NULL) {
+ memcpy(b, &match, sizeof(rrl_item_t));
+ b->hop = hop;
+ }
+ /* Check for collisions. */
+ if (!bucket_match(b, &match)) {
+ dbg_rrl("%s: collision in bucket '%4x'\n", __func__, id);
+ if (!(b->flags & RRL_BF_SSTART)) {
+ memcpy(b, &match, sizeof(rrl_item_t));
+ b->hop = hop;
+ b->ntok = t->rate + t->rate / RRL_SSTART;
+ b->flags |= RRL_BF_SSTART;
+ dbg_rrl("%s: bucket '%4x' slow-start\n", __func__, id);
+ }
+ }
+
+ return b;
+}
+
int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req,
const knot_zone_t *zone)
{
@@ -344,8 +437,8 @@ int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req,
uint32_t now = time(NULL);
rrl_item_t *b = rrl_hash(rrl, a, req, zone, now, &lock);
if (!b) {
- assert(lock < 0);
dbg_rrl("%s: failed to compute bucket from packet\n", __func__);
+ if (lock > -1) rrl_unlock(rrl, lock);
return KNOT_ERROR;
}
@@ -369,7 +462,6 @@ int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req,
/* Add new tokens. */
uint32_t dn = rrl->rate * dt;
if (b->flags & RRL_BF_SSTART) { /* Bucket in slow-start. */
- dn /= RRL_SSTART;
b->flags &= ~RRL_BF_SSTART;
dbg_rrl("%s: bucket '0x%x' slow-start finished\n",
__func__, (unsigned)(b - rrl->arr));
@@ -392,12 +484,8 @@ int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req,
} else if (b->ntok == 0) {
ret = KNOT_ELIMIT;
}
-
- /* Unlock bucket. */
- if (lock > -1) {
- rrl_unlock_mx(rrl, lock);
- }
+ if (lock > -1) rrl_unlock(rrl, lock);
return ret;
}
@@ -405,8 +493,9 @@ int rrl_destroy(rrl_table_t *rrl)
{
if (rrl) {
dbg_rrl("%s: freeing table %p\n", __func__, rrl);
+ if (rrl->lk_count > 0) pthread_mutex_destroy(&rrl->ll);
for (size_t i = 0; i < rrl->lk_count; ++i) {
- pthread_mutex_destroy(&rrl->lk[i].mx);
+ pthread_mutex_destroy(rrl->lk + i);
}
free(rrl->lk);
}
@@ -414,3 +503,47 @@ int rrl_destroy(rrl_table_t *rrl)
free(rrl);
return KNOT_EOK;
}
+
+int rrl_reseed(rrl_table_t *rrl)
+{
+ /* Lock entire table. */
+ if (rrl->lk_count > 0) {
+ pthread_mutex_lock(&rrl->ll);
+ for (unsigned i = 0; i < rrl->lk_count; ++i) {
+ rrl_lock(rrl, i);
+ }
+ }
+
+ memset(rrl->arr, 0, rrl->size * sizeof(rrl_item_t));
+ rrl->seed = (uint32_t)(tls_rand() * (double)UINT32_MAX);
+ dbg_rrl("%s: reseed to '%u'\n", __func__, rrl->seed);
+
+ if (rrl->lk_count > 0) {
+ for (unsigned i = 0; i < rrl->lk_count; ++i) {
+ rrl_unlock(rrl, i);
+ }
+ pthread_mutex_unlock(&rrl->ll);
+ }
+
+ return KNOT_EOK;
+}
+
+int rrl_lock(rrl_table_t *t, int lk_id)
+{
+ assert(lk_id > -1);
+ dbg_rrl_verb("%s: locking id '%d'\n", __func__, lk_id);
+ if (pthread_mutex_lock(t->lk + lk_id) != 0) {
+ return KNOT_ERROR;
+ }
+ return KNOT_EOK;
+}
+
+int rrl_unlock(rrl_table_t *t, int lk_id)
+{
+ assert(lk_id > -1);
+ dbg_rrl_verb("%s: unlocking id '%d'\n", __func__, lk_id);
+ if (pthread_mutex_unlock(t->lk + lk_id)!= 0) {
+ return KNOT_ERROR;
+ }
+ return KNOT_EOK;
+}
diff --git a/src/knot/server/rrl.h b/src/knot/server/rrl.h
index e0f650d..6ced2f8 100644
--- a/src/knot/server/rrl.h
+++ b/src/knot/server/rrl.h
@@ -34,23 +34,21 @@
#include "libknot/zone/zone.h"
/* Defaults */
-#define RRL_LOCK_GRANULARITY 10 /* Last digit granularity */
+#define RRL_LOCK_GRANULARITY 32 /* Last digit granularity */
/*!
* \brief RRL hash bucket.
*/
typedef struct rrl_item {
- uint64_t pref; /* Prefix associated. */
+ unsigned hop; /* Hop bitmap. */
+ uint64_t netblk; /* Prefix associated. */
uint16_t ntok; /* Tokens available */
uint8_t cls; /* Bucket class */
uint8_t flags; /* Flags */
+ uint32_t qname; /* imputed(QNAME) hash */
uint32_t time; /* Timestamp */
} rrl_item_t;
-typedef struct rrl_lock { /* Wrapper around lock struct. */
- pthread_mutex_t mx;
-} rrl_lock_t;
-
/*!
* \brief RRL hash bucket table.
*
@@ -66,8 +64,9 @@ typedef struct rrl_lock { /* Wrapper around lock struct. */
typedef struct rrl_table {
uint32_t rate; /* Configured RRL limit */
uint32_t seed; /* Pseudorandom seed for hashing. */
- rrl_lock_t *lk; /* Table locks. */
- size_t lk_count; /* Table lock count (granularity). */
+ pthread_mutex_t ll;
+ pthread_mutex_t *lk; /* Table locks. */
+ unsigned lk_count; /* Table lock count (granularity). */
size_t size; /* Number of buckets */
rrl_item_t arr[]; /* Buckets */
} rrl_table_t;
@@ -115,7 +114,20 @@ uint32_t rrl_setrate(rrl_table_t *rrl, uint32_t rate);
* \retval KNOT_EOK
* \retval KNOT_EINVAL
*/
-int rrl_setlocks(rrl_table_t *rrl, size_t granularity);
+int rrl_setlocks(rrl_table_t *rrl, unsigned granularity);
+
+/*!
+ * \brief Get bucket for current combination of parameters.
+ * \param t RRL table.
+ * \param a Source address.
+ * \param p RRL request.
+ * \param zone Relate zone.
+ * \param stamp Timestamp (current time).
+ * \param lock Held lock.
+ * \return assigned bucket
+ */
+rrl_item_t* rrl_hash(rrl_table_t *t, const sockaddr_t *a, rrl_req_t *p,
+ const knot_zone_t *zone, uint32_t stamp, int* lock);
/*!
* \brief Query the RRL table for accept or deny, when the rate limit is reached.
@@ -137,6 +149,30 @@ int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req,
*/
int rrl_destroy(rrl_table_t *rrl);
+/*!
+ * \brief Reseed RRL table secret.
+ * \param rrl RRL table.
+ * \return KNOT_EOK
+ */
+int rrl_reseed(rrl_table_t *rrl);
+
+/*!
+ * \brief Lock specified element lock.
+ * \param rrl RRL table.
+ * \param lk_id Specified lock.
+ * \retval KNOT_EOK
+ * \retval KNOT_ERROR
+ */
+int rrl_lock(rrl_table_t *rrl, int lk_id);
+
+/*!
+ * \brief Unlock specified element lock.
+ * \param rrl RRL table.
+ * \param lk_id Specified lock.
+ * \retval KNOT_EOK
+ * \retval KNOT_ERROR
+ */
+int rrl_unlock(rrl_table_t *rrl, int lk_id);
#endif /* _KNOTD_RRL_H_ */
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
index 08abe11..4ac919d 100644
--- a/src/knot/server/server.c
+++ b/src/knot/server/server.c
@@ -652,7 +652,7 @@ int server_refresh(server_t *server)
if (zd->xfr_in.timer) {
evsched_cancel(sch, zd->xfr_in.timer);
evsched_schedule(sch, zd->xfr_in.timer,
- tls_rand() * 500 + i*200);
+ tls_rand() * 500 + i/2);
/* Cumulative delay. */
}
}
diff --git a/src/knotc.8 b/src/knotc.8
index 039a994..bf4561f 100644
--- a/src/knotc.8
+++ b/src/knotc.8
@@ -1,4 +1,4 @@
-.TH knotc "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc3"
+.TH knotc "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc4"
.SH NAME
.B knotc
\- Knot DNS control utility
@@ -63,6 +63,9 @@ Flush journal and update zone files.
status
Check if server is running.
.TP
+zonestatus
+Show status of configured zones.
+.TP
compile
Compile zone file.
.TP
diff --git a/src/knotd.8 b/src/knotd.8
index 22d0dac..3681343 100644
--- a/src/knotd.8
+++ b/src/knotd.8
@@ -1,4 +1,4 @@
-.TH "knotd" "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc3"
+.TH "knotd" "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc4"
.SH NAME
.B knotd
\- Knot DNS daemon
diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c
index b6381c4..2e6e922 100644
--- a/src/libknot/packet/packet.c
+++ b/src/libknot/packet/packet.c
@@ -307,9 +307,6 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
return KNOT_EMALF;
}
}
- if (*pos != i + 1) {
- dbg_packet("Parsed dname expected len=%zu, parsed=%zu.\n", i+1-bp, *pos-bp);
- }
question->qtype = knot_wire_read_u16(wire + i + 1);
question->qclass = knot_wire_read_u16(wire + i + 3);
*pos += 4;
diff --git a/src/tests/knot/rrl_tests.c b/src/tests/knot/rrl_tests.c
index 35b481e..ed16530 100644
--- a/src/tests/knot/rrl_tests.c
+++ b/src/tests/knot/rrl_tests.c
@@ -18,13 +18,78 @@
#include <sys/socket.h>
#include "tests/knot/rrl_tests.h"
#include "knot/server/rrl.h"
+#include "knot/server/dthreads.h"
#include "knot/common.h"
#include "libknot/packet/response.h"
#include "libknot/packet/query.h"
#include "libknot/nameserver/name-server.h"
+#include "common/prng.h"
/* Enable time-dependent tests. */
//#define ENABLE_TIMED_TESTS
+#define RRL_SIZE 196613
+#define RRL_THREADS 8
+#define RRL_INSERTS (RRL_SIZE/(5*RRL_THREADS)) /* lf = 1/5 */
+#define RRL_LOCKS 64
+
+/* Disabled as default as it depends on random input.
+ * Table may be consistent even if some collision occur (and they may occur).
+ */
+#ifdef ENABLE_TIMED_TESTS
+struct bucketmap_t {
+ unsigned i;
+ uint64_t x;
+};
+
+/*! \brief Unit runnable. */
+struct runnable_data {
+ int passed;
+ rrl_table_t *rrl;
+ sockaddr_t *addr;
+ rrl_req_t *rq;
+ knot_zone_t *zone;
+};
+
+static void* rrl_runnable(void *arg)
+{
+ struct runnable_data* d = (struct runnable_data*)arg;
+ sockaddr_t addr;
+ memcpy(&addr, d->addr, sizeof(sockaddr_t));
+ sockaddr_update(&addr);
+ int lock = -1;
+ uint32_t now = time(NULL);
+ struct bucketmap_t *m = malloc(RRL_INSERTS * sizeof(struct bucketmap_t));
+ for (unsigned i = 0; i < RRL_INSERTS; ++i) {
+ m[i].i = tls_rand() * UINT32_MAX;
+ addr.addr4.sin_addr.s_addr = m[i].i;
+ rrl_item_t *b = rrl_hash(d->rrl, &addr, d->rq, d->zone, now, &lock);
+ rrl_unlock(d->rrl, lock);
+ m[i].x = b->netblk;
+ }
+ for (unsigned i = 0; i < RRL_INSERTS; ++i) {
+ addr.addr4.sin_addr.s_addr = m[i].i;
+ rrl_item_t *b = rrl_hash(d->rrl, &addr, d->rq, d->zone, now, &lock);
+ rrl_unlock(d->rrl, lock);
+ if (b->netblk != m[i].x) {
+ d->passed = 0;
+ }
+ }
+ free(m);
+ return NULL;
+}
+
+static void rrl_hopscotch(struct runnable_data* rd)
+{
+ rd->passed = 1;
+ pthread_t thr[RRL_THREADS];
+ for (unsigned i = 0; i < RRL_THREADS; ++i) {
+ pthread_create(thr + i, NULL, &rrl_runnable, rd);
+ }
+ for (unsigned i = 0; i < RRL_THREADS; ++i) {
+ pthread_join(thr[i], NULL);
+ }
+}
+#endif
static int rrl_tests_count(int argc, char *argv[]);
static int rrl_tests_run(int argc, char *argv[]);
@@ -44,9 +109,9 @@ unit_api rrl_tests_api = {
static int rrl_tests_count(int argc, char *argv[])
{
- int c = 6;
-#ifndef ENABLE_TIMED_TESTS
- c -= 2;
+ int c = 5;
+#ifdef ENABLE_TIMED_TESTS
+ c += 5;
#endif
return c;
}
@@ -60,7 +125,11 @@ static int rrl_tests_run(int argc, char *argv[])
qst.qname = knot_dname_new_from_str("beef.", 5, NULL);
knot_packet_t *query = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
knot_query_init(query);
- knot_packet_set_max_size(query, 512);
+ if (knot_packet_set_max_size(query, 512) < 0) {
+ knot_dname_free(&qst.qname);
+ knot_packet_free(&query);
+ return KNOT_ERROR; /* Fatal */
+ }
knot_query_set_question(query, &qst);
/* Prepare response */
@@ -77,22 +146,26 @@ static int rrl_tests_run(int argc, char *argv[])
rq.flags = 0;
/* 1. create rrl table */
- rrl_table_t *rrl = rrl_create(101);
+ rrl_table_t *rrl = rrl_create(RRL_SIZE);
ok(rrl != NULL, "rrl: create");
/* 2. set rate limit */
uint32_t rate = 10;
rrl_setrate(rrl, rate);
ok(rate == rrl_rate(rrl), "rrl: setrate");
+
+ /* 3. setlocks */
+ int ret = rrl_setlocks(rrl, RRL_LOCKS);
+ ok(ret == KNOT_EOK, "rrl: setlocks");
- /* 3. N unlimited requests. */
+ /* 4. N unlimited requests. */
knot_dname_t *apex = knot_dname_new_from_str("rrl.", 4, NULL);
knot_zone_t *zone = knot_zone_new(knot_node_new(apex, NULL, 0), 0, 0);
sockaddr_t addr;
sockaddr_t addr6;
sockaddr_set(&addr, AF_INET, "1.2.3.4", 0);
sockaddr_set(&addr6, AF_INET6, "1122:3344:5566:7788::aabb", 0);
- int ret = 0;
+ ret = 0;
for (unsigned i = 0; i < rate; ++i) {
if (rrl_query(rrl, &addr, &rq, zone) != KNOT_EOK ||
rrl_query(rrl, &addr6, &rq, zone) != KNOT_EOK) {
@@ -103,16 +176,16 @@ static int rrl_tests_run(int argc, char *argv[])
ok(ret == 0, "rrl: unlimited IPv4/v6 requests");
#ifdef ENABLE_TIMED_TESTS
- /* 4. limited request */
+ /* 5. limited request */
ret = rrl_query(rrl, &addr, &rq, zone);
ok(ret != 0, "rrl: throttled IPv4 request");
- /* 5. limited IPv6 request */
+ /* 6. limited IPv6 request */
ret = rrl_query(rrl, &addr6, &rq, zone);
ok(ret != 0, "rrl: throttled IPv6 request");
#endif
- /* 6. invalid values. */
+ /* 7. invalid values. */
ret = 0;
lives_ok( {
rrl_create(0); // NULL
@@ -123,7 +196,23 @@ static int rrl_tests_run(int argc, char *argv[])
ret += rrl_query(rrl, 0, 0, 0); // -1
ret += rrl_query(rrl, (void*)0x1, 0, 0); // -1
ret += rrl_destroy(0); // -1
- }, "dthreads: not crashed while executing functions on NULL context");
+ }, "rrl: not crashed while executing functions on NULL context");
+
+#ifdef ENABLE_TIMED_TESTS
+ /* 8. hopscotch test */
+ struct runnable_data rd = {
+ 1, rrl, &addr, &rq, zone
+ };
+ rrl_hopscotch(&rd);
+ ok(rd.passed, "rrl: hashtable is ~ consistent");
+
+ /* 9. reseed */
+ ok(rrl_reseed(rrl) == 0, "rrl: reseed");
+
+ /* 10. hopscotch after reseed. */
+ rrl_hopscotch(&rd);
+ ok(rd.passed, "rrl: hashtable is ~ consistent");
+#endif
knot_dname_release(qst.qname);
knot_dname_release(apex);