summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Knot.files6
-rw-r--r--Makefile.in2
-rw-r--r--RELNOTES16
-rw-r--r--aclocal.m46
-rwxr-xr-xconfigure22
-rw-r--r--configure.ac4
-rwxr-xr-xdepcomp11
-rw-r--r--doc/Makefile.in2
-rw-r--r--doc/configuration.texi29
-rw-r--r--doc/introduction.texi2
-rw-r--r--doc/knot.texi6
-rw-r--r--doc/reference.texi44
-rw-r--r--doc/texinfo.tex106
-rw-r--r--samples/Makefile.in2
-rw-r--r--samples/knot.full.conf20
-rw-r--r--src/Makefile.am6
-rw-r--r--src/Makefile.in50
-rw-r--r--src/common/acl.c1
-rw-r--r--src/common/acl.h2
-rw-r--r--src/common/errcode.c1
-rw-r--r--src/common/errcode.h3
-rw-r--r--src/common/fdset.c23
-rw-r--r--src/common/fdset.h9
-rw-r--r--src/common/fdset_kqueue.c4
-rw-r--r--src/common/hattrie/murmurhash3.c77
-rw-r--r--src/common/hattrie/murmurhash3.h11
-rw-r--r--src/common/latency.c1
-rw-r--r--src/common/latency.h1
-rw-r--r--src/common/slab/slab.c225
-rw-r--r--src/common/slab/slab.h78
-rw-r--r--src/config.h.in3
-rw-r--r--src/knot.conf.522
-rw-r--r--src/knot/conf/cf-lex.l3
-rw-r--r--src/knot/conf/cf-parse.y7
-rw-r--r--src/knot/conf/conf.c12
-rw-r--r--src/knot/conf/conf.h5
-rw-r--r--src/knot/ctl/knotc_main.c2
-rw-r--r--src/knot/main.c1
-rw-r--r--src/knot/other/debug.h42
-rw-r--r--src/knot/server/dthreads.c5
-rw-r--r--src/knot/server/rrl.c416
-rw-r--r--src/knot/server/rrl.h143
-rw-r--r--src/knot/server/server.c37
-rw-r--r--src/knot/server/server.h4
-rw-r--r--src/knot/server/socket.c1
-rw-r--r--src/knot/server/socket.h1
-rw-r--r--src/knot/server/tcp-handler.c5
-rw-r--r--src/knot/server/udp-handler.c123
-rw-r--r--src/knot/server/udp-handler.h3
-rw-r--r--src/knot/server/xfr-handler.c3
-rw-r--r--src/knot/server/zones.c2
-rw-r--r--src/knot/stat/stat.c3
-rw-r--r--src/knot/stat/stat.h3
-rw-r--r--src/knot/zone/zone-dump-text.c1
-rw-r--r--src/knotc.82
-rw-r--r--src/knotd.82
-rw-r--r--src/libknot/hash/cuckoo-hash-table.c45
-rw-r--r--src/libknot/nameserver/name-server.c4
-rw-r--r--src/libknot/packet/packet.c3
-rw-r--r--src/libknot/packet/packet.h14
-rw-r--r--src/libknot/packet/response.c3
-rw-r--r--src/libknot/rdata.c3
-rw-r--r--src/libknot/rrset.c3
-rw-r--r--src/libknot/updates/ddns.c3
-rw-r--r--src/libknot/zone/zone-contents.c7
-rw-r--r--src/libknot/zone/zone-tree.c2
-rw-r--r--src/libknot/zone/zone.h6
-rw-r--r--src/tests/common/events_tests.c4
-rw-r--r--src/tests/common/fdset_tests.c5
-rw-r--r--src/tests/common/slab_tests.c31
-rw-r--r--src/tests/knot/dthreads_tests.c7
-rw-r--r--src/tests/knot/rrl_tests.c135
-rw-r--r--src/tests/knot/rrl_tests.h25
-rw-r--r--src/tests/unittests_main.c2
-rw-r--r--src/zcompile/parser-descriptor.c3
-rw-r--r--src/zcompile/parser-util.c3
76 files changed, 1433 insertions, 496 deletions
diff --git a/Knot.files b/Knot.files
index 37dc52a..c968d91 100644
--- a/Knot.files
+++ b/Knot.files
@@ -62,6 +62,8 @@ src/libknot/zone/zone-tree.c
src/libknot/zone/dname-table.h
src/libknot/zone/dname-table.c
src/Makefile.am
+src/common/hattrie/murmurhash3.c
+src/common/hattrie/murmurhash3.h
src/common/slab/slab.c
src/common/slab/slab.h
src/common/slab/alloc-common.h
@@ -160,6 +162,8 @@ src/knot/server/journal.c
src/knot/server/journal.h
src/knot/server/notify.c
src/knot/server/notify.h
+src/knot/server/rrl.c
+src/knot/server/rrl.h
src/knot/ctl/process.c
src/knot/ctl/process.h
src/knot/conf/cf-lex.l
@@ -196,6 +200,8 @@ src/tests/knot/journal_tests.c
src/tests/knot/journal_tests.h
src/tests/knot/server_tests.c
src/tests/knot/server_tests.h
+src/tests/knot/rrl_tests.c
+src/tests/knot/rrl_tests.h
src/tests/libknot/unittests_libknot.c
src/tests/libknot/libknot/dname_tests.c
src/tests/libknot/libknot/dname_tests.h
diff --git a/Makefile.in b/Makefile.in
index 934681a..21d7df3 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.12.4 from Makefile.am.
+# Makefile.in generated by automake 1.12.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
diff --git a/RELNOTES b/RELNOTES
index 449a849..9060ba0 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -1,5 +1,15 @@
-v1.2-rc2 - Feb 15, 2013
--------------------
+v1.2.0-rc3 - Mar 1, 2013
+------------------------
+
+Features:
+ * Response rate limiting (see documentation)
+
+Bugfixes:
+ * Fixed OpenBSD build
+ * Responses to ANY should contain RRSIGs
+
+v1.2.0-rc2 - Feb 15, 2013
+-------------------------
Bugfixes:
* Fixed processing of some non-standard dnames.
@@ -7,7 +17,7 @@ Bugfixes:
* More compliant rcodes in case of DDNS/TSIG failures.
* Correct processing of malformed DDNS prereq section.
-v1.2-rc1 - Jan 4, 2013
+v1.2.0-rc1 - Jan 4, 2013
------------------
Features:
diff --git a/aclocal.m4 b/aclocal.m4
index ef4e358..a220341 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,4 +1,4 @@
-# generated automatically by aclocal 1.12.4 -*- Autoconf -*-
+# generated automatically by aclocal 1.12.6 -*- Autoconf -*-
# Copyright (C) 1996-2012 Free Software Foundation, Inc.
@@ -34,7 +34,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.12'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
-m4_if([$1], [1.12.4], [],
+m4_if([$1], [1.12.6], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
@@ -50,7 +50,7 @@ m4_define([_AM_AUTOCONF_VERSION], [])
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.12.4])dnl
+[AM_AUTOMAKE_VERSION([1.12.6])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
diff --git a/configure b/configure
index 2549253..3dfabbd 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-rc2.
+# Generated by GNU Autoconf 2.69 for knot 1.2.0-rc3.
#
# 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-rc2'
-PACKAGE_STRING='knot 1.2-rc2'
+PACKAGE_VERSION='1.2.0-rc3'
+PACKAGE_STRING='knot 1.2.0-rc3'
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-rc2 to adapt to many kinds of systems.
+\`configure' configures knot 1.2.0-rc3 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-rc2:";;
+ short | recursive ) echo "Configuration of knot 1.2.0-rc3:";;
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-rc2
+knot configure 1.2.0-rc3
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-rc2, which was
+It was created by knot $as_me 1.2.0-rc3, 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-rc2'
+ VERSION='1.2.0-rc3'
cat >>confdefs.h <<_ACEOF
@@ -14068,7 +14068,7 @@ fi
done
-for ac_header in arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in_systm.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/socket.h sys/time.h sys/select.h sys/wait.h sys/stat.h cap-ng.h syslog.h unistd.h urcu.h ev.h pthread_np.h
+for ac_header in arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in_systm.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/socket.h sys/time.h sys/select.h sys/wait.h sys/stat.h cap-ng.h syslog.h unistd.h urcu.h ev.h pthread_np.h signal.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -15342,7 +15342,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-rc2, which was
+This file was extended by knot $as_me 1.2.0-rc3, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -15408,7 +15408,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-rc2
+knot config.status 1.2.0-rc3
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index 7a723e5..f593068 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
# -*- Autoconf -*-
AC_PREREQ([2.60])
-AC_INIT([knot], [1.2-rc2], [knot-dns@labs.nic.cz])
+AC_INIT([knot], [1.2.0-rc3], [knot-dns@labs.nic.cz])
AM_INIT_AUTOMAKE([gnu -Wall -Werror])
AC_CONFIG_SRCDIR([src/knot/main.c])
AC_CONFIG_HEADERS([src/config.h])
@@ -151,7 +151,7 @@ AC_SEARCH_LIBS([adler32], [z])
# Checks for header files.
AC_HEADER_RESOLV
-AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in_systm.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/socket.h sys/time.h sys/select.h sys/wait.h sys/stat.h cap-ng.h syslog.h unistd.h urcu.h ev.h pthread_np.h])
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in_systm.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/socket.h sys/time.h sys/select.h sys/wait.h sys/stat.h cap-ng.h syslog.h unistd.h urcu.h ev.h pthread_np.h signal.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
diff --git a/depcomp b/depcomp
index 0544c68..e1f51f4 100755
--- a/depcomp
+++ b/depcomp
@@ -74,6 +74,9 @@ tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
+# Avoid interferences from the environment.
+gccflag= dashmflag=
+
# Some modes work just like other modes, but use different flags. We
# parameterize here, but still list the modes in the big case below,
# to make depend.m4 easier to write. Note that we *cannot* use a case
@@ -108,7 +111,7 @@ if test "$depmode" = msvc7msys; then
fi
if test "$depmode" = xlc; then
- # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency informations.
+ # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
gccflag=-qmakedep=gcc,-MF
depmode=gcc
fi
@@ -142,13 +145,17 @@ gcc3)
;;
gcc)
+## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
+## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
+## (see the conditional assignment to $gccflag above).
## There are various ways to get dependency output from gcc. Here's
## why we pick this rather obscure method:
## - Don't want to use -MD because we'd like the dependencies to end
## up in a subdir. Having to rename by hand is ugly.
## (We might end up doing this anyway to support other compilers.)
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
-## -MM, not -M (despite what the docs say).
+## -MM, not -M (despite what the docs say). Also, it might not be
+## supported by the other compilers which use the 'gcc' depmode.
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
diff --git a/doc/Makefile.in b/doc/Makefile.in
index d11a23d..779e241 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.12.4 from Makefile.am.
+# Makefile.in generated by automake 1.12.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
diff --git a/doc/configuration.texi b/doc/configuration.texi
index ae5a4a1..b979c38 100644
--- a/doc/configuration.texi
+++ b/doc/configuration.texi
@@ -12,6 +12,7 @@ In this chapter we provide suggested configurations and explain the meaning of i
* Remote control interface::
* Enabling zone semantic checks::
* Creating IXFR differences from zone file changes::
+* Using Response Rate Limiting::
@end menu
@node Minimal configuration
@@ -256,3 +257,31 @@ If Knot is being run as a master server, experimental feature @code{ixfr-from-di
can be enabled to create IXFR differences from changes made to the master zone file.
See @ref{Controlling running daemon} for more information. For more about @code{zones} statement see @ref{zones}.
+@node Using Response Rate Limiting
+@section Using Response Rate Limiting
+
+Response rate limiting (RRL) is a method to combat recent DNS reflection amplification attacks.
+These attacked rely on the fact that source address of a UDP query could be forged,
+and without a worldwide deployment of BCP38, such a forgery could not be detected.
+Attacker could then exploit DNS server responding to every query, potentially flooding the
+victim with a large unsolicited DNS responses.
+
+As of Knot DNS version 1.2.0, RRL is compiled in, but disabled by default.
+You can enable it with the @ref{rate-limit} option in the @ref{system} section.
+Setting to a value greater than @code{0} means that every flow is allowed N responses per second,
+(i.e. @code{rate-limit 50;} means @code{50} responses per second).
+It is also possible to configure SLIP interval, which causes every Nth blocked response to be slipped
+as a truncated response. Not that some error responses cannot be truncated and are slipped as-is.
+For more information, refer to @ref{rate-limit-slip}.
+It is advisable to not set slip interval to a value larger than 2, to allow legitimate clients
+get at least some level of service.
+
+Example configuration:
+@example
+system @{
+ rate-limit 200; # Each flow is allowed to 200 resp. per second
+ rate-limit-slip 2; # Every other response is slipped (default)
+@}
+@end example
+
+
diff --git a/doc/introduction.texi b/doc/introduction.texi
index 4217001..3644636 100644
--- a/doc/introduction.texi
+++ b/doc/introduction.texi
@@ -38,7 +38,6 @@ Knot DNS supports the following DNS features:
@item Unknown RR types
@end itemize
-@*
Server features:
@itemize
@@ -48,7 +47,6 @@ Server features:
@item Semantic checks of zones
@end itemize
-@*
For more info and downloads see
@url{http://www.knot-dns.cz, www.knot-dns.cz}.
diff --git a/doc/knot.texi b/doc/knot.texi
index b0929ae..04e81b5 100644
--- a/doc/knot.texi
+++ b/doc/knot.texi
@@ -171,6 +171,12 @@ Statement Definition and Usage
* pidfile::
* workers::
* user::
+* max-conn-idle::
+* max-conn-hs::
+* max-conn-reply::
+* rate-limit::
+* rate-limit-size::
+* rate-limit-slip::
@code{keys} Statement
diff --git a/doc/reference.texi b/doc/reference.texi
index f28608d..2d9ac7e 100644
--- a/doc/reference.texi
+++ b/doc/reference.texi
@@ -42,6 +42,9 @@ else.
[ @code{max-conn-idle} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ]
[ @code{max-conn-hs} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ]
[ @code{max-conn-reply} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ]
+ [ @code{rate-limit} @kbd{integer}@code{;} ]
+ [ @code{rate-limit-size} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ]
+ [ @code{rate-limit-slip} @kbd{integer}@code{;} ]
@code{@}}
@end example
@@ -56,9 +59,12 @@ else.
* pidfile::
* workers::
* user::
-* max-conn-idle::
-* max-conn-hs::
-* max-conn-reply::
+* max-conn-idle::
+* max-conn-hs::
+* max-conn-reply::
+* rate-limit::
+* rate-limit-size::
+* rate-limit-slip::
@end menu
@node identity
@@ -178,6 +184,38 @@ that already made at least 1 meaningful query.
Maximum time to wait for a reply to an issued SOA query.
+@node rate-limit
+@subsubsection rate-limit
+@vindex rate-limit
+
+Rate limiting is based on a token bucket scheme, rate basically represents number of tokens available each second.
+Each response is processed and classified (based on a several discriminators, f.e. source netblock, qtype, name, rcode, etc.).
+Classified responses are then hashed and assigned to a bucket containing number of available tokens, timestamp and metadata.
+When available tokens are exhausted, response is rejected or enters SLIP (server responds with a truncated response).
+Number of available tokens is recalculated each second.
+
+Default value: @kbd{0 (disabled)}
+
+@node rate-limit-size
+@subsubsection rate-limit-size
+@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.
+Size should be selected as a reasonably large prime due to the better hash function distribution properties.
+
+Default value: @kbd{1572869}
+
+@node rate-limit-slip
+@subsubsection rate-limit-slip
+@vindex rate-limit-slip
+
+As attacks using DNS/UDP are usually based on a forged source address, an attacker could deny services to the victim netblock
+if all responses would be completely blocked. The idea behind SLIP mechanism is to send each Nth response as truncated, thus allowing
+client to reconnect via TCP for at least some degree of service. It is worth noting, that some responses can't be truncated (f.e. SERVFAIL).
+
+Default value: @kbd{2}
+
@node system Example
@subsection system Example
diff --git a/doc/texinfo.tex b/doc/texinfo.tex
index f458ba7..b5f3141 100644
--- a/doc/texinfo.tex
+++ b/doc/texinfo.tex
@@ -3,7 +3,7 @@
% Load plain if necessary, i.e., if running under initex.
\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
%
-\def\texinfoversion{2012-09-04.17}
+\def\texinfoversion{2012-11-08.11}
%
% Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995,
% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
@@ -2272,8 +2272,6 @@ end
\gdef\markupsetcodequoteleft{\let`\codequoteleft}
\gdef\markupsetcodequoteright{\let'\codequoteright}
-
-\gdef\markupsetnoligaturesquoteleft{\let`\noligaturesquoteleft}
}
\let\markupsetuplqcode \markupsetcodequoteleft
@@ -2282,6 +2280,9 @@ end
\let\markupsetuplqexample \markupsetcodequoteleft
\let\markupsetuprqexample \markupsetcodequoteright
%
+\let\markupsetuplqkbd \markupsetcodequoteleft
+\let\markupsetuprqkbd \markupsetcodequoteright
+%
\let\markupsetuplqsamp \markupsetcodequoteleft
\let\markupsetuprqsamp \markupsetcodequoteright
%
@@ -2291,8 +2292,6 @@ end
\let\markupsetuplqverbatim \markupsetcodequoteleft
\let\markupsetuprqverbatim \markupsetcodequoteright
-\let\markupsetuplqkbd \markupsetnoligaturesquoteleft
-
% Allow an option to not use regular directed right quote/apostrophe
% (char 0x27), but instead the undirected quote from cmtt (char 0x0d).
% The undirected quote is ugly, so don't make it the default, but it
@@ -2382,8 +2381,7 @@ end
\aftersmartic
}
-% like \smartslanted except unconditionally uses \ttsl, and no ic.
-% @var is set to this for defun arguments.
+% Unconditional use \ttsl, and no ic. @var is set to this for defuns.
\def\ttslanted#1{{\ttsl #1}}
% @cite is like \smartslanted except unconditionally use \sl. We never want
@@ -2695,10 +2693,6 @@ end
\let\email=\uref
\fi
-% @kbd is like @code, except that if the argument is just one @key command,
-% then @kbd has no effect.
-\def\kbd#1{{\setupmarkupstyle{kbd}\def\look{#1}\expandafter\kbdfoo\look??\par}}
-
% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always),
% `example' (@kbd uses ttsl only inside of @example and friends),
% or `code' (@kbd uses normal tty font always).
@@ -2722,11 +2716,17 @@ end
% Default is `distinct'.
\kbdinputstyle distinct
+% @kbd is like @code, except that if the argument is just one @key command,
+% then @kbd has no effect.
+\def\kbd#1{{\def\look{#1}\expandafter\kbdsub\look??\par}}
+
\def\xkey{\key}
-\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}%
-\ifx\one\xkey\ifx\threex\three \key{#2}%
-\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi
-\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi}
+\def\kbdsub#1#2#3\par{%
+ \def\one{#1}\def\three{#3}\def\threex{??}%
+ \ifx\one\xkey\ifx\threex\three \key{#2}%
+ \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi
+ \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi
+}
% definition of @key that produces a lozenge. Doesn't adjust to text size.
%\setfont\keyrm\rmshape{8}{1000}{OT1}
@@ -3272,7 +3272,8 @@ end
% Settings used for typesetting titles: no hyphenation, no indentation,
% don't worry much about spacing, ragged right. This should be used
% inside a \vbox, and fonts need to be set appropriately first. Because
-% it is always used for titles, nothing else, we call \rmisbold.
+% it is always used for titles, nothing else, we call \rmisbold. \par
+% should be specified before the end of the \vbox, since a vbox is a group.
%
\def\raggedtitlesettings{%
\rmisbold
@@ -3289,7 +3290,7 @@ end
\parseargdef\title{%
\checkenv\titlepage
- \vbox{\titlefonts \raggedtitlesettings #1}
+ \vbox{\titlefonts \raggedtitlesettings #1\par}%
% print a rule at the page bottom also.
\finishedtitlepagefalse
\vskip4pt \hrule height 4pt width \hsize \vskip4pt
@@ -4256,7 +4257,7 @@ end
}
\def\ifcmddefinedfail{\doignore{ifcommanddefined}}
-% @ifcommandnotdefined CMD ... handlded similar to @ifclear above.
+% @ifcommandnotdefined CMD ... handled similar to @ifclear above.
\makecond{ifcommandnotdefined}
\def\ifcommandnotdefined{%
\parsearg{\doifcmddefined{\else \let\next=\ifcmdnotdefinedfail}}}
@@ -5592,14 +5593,6 @@ end
% Define @majorheading, @heading and @subheading
-% NOTE on use of \vbox for chapter headings, section headings, and such:
-% 1) We use \vbox rather than the earlier \line to permit
-% overlong headings to fold.
-% 2) \hyphenpenalty is set to 10000 because hyphenation in a
-% heading is obnoxious; this forbids it.
-% 3) Likewise, headings look best if no \parindent is used, and
-% if justification is not attempted. Hence \raggedright.
-
\def\majorheading{%
{\advance\chapheadingskip by 10pt \chapbreak }%
\parsearg\chapheadingzzz
@@ -5607,10 +5600,8 @@ end
\def\chapheading{\chapbreak \parsearg\chapheadingzzz}
\def\chapheadingzzz#1{%
- {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
- \parindent=0pt\ptexraggedright
- \rmisbold #1\hfill}}%
- \bigskip \par\penalty 200\relax
+ \vbox{\chapfonts \raggedtitlesettings #1\par}%
+ \nobreak\bigskip \nobreak
\suppressfirstparagraphindent
}
@@ -5769,8 +5760,7 @@ end
%
% Typeset the actual heading.
\nobreak % Avoid page breaks at the interline glue.
- \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright
- \hangindent=\wd0 \centerparametersmaybe
+ \vbox{\raggedtitlesettings \hangindent=\wd0 \centerparametersmaybe
\unhbox0 #1\par}%
}%
\nobreak\bigskip % no page break after a chapter title
@@ -5792,18 +5782,18 @@ end
\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
%
\def\unnchfopen #1{%
-\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
- \parindent=0pt\ptexraggedright
- \rmisbold #1\hfill}}\bigskip \par\nobreak
+ \chapoddpage
+ \vbox{\chapfonts \raggedtitlesettings #1\par}%
+ \nobreak\bigskip\nobreak
}
\def\chfopen #1#2{\chapoddpage {\chapfonts
\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
\par\penalty 5000 %
}
\def\centerchfopen #1{%
-\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
- \parindent=0pt
- \hfill {\rmisbold #1}\hfill}}\bigskip \par\nobreak
+ \chapoddpage
+ \vbox{\chapfonts \raggedtitlesettings \hfill #1\hfill}%
+ \nobreak\bigskip \nobreak
}
\def\CHAPFopen{%
\global\let\chapmacro=\chfopen
@@ -6569,16 +6559,9 @@ end
\makedispenvdef{quotation}{\quotationstart}
%
\def\quotationstart{%
- {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
- \parindent=0pt
- %
- % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+ \indentedblockstart % same as \indentedblock, but increase right margin too.
\ifx\nonarrowing\relax
- \advance\leftskip by \lispnarrowing
\advance\rightskip by \lispnarrowing
- \exdentamount = \lispnarrowing
- \else
- \let\nonarrowing = \relax
\fi
\parsearg\quotationlabel
}
@@ -6604,6 +6587,32 @@ end
\fi
}
+% @indentedblock is like @quotation, but indents only on the left and
+% has no optional argument.
+%
+\makedispenvdef{indentedblock}{\indentedblockstart}
+%
+\def\indentedblockstart{%
+ {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
+ \parindent=0pt
+ %
+ % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+ \ifx\nonarrowing\relax
+ \advance\leftskip by \lispnarrowing
+ \exdentamount = \lispnarrowing
+ \else
+ \let\nonarrowing = \relax
+ \fi
+}
+
+% Keep a nonzero parskip for the environment, since we're doing normal filling.
+%
+\def\Eindentedblock{%
+ \par
+ {\parskip=0pt \afterenvbreak}%
+}
+\def\Esmallindentedblock{\Eindentedblock}
+
% LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>}
% If we want to allow any <char> as delimiter,
@@ -7082,7 +7091,10 @@ end
\df \sl \hyphenchar\font=0
%
% On the other hand, if an argument has two dashes (for instance), we
- % want a way to get ttsl. Let's try @var for that.
+ % want a way to get ttsl. We used to recommend @var for that, so
+ % leave the code in, but it's strange for @var to lead to typewriter.
+ % Nowadays we recommend @code, since the difference between a ttsl hyphen
+ % and a tt hyphen is pretty tiny. @code also disables ?` !`.
\def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}%
#1%
\sl\hyphenchar\font=45
diff --git a/samples/Makefile.in b/samples/Makefile.in
index a9402a6..44b12ae 100644
--- a/samples/Makefile.in
+++ b/samples/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.12.4 from Makefile.am.
+# Makefile.in generated by automake 1.12.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
diff --git a/samples/knot.full.conf b/samples/knot.full.conf
index 8b4571e..f6a9d89 100644
--- a/samples/knot.full.conf
+++ b/samples/knot.full.conf
@@ -60,6 +60,26 @@ system {
# f.e. 1s = 1 second, 1m = 1 minute, 1h = 1 hour, 1d = 1 day
# Default: 10s
max-conn-reply 10s;
+
+ # Rate limit
+ # in queries / second
+ # Default: off (=0)
+ rate-limit 0;
+
+ # Rate limit bucket size
+ # 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;
+
+ # Rate limit SLIP
+ # Each Nth blocked response will be sent as truncated, this is a way to allow
+ # legitimate requests to get a chance to reconnect using TCP
+ # Default: 2
+ rate-limit-slip 2;
}
# Section 'keys' contains list of TSIG keys
diff --git a/src/Makefile.am b/src/Makefile.am
index 4458615..963364d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -76,6 +76,8 @@ unittests_SOURCES = \
tests/knot/journal_tests.h \
tests/knot/server_tests.c \
tests/knot/server_tests.h \
+ tests/knot/rrl_tests.c \
+ tests/knot/rrl_tests.h \
tests/unittests_main.c
unittests_libknot_realdata_SOURCES = \
@@ -215,6 +217,8 @@ libknot_la_SOURCES = \
libknot/tsig-op.c
libknots_la_SOURCES = \
+ common/hattrie/murmurhash3.c \
+ common/hattrie/murmurhash3.h \
common/slab/slab.c \
common/slab/slab.h \
common/libtap/tap.c \
@@ -304,6 +308,8 @@ libknotd_la_SOURCES = \
knot/server/notify.h \
knot/server/notify.c \
knot/server/zones.h \
+ knot/server/rrl.c \
+ knot/server/rrl.h \
knot/zone/zone-load.c \
knot/zone/zone-load.h \
knot/zone/semantic-check.c \
diff --git a/src/Makefile.in b/src/Makefile.in
index 7c00c90..4991a28 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.12.4 from Makefile.am.
+# Makefile.in generated by automake 1.12.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
@@ -88,12 +88,12 @@ libknotd_la_DEPENDENCIES = libknot.la libknots.la @LIBOBJS@
am_libknotd_la_OBJECTS = gatherer.lo stat.lo libknotd_la-cf-parse.lo \
libknotd_la-cf-lex.lo conf.lo logconf.lo process.lo remote.lo \
dthreads.lo journal.lo socket.lo server.lo udp-handler.lo \
- tcp-handler.lo xfr-handler.lo zones.lo notify.lo zone-load.lo \
- semantic-check.lo zone-dump.lo zone-dump-text.lo
+ tcp-handler.lo xfr-handler.lo zones.lo notify.lo rrl.lo \
+ zone-load.lo semantic-check.lo zone-dump.lo zone-dump-text.lo
libknotd_la_OBJECTS = $(am_libknotd_la_OBJECTS)
libknots_la_DEPENDENCIES = @LIBOBJS@
-am_libknots_la_OBJECTS = slab.lo tap.lo mempattern.lo lists.lo \
- base64.lo heap.lo print.lo skip-list.lo base32hex.lo \
+am_libknots_la_OBJECTS = murmurhash3.lo slab.lo tap.lo mempattern.lo \
+ lists.lo base64.lo heap.lo print.lo skip-list.lo base32hex.lo \
general-tree.lo evqueue.lo evsched.lo acl.lo sockaddr.lo \
ref.lo errors.lo errcode.lo dSFMT.lo prng.lo fdset.lo \
fdset_poll.lo fdset_kqueue.lo fdset_epoll.lo log.lo
@@ -119,7 +119,8 @@ am_unittests_OBJECTS = acl_tests.$(OBJEXT) base32hex_tests.$(OBJEXT) \
skiplist_tests.$(OBJEXT) slab_tests.$(OBJEXT) \
fdset_tests.$(OBJEXT) conf_tests.$(OBJEXT) \
dthreads_tests.$(OBJEXT) journal_tests.$(OBJEXT) \
- server_tests.$(OBJEXT) unittests_main.$(OBJEXT)
+ server_tests.$(OBJEXT) rrl_tests.$(OBJEXT) \
+ unittests_main.$(OBJEXT)
nodist_unittests_OBJECTS =
unittests_OBJECTS = $(am_unittests_OBJECTS) \
$(nodist_unittests_OBJECTS)
@@ -426,6 +427,8 @@ unittests_SOURCES = \
tests/knot/journal_tests.h \
tests/knot/server_tests.c \
tests/knot/server_tests.h \
+ tests/knot/rrl_tests.c \
+ tests/knot/rrl_tests.h \
tests/unittests_main.c
unittests_libknot_realdata_SOURCES = \
@@ -564,6 +567,8 @@ libknot_la_SOURCES = \
libknot/tsig-op.c
libknots_la_SOURCES = \
+ common/hattrie/murmurhash3.c \
+ common/hattrie/murmurhash3.h \
common/slab/slab.c \
common/slab/slab.h \
common/libtap/tap.c \
@@ -653,6 +658,8 @@ libknotd_la_SOURCES = \
knot/server/notify.h \
knot/server/notify.c \
knot/server/zones.h \
+ knot/server/rrl.c \
+ knot/server/rrl.h \
knot/zone/zone-load.c \
knot/zone/zone-load.h \
knot/zone/semantic-check.c \
@@ -919,6 +926,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logconf.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempattern.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/murmurhash3.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/name-server.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_tests.Po@am__quote@
@@ -944,6 +952,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/response.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/response_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/response_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrl_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrset.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrset_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrset_tests_realdata.Po@am__quote@
@@ -1315,6 +1325,13 @@ notify.lo: knot/server/notify.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o notify.lo `test -f 'knot/server/notify.c' || echo '$(srcdir)/'`knot/server/notify.c
+rrl.lo: knot/server/rrl.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rrl.lo -MD -MP -MF $(DEPDIR)/rrl.Tpo -c -o rrl.lo `test -f 'knot/server/rrl.c' || echo '$(srcdir)/'`knot/server/rrl.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rrl.Tpo $(DEPDIR)/rrl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/server/rrl.c' object='rrl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rrl.lo `test -f 'knot/server/rrl.c' || echo '$(srcdir)/'`knot/server/rrl.c
+
zone-load.lo: knot/zone/zone-load.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone-load.lo -MD -MP -MF $(DEPDIR)/zone-load.Tpo -c -o zone-load.lo `test -f 'knot/zone/zone-load.c' || echo '$(srcdir)/'`knot/zone/zone-load.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone-load.Tpo $(DEPDIR)/zone-load.Plo
@@ -1343,6 +1360,13 @@ zone-dump-text.lo: knot/zone/zone-dump-text.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone-dump-text.lo `test -f 'knot/zone/zone-dump-text.c' || echo '$(srcdir)/'`knot/zone/zone-dump-text.c
+murmurhash3.lo: common/hattrie/murmurhash3.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT murmurhash3.lo -MD -MP -MF $(DEPDIR)/murmurhash3.Tpo -c -o murmurhash3.lo `test -f 'common/hattrie/murmurhash3.c' || echo '$(srcdir)/'`common/hattrie/murmurhash3.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/murmurhash3.Tpo $(DEPDIR)/murmurhash3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/hattrie/murmurhash3.c' object='murmurhash3.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o murmurhash3.lo `test -f 'common/hattrie/murmurhash3.c' || echo '$(srcdir)/'`common/hattrie/murmurhash3.c
+
slab.lo: common/slab/slab.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT slab.lo -MD -MP -MF $(DEPDIR)/slab.Tpo -c -o slab.lo `test -f 'common/slab/slab.c' || echo '$(srcdir)/'`common/slab/slab.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/slab.Tpo $(DEPDIR)/slab.Plo
@@ -1763,6 +1787,20 @@ server_tests.obj: tests/knot/server_tests.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o server_tests.obj `if test -f 'tests/knot/server_tests.c'; then $(CYGPATH_W) 'tests/knot/server_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/server_tests.c'; fi`
+rrl_tests.o: tests/knot/rrl_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rrl_tests.o -MD -MP -MF $(DEPDIR)/rrl_tests.Tpo -c -o rrl_tests.o `test -f 'tests/knot/rrl_tests.c' || echo '$(srcdir)/'`tests/knot/rrl_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rrl_tests.Tpo $(DEPDIR)/rrl_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/knot/rrl_tests.c' object='rrl_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rrl_tests.o `test -f 'tests/knot/rrl_tests.c' || echo '$(srcdir)/'`tests/knot/rrl_tests.c
+
+rrl_tests.obj: tests/knot/rrl_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rrl_tests.obj -MD -MP -MF $(DEPDIR)/rrl_tests.Tpo -c -o rrl_tests.obj `if test -f 'tests/knot/rrl_tests.c'; then $(CYGPATH_W) 'tests/knot/rrl_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/rrl_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rrl_tests.Tpo $(DEPDIR)/rrl_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/knot/rrl_tests.c' object='rrl_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rrl_tests.obj `if test -f 'tests/knot/rrl_tests.c'; then $(CYGPATH_W) 'tests/knot/rrl_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/rrl_tests.c'; fi`
+
unittests_main.o: tests/unittests_main.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unittests_main.o -MD -MP -MF $(DEPDIR)/unittests_main.Tpo -c -o unittests_main.o `test -f 'tests/unittests_main.c' || echo '$(srcdir)/'`tests/unittests_main.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unittests_main.Tpo $(DEPDIR)/unittests_main.Po
diff --git a/src/common/acl.c b/src/common/acl.c
index c8e0488..252f3f9 100644
--- a/src/common/acl.c
+++ b/src/common/acl.c
@@ -17,6 +17,7 @@
#include <string.h>
#include <stdlib.h>
#include <assert.h>
+#include <sys/types.h>
#include <sys/socket.h>
#include "common/acl.h"
diff --git a/src/common/acl.h b/src/common/acl.h
index 7ce8f26..7d4adac 100644
--- a/src/common/acl.h
+++ b/src/common/acl.h
@@ -50,7 +50,7 @@ typedef struct acl_t {
acl_rule_t default_rule; /*!< \brief Default rule. */
skip_list_t *rules; /*!< \brief Data container. */
skip_list_t *rules_pref; /*!< \brief Preferred data container. */
- const char name[]; /*!< \brief ACL semantic name. */
+ char name[]; /*!< \brief ACL semantic name. */
} acl_t;
/*! \brief Single ACL value. */
diff --git a/src/common/errcode.c b/src/common/errcode.c
index ff9be63..75c4e13 100644
--- a/src/common/errcode.c
+++ b/src/common/errcode.c
@@ -70,6 +70,7 @@ const error_table_t knot_error_msgs[] = {
{KNOT_ECNAME, "CNAME loop found in zone."},
{KNOT_ENODIFF, "Cannot create zone diff."},
{KNOT_EDSDIGESTLEN, "DS digest length does not match digest type." },
+ {KNOT_ELIMIT, "Exceeded response rate limit." },
{KNOT_ERROR, 0}
};
diff --git a/src/common/errcode.h b/src/common/errcode.h
index 0693a0d..b2afae5 100644
--- a/src/common/errcode.h
+++ b/src/common/errcode.h
@@ -83,7 +83,8 @@ enum knot_error {
KNOT_EIXFRSPACE, /*!< IXFR reply did not fit in. */
KNOT_ECNAME, /*!< CNAME loop found in zone. */
KNOT_ENODIFF, /*!< No zone diff can be created. */
- KNOT_EDSDIGESTLEN /*!< DS digest length does not match digest type. */
+ KNOT_EDSDIGESTLEN, /*!< DS digest length does not match digest type. */
+ KNOT_ELIMIT /*!< Exceeded response rate limit. */
};
/*! \brief Table linking error messages to error codes. */
diff --git a/src/common/fdset.c b/src/common/fdset.c
index 6c5f0d3..c304840 100644
--- a/src/common/fdset.c
+++ b/src/common/fdset.c
@@ -205,7 +205,7 @@ int fdset_sweep(fdset_t* fdset, void(*cb)(fdset_t*, int, void*), void *data)
}
/* OpenBSD compatibility. */
-#ifndef HAVE_PSELECT
+#if !defined(HAVE_PSELECT) || defined(PSELECT_COMPAT)
/*
* Like select(2) but set the signals to block while waiting in
* select. This version is not entirely race condition safe. Only
@@ -248,12 +248,12 @@ int fdset_sweep(fdset_t* fdset, void(*cb)(fdset_t*, int, void*), void *data)
#include <signal.h>
static int
-pselect (int n,
- fd_set *readfds,
- fd_set *writefds,
- fd_set *exceptfds,
- const struct timespec *timeout,
- const sigset_t *sigmask)
+pselect_compat (int n,
+ fd_set *readfds,
+ fd_set *writefds,
+ fd_set *exceptfds,
+ const struct timespec *timeout,
+ const sigset_t *sigmask)
{
int result;
sigset_t saved_sigmask;
@@ -276,10 +276,17 @@ pselect (int n,
return result;
}
-#endif
+int fdset_pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ const struct timespec *timeout, const sigset_t *sigmask)
+{
+ return pselect_compat(n, readfds, writefds, exceptfds, timeout, sigmask);
+}
+
+#else
int fdset_pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
const struct timespec *timeout, const sigset_t *sigmask)
{
return pselect(n, readfds, writefds, exceptfds, timeout, sigmask);
}
+#endif
diff --git a/src/common/fdset.h b/src/common/fdset.h
index 12fc5c7..a589145 100644
--- a/src/common/fdset.h
+++ b/src/common/fdset.h
@@ -32,9 +32,16 @@
#ifndef _KNOTD_FDSET_H_
#define _KNOTD_FDSET_H_
+#include "config.h"
#include <stddef.h>
#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
+ #include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+ #include <sys/time.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+ #include <signal.h>
#endif
#include "skip-list.h"
#include "mempattern.h"
diff --git a/src/common/fdset_kqueue.c b/src/common/fdset_kqueue.c
index 108c572..a45da96 100644
--- a/src/common/fdset_kqueue.c
+++ b/src/common/fdset_kqueue.c
@@ -95,6 +95,7 @@ int fdset_kqueue_add(fdset_t *fdset, int fd, int events)
int evfilt = EVFILT_READ;
EV_SET(&fdset->events[fdset->nfds], fd, evfilt,
EV_ADD|EV_ENABLE, 0, 0, 0);
+ memset(fdset->revents + fdset->nfds, 0, sizeof(struct kevent));
++fdset->nfds;
return 0;
@@ -195,6 +196,7 @@ int fdset_kqueue_begin(fdset_t *fdset, fdset_it_t *it)
/* Find first. */
it->pos = 0;
+ it->fd = -1;
return fdset_next(fdset, it);
}
@@ -221,7 +223,7 @@ int fdset_kqueue_end(fdset_t *fdset, fdset_it_t *it)
int fdset_kqueue_next(fdset_t *fdset, fdset_it_t *it)
{
- if (fdset == NULL || it == NULL || fdset->nfds < 1) {
+ if (fdset == NULL || it == NULL || fdset->polled < 1) {
return -1;
}
diff --git a/src/common/hattrie/murmurhash3.c b/src/common/hattrie/murmurhash3.c
new file mode 100644
index 0000000..cb24c8f
--- /dev/null
+++ b/src/common/hattrie/murmurhash3.c
@@ -0,0 +1,77 @@
+/* This is MurmurHash3. The original C++ code was placed in the public domain
+ * by its author, Austin Appleby. */
+
+#include "murmurhash3.h"
+
+static inline uint32_t fmix(uint32_t h)
+{
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+
+ return h;
+}
+
+
+static inline uint32_t rotl32(uint32_t x, int8_t r)
+{
+ return (x << r) | (x >> (32 - r));
+}
+
+
+uint32_t hash(const char* data, size_t len_)
+{
+ const int len = (int) len_;
+ const int nblocks = len / 4;
+
+ uint32_t h1 = 0xc062fb4a;
+
+ uint32_t c1 = 0xcc9e2d51;
+ uint32_t c2 = 0x1b873593;
+
+ //----------
+ // body
+
+ const uint32_t * blocks = (const uint32_t*) (data + nblocks * 4);
+
+ int i;
+ for(i = -nblocks; i; i++)
+ {
+ uint32_t k1 = blocks[i];
+
+ k1 *= c1;
+ k1 = rotl32(k1, 15);
+ k1 *= c2;
+
+ h1 ^= k1;
+ h1 = rotl32(h1, 13);
+ h1 = h1*5+0xe6546b64;
+ }
+
+ //----------
+ // tail
+
+ const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
+
+ uint32_t k1 = 0;
+
+ switch(len & 3)
+ {
+ case 3: k1 ^= tail[2] << 16;
+ case 2: k1 ^= tail[1] << 8;
+ case 1: k1 ^= tail[0];
+ k1 *= c1; k1 = rotl32(k1,15); k1 *= c2; h1 ^= k1;
+ }
+
+ //----------
+ // finalization
+
+ h1 ^= len;
+
+ h1 = fmix(h1);
+
+ return h1;
+}
+
diff --git a/src/common/hattrie/murmurhash3.h b/src/common/hattrie/murmurhash3.h
new file mode 100644
index 0000000..ada7e47
--- /dev/null
+++ b/src/common/hattrie/murmurhash3.h
@@ -0,0 +1,11 @@
+
+#ifndef MURMURHASH3_H
+#define MURMURHASH3_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+uint32_t hash(const char* data, size_t len);
+
+#endif
+
diff --git a/src/common/latency.c b/src/common/latency.c
index a563f58..7f5f5f8 100644
--- a/src/common/latency.c
+++ b/src/common/latency.c
@@ -17,6 +17,7 @@
#ifdef PROF_LATENCY
#include <sys/resource.h>
+#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
diff --git a/src/common/latency.h b/src/common/latency.h
index d965c56..39c3093 100644
--- a/src/common/latency.h
+++ b/src/common/latency.h
@@ -35,6 +35,7 @@
/* Do not include from latency.c */
#include <sys/time.h>
+#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
diff --git a/src/common/slab/slab.c b/src/common/slab/slab.c
index b581c42..83d7ac5 100644
--- a/src/common/slab/slab.c
+++ b/src/common/slab/slab.c
@@ -29,35 +29,9 @@
* Magic constants.
*/
#define SLAB_MAGIC 0x51 /*!< "Sl" magic byte (slab type). */
-#define LOBJ_MAGIC 0x0B /*!< "Ob" magic byte (object type). */
#define POISON_DWORD 0xdeadbeef /*!< Memory boundary guard magic. */
#define SLAB_MINCOLOR 64 /*!< Minimum space reserved for cache coloring. */
-#define SLAB_HEADER sizeof(slab_t) /*!< Slab header size. */
-#define ALIGN_PTRSZ __attribute__ ((__aligned__(sizeof(void*))))
-/*! \brief Fast cache id lookup table.
- *
- * Provides O(1) lookup.
- * Filled with interesting values from default
- * or on-demand.
- */
-unsigned ALIGN_PTRSZ SLAB_CACHE_LUT[SLAB_SIZE] = {
- [24] = SLAB_GP_COUNT + 1,
- [800] = SLAB_GP_COUNT + 2
-};
-
-/*! \brief Find the next highest power of 2. */
-static inline unsigned get_next_pow2(unsigned v)
-{
- // Next highest power of 2
- --v;
- v |= v >> 1; v |= v >> 2;
- v |= v >> 4; v |= v >> 8;
- v |= v >> 16;
- ++v;
-
- return v;
-}
/*! \brief Return binary logarithm of a number, which is a power of 2. */
static inline unsigned fastlog2(unsigned v)
@@ -71,45 +45,12 @@ static inline unsigned fastlog2(unsigned v)
return r;
}
-/*!
- * \brief Fast hashing function.
- *
- * Finds the next highest power of 2 and returns binary logarithm.
- * Values are stored in LUT cache for future access.
- */
-static unsigned slab_cache_id(unsigned size)
-{
- // Assert cache id of the smallest bufsize is 0
- if(size <= SLAB_MIN_BUFLEN) {
- return 0;
- }
-
- // Check LUT
- unsigned id = 0;
- if ((id = SLAB_CACHE_LUT[size])) {
- return id;
- } else {
-
- // Compute binary logarithm
- // Next highest power of 2
- id = fastlog2(get_next_pow2(size));
-
- // Shift cacheid of SLAB_MIN_BUFLEN to 0
- id -= SLAB_EXP_OFFSET;
-
- // Store
- SLAB_CACHE_LUT[size] = id;
- }
-
- return id;
-}
-
/*
* Slab run-time constants.
*/
+size_t SLAB_SZ = 0; /*!< Slab size. */
size_t SLAB_MASK = 0; /*!< \brief Slab address mask (for computing offsets). */
-static unsigned SLAB_LOGSIZE = 0; /*!< \brief Binary logarithm of slab size. */
/*!
* Depot is a caching sub-allocator of slabs.
@@ -150,7 +91,7 @@ static void* slab_depot_alloc(size_t bufsize)
}
#else // MEM_SLAB_DEPOT
- if(posix_memalign(&page, SLAB_SIZE, SLAB_SIZE) == 0) {
+ if(posix_memalign(&page, SLAB_SZ, SLAB_SZ) == 0) {
((slab_t*)page)->bufsize = 0;
} else {
page = 0;
@@ -203,12 +144,18 @@ static void slab_depot_destroy()
/*! \brief Initializes slab subsystem (it is called automatically). */
void __attribute__ ((constructor)) slab_init()
{
+ long slab_size = sysconf(_SC_PAGESIZE);
+ if (slab_size < 0) {
+ slab_size = SLAB_MINSIZE;
+ }
+
// Fetch page size
- SLAB_LOGSIZE = fastlog2(SLAB_SIZE);
+ SLAB_SZ = (size_t)slab_size;
+ unsigned slab_logsz = fastlog2(SLAB_SZ);
// Compute slab page mask
SLAB_MASK = 0;
- for (int i = 0; i < SLAB_LOGSIZE; ++i) {
+ for (int i = 0; i < slab_logsz; ++i) {
SLAB_MASK |= 1 << i;
}
SLAB_MASK = ~SLAB_MASK;
@@ -220,10 +167,9 @@ void __attribute__ ((constructor)) slab_init()
/*! \brief Deinitializes slab subsystem (it is called automatically). */
void __attribute__ ((destructor)) slab_deinit()
{
- // Deinitialize global allocator
- if (SLAB_LOGSIZE) {
+ // Deinitialize depot
+ if (SLAB_MASK) {
slab_depot_destroy();
- SLAB_LOGSIZE = SLAB_MASK = 0;
}
}
@@ -334,7 +280,7 @@ static inline void slab_list_move(slab_t** target, slab_t* slab)
slab_t* slab_create(slab_cache_t* cache)
{
- const size_t size = SLAB_SIZE;
+ const size_t size = SLAB_SZ;
slab_t* slab = slab_depot_alloc(cache->bufsize);
@@ -582,149 +528,4 @@ int slab_cache_reap(slab_cache_t* cache)
return count;
}
-int slab_alloc_init(slab_alloc_t* alloc)
-{
- // Invalidate
- memset(alloc, 0, sizeof(slab_alloc_t));
-
- // Initialize descriptors cache
- slab_cache_init(&alloc->descriptors, sizeof(slab_cache_t));
-
- return 0;
-}
-
-void slab_alloc_destroy(slab_alloc_t* alloc)
-{
- // Destroy all caches
- for (unsigned i = 0; i < SLAB_CACHE_COUNT; ++i) {
- if (alloc->caches[i] != 0) {
- slab_cache_destroy(alloc->caches[i]);
- }
- }
-
- // Destroy cache for descriptors
- slab_cache_destroy(&alloc->descriptors);
-}
-
-void* slab_alloc_alloc(slab_alloc_t* alloc, size_t size)
-{
- // Invalid size check
- if (knot_unlikely(!size)) {
- return 0;
- }
-
-#ifdef MEM_POISON
- // Reserve memory for poison
- size += sizeof(int);
-#endif
- // Directly map large block
- if (knot_unlikely(size > SLAB_SIZE/2)) {
-
- // Map block
- size += sizeof(slab_obj_t);
- slab_obj_t* p = 0;
- p = malloc(size);
-
- dbg_mem("%s: mapping large block of %zu bytes at %p\n",
- __func__, size, p + 1);
-
- /* Initialize. */
- p->magic = LOBJ_MAGIC;
- p->size = size - sizeof(slab_obj_t);
-
-#ifdef MEM_POISON
- // Reduce real size
- p->size -= sizeof(int);
-
- // Memory barrier
- int* pb = (int*)((char*)p + size - sizeof(int));
- *pb = POISON_DWORD;
- mprotect(pb, sizeof(int), PROT_NONE);
-#endif
-
- return p + 1;
- }
-
- // Get cache id from size
- unsigned cache_id = slab_cache_id(size);
-
- // Check if associated cache exists
- if (knot_unlikely(alloc->caches[cache_id] == 0)) {
-
- // Assert minimum cache size
- if (knot_unlikely(size < SLAB_MIN_BUFLEN)) {
- size = SLAB_MIN_BUFLEN;
- }
-
- // Calculate cache bufsize
- size_t bufsize = size;
- if (cache_id < SLAB_GP_COUNT) {
- bufsize = get_next_pow2(size);
- }
-
- // Create cache
- dbg_mem("%s: creating cache of %zuB (req. %zuB) (id=%u)\n",
- __func__, bufsize, size, cache_id);
-
- slab_cache_t* cache = slab_cache_alloc(&alloc->descriptors);
- slab_cache_init(cache, bufsize);
- alloc->caches[cache_id] = cache;
- }
-
- // Allocate from cache
- void* mem = slab_cache_alloc(alloc->caches[cache_id]);
-
-#ifdef MEM_POISON
- // Memory barrier
- //int* pb = (int*)((char*)mem + size - sizeof(int));
- //mprotect(pb, sizeof(int), PROT_NONE);
-#endif
- return mem;
-}
-
-void *slab_alloc_realloc(slab_alloc_t* alloc, void *ptr, size_t size)
-{
- // realloc(0) equals to free(ptr)
- if (!size) {
- slab_free(ptr);
- return 0;
- }
-
- // Allocate new buf
- void *nptr = slab_alloc_alloc(alloc, size);
- assert(nptr);
-
- // Copy memory if present
- if (ptr) {
- slab_t* slab = slab_from_ptr(ptr);
- memcpy(nptr, ptr, slab->cache->bufsize);
-
- // Free old buf
- slab_free(ptr);
- }
-
- return nptr;
-}
-
-void slab_alloc_stats(slab_alloc_t* alloc)
-{
-#ifdef MEM_DEBUG
- printf("Cache usage:\n");
- for (int i = 0; i < SLAB_CACHE_COUNT; ++i) {
-
- if (!alloc->caches[i])
- continue;
-
- slab_cache_t* cache = alloc->caches[i];
- unsigned free_s = slab_list_walk(cache->slabs_free);
- unsigned full_s = slab_list_walk(cache->slabs_full);
- printf("%4zu: allocs=%lu frees=%lu "
- "(%u empty+partial, %u full)\n",
- cache->bufsize, cache->stat_allocs,
- cache->stat_frees, free_s, full_s);
- }
-#else
- printf("Cache usage: not available, enable MEM_DEBUG and recompile.\n");
-#endif
-}
diff --git a/src/common/slab/slab.h b/src/common/slab/slab.h
index 4ea7e31..75fcca6 100644
--- a/src/common/slab/slab.h
+++ b/src/common/slab/slab.h
@@ -89,14 +89,11 @@
#include <stdint.h>
/* Constants. */
-#define SLAB_SIZE (4096*4) //!< Slab size (16K blocks)
+#define SLAB_MINSIZE 4096 //!< Slab minimal size (4K blocks)
#define SLAB_MIN_BUFLEN 8 //!< Minimal allocation block size is 8B.
-#define SLAB_EXP_OFFSET 3 //!< Minimal allocation size is 8B = 2^3, exp is 3.
-#define SLAB_GP_COUNT 10 //!< General-purpose caches count.
-#define SLAB_US_COUNT 10 //!< User-specified caches count.
#define SLAB_DEPOT_SIZE 16 //!< N slabs cached = N*SLAB_SIZE kB cap
-#define SLAB_CACHE_COUNT (SLAB_GP_COUNT + SLAB_US_COUNT) //!< Slab cache count.
struct slab_cache_t;
+extern size_t SLAB_MASK;
/* Macros. */
@@ -188,22 +185,6 @@ typedef struct slab_cache_t {
} slab_cache_t;
/*!
- * \brief Slab allocator descriptor.
- *
- * \note For a number of slab caches, consult SLAB_GP_COUNT
- * and a number of specific records in SLAB_CACHE_LUT lookup table.
- *
- * \warning It is currently not advised to use this general purpose allocator,
- * as it usually doesn't yield an expected performance for higher
- * bookkeeping costs and it also depends on the allocation behavior
- * as well. Look for slab_cache for a specialized use in most cases.
- */
-typedef struct slab_alloc_t {
- slab_cache_t descriptors; /*!< Slab cache for cache descriptors. */
- slab_cache_t* caches[SLAB_CACHE_COUNT]; /*!< Number of slab caches. */
-} slab_alloc_t;
-
-/*!
* \brief Create a slab of predefined size.
*
* At the moment, slabs are equal to page size and page size aligned.
@@ -292,61 +273,6 @@ void* slab_cache_alloc(slab_cache_t* cache);
*/
int slab_cache_reap(slab_cache_t* cache);
-/*!
- * \brief Create a general purpose slab allocator.
- *
- * \note Please consult struct slab_alloc_t for performance hints.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int slab_alloc_init(slab_alloc_t* alloc);
-
-/*!
- * \brief Delete slab allocator.
- *
- * This destroys all associated caches and frees memory.
- *
- * \param alloc Given allocator instance.
- */
-void slab_alloc_destroy(slab_alloc_t* alloc);
-
-/*!
- * \brief Allocate a block of memory.
- *
- * Returns a block of allocated memory.
- *
- * \note At least SLAB_MIN_BUFSIZE bytes is allocated.
- *
- * \note Please consult struct slab_alloc_t for performance hints.
- *
- * \param alloc Allocator instance.
- * \param size Requested block size.
- * \retval Pointer to allocated memory.
- * \retval NULL on error.
- */
-void* slab_alloc_alloc(slab_alloc_t* alloc, size_t size);
-
-/*!
- * \brief Reallocate data from one slab to another.
- *
- * \param alloc Allocator instance.
- * \param ptr Pointer to allocated memory.
- * \param size Requested memory block size.
- * \retval Pointer to newly allocated memory.
- * \retval NULL on error.
- *
- */
-void *slab_alloc_realloc(slab_alloc_t* alloc, void *ptr, size_t size);
-
-/*!
- *
- * \brief Dump allocator stats.
- *
- * \param alloc Allocator instance.
- */
-void slab_alloc_stats(slab_alloc_t* alloc);
-
#endif /* _KNOTD_COMMON_SLAB_H_ */
/*! @} */
diff --git a/src/config.h.in b/src/config.h.in
index bc3eb0e..a6f0975 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -135,6 +135,9 @@
/* Define to 1 if you have the `setgroups' function. */
#undef HAVE_SETGROUPS
+/* Define to 1 if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
/* Define to 1 if you have the `socket' function. */
#undef HAVE_SOCKET
diff --git a/src/knot.conf.5 b/src/knot.conf.5
index f99739c..734c812 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-rc2"
+.TH "knot.conf" "5" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc3"
.SH "NAME"
.LP
.B knot.conf
@@ -73,6 +73,26 @@ serves as an example of the configuration for knotc(8) and knotd(8).
# f.e. 1s = 1 second, 1m = 1 minute, 1h = 1 hour, 1d = 1 day
# Default: 10s
max-conn-reply 10s;
+
+ # Rate limit
+ # in queries / second
+ # Default: off (=0)
+ rate-limit 0;
+
+ # Rate limit bucket size
+ # 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;
+
+ # Rate limit SLIP
+ # Each Nth blocked response will be sent as truncated, this is a way to allow
+ # legitimate requests to get a chance to reconnect using TCP
+ # Default: 2
+ rate-limit-slip 2;
}
# Section 'keys' contains list of TSIG keys
diff --git a/src/knot/conf/cf-lex.l b/src/knot/conf/cf-lex.l
index 4be9405..5a53917 100644
--- a/src/knot/conf/cf-lex.l
+++ b/src/knot/conf/cf-lex.l
@@ -95,6 +95,9 @@ ixfr-from-differences { lval.t = yytext; return BUILD_DIFFS; }
max-conn-idle { lval.t = yytext; return MAX_CONN_IDLE; }
max-conn-handshake { lval.t = yytext; return MAX_CONN_HS; }
max-conn-reply { lval.t = yytext; return MAX_CONN_REPLY; }
+rate-limit { lval.t = yytext; return RATE_LIMIT; }
+rate-limit-size { lval.t = yytext; return RATE_LIMIT_SIZE; }
+rate-limit-slip { lval.t = yytext; return RATE_LIMIT_SLIP; }
interfaces { lval.t = yytext; return INTERFACES; }
address { lval.t = yytext; return ADDRESS; }
diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y
index a0c6aaf..ad5659e 100644
--- a/src/knot/conf/cf-parse.y
+++ b/src/knot/conf/cf-parse.y
@@ -294,6 +294,9 @@ static int conf_mask(void* scanner, int nval, int prefixlen) {
%token <tok> MAX_CONN_IDLE
%token <tok> MAX_CONN_HS
%token <tok> MAX_CONN_REPLY
+%token <tok> RATE_LIMIT
+%token <tok> RATE_LIMIT_SIZE
+%token <tok> RATE_LIMIT_SLIP
%token <tok> INTERFACES ADDRESS PORT
%token <tok> IPA
@@ -435,6 +438,10 @@ system:
| system MAX_CONN_IDLE INTERVAL ';' { new_config->max_conn_idle = $3.i; }
| system MAX_CONN_HS INTERVAL ';' { new_config->max_conn_hs = $3.i; }
| system MAX_CONN_REPLY INTERVAL ';' { new_config->max_conn_reply = $3.i; }
+ | system RATE_LIMIT NUM ';' { new_config->rrl = $3.i; }
+ | system RATE_LIMIT_SIZE SIZE ';' { new_config->rrl_size = $3.l; }
+ | system RATE_LIMIT_SIZE NUM ';' { new_config->rrl_size = $3.i; }
+ | system RATE_LIMIT_SLIP NUM ';' { new_config->rrl_slip = $3.i; }
;
keys:
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index ac36670..f93eefe 100644
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -181,6 +181,14 @@ static int conf_process(conf_t *conf)
if (conf->ctl.iface && conf->ctl.iface->port <= 0) {
conf->ctl.iface->port = REMOTE_DPORT;
}
+
+ /* Default RRL limits. */
+ if (conf->rrl_slip < 0) {
+ conf->rrl_slip = CONFIG_RRL_SLIP;
+ }
+ if (conf->rrl_size == 0) {
+ conf->rrl_size = CONFIG_RRL_SIZE;
+ }
// Postprocess zones
int ret = KNOT_EOK;
@@ -226,6 +234,10 @@ static int conf_process(conf_t *conf)
// Default zone file
if (zone->file == NULL) {
zone->file = strcdup(zone->name, ".zone");
+ if (!zone->file) {
+ ret = KNOT_ENOMEM;
+ continue;
+ }
}
// Relative zone filenames should be relative to storage
diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h
index a64388e..f56d92d 100644
--- a/src/knot/conf/conf.h
+++ b/src/knot/conf/conf.h
@@ -50,6 +50,8 @@
#define CONFIG_REPLY_WD 10 /*!< SOA/NOTIFY query timeout [s]. */
#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. */
/*!
* \brief Configuration for the interface
@@ -180,6 +182,9 @@ typedef struct conf_t {
int max_conn_idle; /*!< TCP idle timeout. */
int max_conn_hs; /*!< TCP of inactivity before first query. */
int max_conn_reply; /*!< TCP/UDP query timeout. */
+ int rrl; /*!< Rate limit (in responses per second). */
+ size_t rrl_size; /*!< Rate limit htable size. */
+ int rrl_slip; /*!< Rate limit SLIP. */
/*
* Log
diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c
index 92c5c09..3881ab9 100644
--- a/src/knot/ctl/knotc_main.c
+++ b/src/knot/ctl/knotc_main.c
@@ -684,6 +684,7 @@ 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();
return 1;
@@ -775,7 +776,6 @@ static int cmd_start(int argc, char *argv[], unsigned flags, int jobs)
} else {
log_server_info("Forcing server start.\n");
pid_remove(pidfile);
- pid = -1;
}
} else {
/* Create empty PID file. */
diff --git a/src/knot/main.c b/src/knot/main.c
index b3d8b8d..4359194 100644
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -356,7 +356,6 @@ int main(int argc, char **argv)
/* Close remote control interface */
if (remote > -1) {
close(remote);
- remote = -1;
}
if ((server_wait(server)) != KNOT_EOK) {
diff --git a/src/knot/other/debug.h b/src/knot/other/debug.h
index 1a8698e..c88e166 100644
--- a/src/knot/other/debug.h
+++ b/src/knot/other/debug.h
@@ -33,6 +33,7 @@
#define KNOTD_THREADS_DEBUG
#define KNOTD_JOURNAL_DEBUG
#define KNOTD_NET_DEBUG
+ #define KNOTD_RRL_DEBUG
#endif
#ifdef KNOT_ZONES_DEBUG
@@ -179,6 +180,47 @@
/******************************************************************************/
+#ifdef KNOTD_RRL_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_rrl(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_rrl_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_rrl(msg...)
+#define dbg_rrl_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_rrl_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_rrl_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_rrl_verb(msg...)
+#define dbg_rrl_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_rrl_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_rrl_hex_detail(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_rrl_detail(msg...)
+#define dbg_rrl_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_rrl(msg...)
+#define dbg_rrl_hex(data, len)
+#define dbg_rrl_verb(msg...)
+#define dbg_rrl_hex_verb(data, len)
+#define dbg_rrl_detail(msg...)
+#define dbg_rrl_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
#ifdef KNOTD_THREADS_DEBUG
/* Brief messages. */
diff --git a/src/knot/server/dthreads.c b/src/knot/server/dthreads.c
index 82d7dd2..45fa866 100644
--- a/src/knot/server/dthreads.c
+++ b/src/knot/server/dthreads.c
@@ -258,10 +258,7 @@ static dthread_t *dt_create_thread(dt_unit_t *unit)
static void dt_delete_thread(dthread_t **thread)
{
// Check
- if (thread == 0) {
- return;
- }
- if (*thread == 0) {
+ if (!thread || !*thread) {
return;
}
diff --git a/src/knot/server/rrl.c b/src/knot/server/rrl.c
new file mode 100644
index 0000000..70101c9
--- /dev/null
+++ b/src/knot/server/rrl.c
@@ -0,0 +1,416 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <assert.h>
+
+#include "knot/server/rrl.h"
+#include "knot/common.h"
+#include "libknot/consts.h"
+#include "libknot/util/wire.h"
+#include "common/hattrie/murmurhash3.h"
+#include "common/prng.h"
+#include "common/errors.h"
+
+/* Limits */
+#define RRL_CLSBLK_MAXLEN (4 + 8 + 1 + 256)
+/* CIDR block prefix lengths for v4/v6 */
+#define RRL_V4_PREFIX ((uint32_t)0x00ffffff) /* /24 */
+#define RRL_V6_PREFIX ((uint64_t)0x00ffffffffffffff) /* /56 */
+/* Defaults */
+#define RRL_DEFAULT_RATE 100
+#define RRL_CAPACITY 4 /* N seconds. */
+#define RRL_SSTART 2 /* 1/Nth of the rate for slow start */
+#define RRL_PSIZE_LARGE 1024
+/* 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. */
+ CLS_NORMAL = 1 << 0, /* Normal response. */
+ CLS_ERROR = 1 << 1, /* Error response. */
+ CLS_NXDOMAIN = 1 << 2, /* NXDOMAIN (special case of error). */
+ CLS_EMPTY = 1 << 3, /* Empty response. */
+ CLS_LARGE = 1 << 4, /* Response size over threshold (1024k). */
+ CLS_WILDCARD = 1 << 5, /* Wildcard query. */
+ CLS_ANY = 1 << 6, /* ANY query (spec. class). */
+ CLS_DNSSEC = 1 << 7 /* DNSSEC related RR query (spec. class) */
+};
+
+/* Classification string. */
+const error_table_t rrl_clsstr_tbl[] = {
+ {CLS_NORMAL, "POSITIVE" },
+ {CLS_ERROR, "ERROR" },
+ {CLS_NXDOMAIN,"NXDOMAIN"},
+ {CLS_EMPTY, "EMPTY"},
+ {CLS_LARGE, "LARGE"},
+ {CLS_WILDCARD,"WILDCARD"},
+ {CLS_ANY, "ANY"},
+ {CLS_DNSSEC, "DNSSEC"},
+ {CLS_NULL, "NULL"},
+ {CLS_NULL, NULL}
+};
+static inline const char *rrl_clsstr(int code)
+{
+ return error_to_str(rrl_clsstr_tbl, code);
+}
+
+/* Bucket flags. */
+enum {
+ RRL_BF_NULL = 0 << 0, /* No flags. */
+ RRL_BF_SSTART = 1 << 0, /* Bucket in slow-start after collision. */
+ RRL_BF_ELIMIT = 1 << 1 /* Bucket is rate-limited. */
+};
+
+static uint8_t rrl_clsid(rrl_req_t *p)
+{
+ /* Check error code */
+ int ret = CLS_NULL;
+ switch (knot_wire_get_rcode(p->w)) {
+ case KNOT_RCODE_NOERROR: ret = CLS_NORMAL; break;
+ case KNOT_RCODE_NXDOMAIN: return CLS_NXDOMAIN; break;
+ default: return CLS_ERROR; break;
+ }
+
+ /* Check if answered from a qname */
+ if (ret == CLS_NORMAL && p->flags & KNOT_PF_WILDCARD) {
+ return CLS_WILDCARD;
+ }
+
+ /* Check query type for spec. classes. */
+ if (p->qst) {
+ switch(p->qst->qtype) {
+ case KNOT_RRTYPE_ANY: /* ANY spec. class */
+ return CLS_ANY;
+ break;
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_RRSIG:
+ case KNOT_RRTYPE_DS: /* DNSSEC-related RR class. */
+ return CLS_DNSSEC;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Check packet size for threshold. */
+ if (p->len >= RRL_PSIZE_LARGE) {
+ return CLS_LARGE;
+ }
+
+ /* Check ancount */
+ if (knot_wire_get_ancount(p->w) == 0) {
+ return CLS_EMPTY;
+ }
+
+ return ret;
+}
+
+static int rrl_clsname(char *dst, size_t maxlen, uint8_t cls,
+ rrl_req_t *p, const knot_zone_t *z)
+{
+ const knot_dname_t *dn = NULL;
+ const uint8_t *n = (const uint8_t*)"\x00"; /* Fallback zone (for errors etc.) */
+ int nb = 1;
+ if (z) { /* Found associated zone. */
+ dn = knot_zone_name(z);
+ }
+ switch (cls) {
+ case CLS_ERROR: /* Could be a non-existent zone or garbage. */
+ case CLS_NXDOMAIN: /* Queries to non-existent names in zone. */
+ case CLS_WILDCARD: /* Queries to names covered by a wildcard. */
+ dbg_rrl_verb("%s: using zone/fallback name\n", __func__);
+ break;
+ default:
+ if (p->qst) dn = p->qst->qname;
+ break;
+ }
+
+ if (dn) { /* Check used dname. */
+ assert(dn); /* Should be always set. */
+ n = knot_dname_name(dn);
+ nb = (int)knot_dname_size(dn);
+ }
+
+ /* Write to wire */
+ if (nb > maxlen) return KNOT_ESPACE;
+ if (memcpy(dst, n, nb) == NULL) {
+ dbg_rrl("%s: failed to serialize name=%p len=%u\n",
+ __func__, n, nb);
+ return KNOT_ERROR;
+ }
+
+ return nb;
+}
+
+static int rrl_classify(char *dst, size_t maxlen, const sockaddr_t *a,
+ rrl_req_t *p, const knot_zone_t *z, uint32_t seed)
+{
+ if (!dst || !p || !a || maxlen == 0) {
+ return KNOT_EINVAL;
+ }
+
+ /* Class */
+ uint8_t cls = rrl_clsid(p);
+ *dst = cls;
+ int blklen = sizeof(cls);
+
+ /* Address (in network byteorder, adjust masks). */
+ uint64_t nb = 0;
+ if (a->family == AF_INET6) { /* Take the /56 prefix. */
+ nb = *((uint64_t*)&a->addr6.sin6_addr) & RRL_V6_PREFIX;
+ } else { /* Take the /24 prefix */
+ nb = (uint32_t)a->addr4.sin_addr.s_addr & RRL_V4_PREFIX;
+ }
+ if (blklen + sizeof(nb) > maxlen) return KNOT_ESPACE;
+ memcpy(dst + blklen, (void*)&nb, sizeof(nb));
+ blklen += sizeof(nb);
+
+ /* Name */
+ int len = rrl_clsname(dst + blklen, maxlen - blklen, cls, p, z);
+ if (len < 0) return len;
+ blklen += len;
+
+ /* Seed. */
+ if (blklen + sizeof(seed) > maxlen) return KNOT_ESPACE;
+ if (memcpy(dst + blklen, (void*)&seed, sizeof(seed)) == 0) {
+ blklen += sizeof(seed);
+ }
+
+ 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)
+{
+ 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;
+
+ /* Check locking. */
+ *lk = -1;
+ if (t->lk_count > 0) {
+ *lk = id % t->lk_count;
+ rrl_lock_mx(t, *lk);
+ }
+
+ rrl_item_t *b = t->arr + id;
+ dbg_rrl("%s: classified pkt as '0x%x' bucket=%p\n", __func__, id, b);
+
+ /* 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 */
+ }
+ /* 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 b;
+}
+
+static void rrl_log_state(const sockaddr_t *a, uint16_t flags, uint8_t cls)
+{
+#ifdef RRL_ENABLE_LOG
+ char saddr[SOCKADDR_STRLEN];
+ memset(saddr, 0, sizeof(saddr));
+ sockaddr_tostr(a, saddr, sizeof(saddr));
+ const char *what = "leaves";
+ if (flags & RRL_BF_ELIMIT) {
+ what = "enters";
+ }
+
+ log_server_notice("Address '%s' %s rate-limiting (class '%s').\n",
+ saddr, what, rrl_clsstr(cls));
+#endif
+}
+
+rrl_table_t *rrl_create(size_t size)
+{
+ 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);
+ t->size = size;
+ dbg_rrl("%s: created table size '%zu'\n", __func__, t->size);
+ return t;
+}
+
+uint32_t rrl_setrate(rrl_table_t *rrl, uint32_t rate)
+{
+ if (!rrl) return 0;
+ uint32_t old = rrl->rate;
+ rrl->rate = rate;
+ return old;
+}
+
+uint32_t rrl_rate(rrl_table_t *rrl)
+{
+ if (!rrl) return 0;
+ return rrl->rate;
+}
+
+int rrl_setlocks(rrl_table_t *rrl, size_t granularity)
+{
+ if (!rrl) return KNOT_EINVAL;
+ assert(!rrl->lk); /* Cannot change while locks are used. */
+
+ /* Alloc new locks. */
+ rrl->lk = malloc(granularity * sizeof(rrl_lock_t));
+ if (!rrl->lk) return KNOT_ENOMEM;
+ memset(rrl->lk, 0, granularity * sizeof(rrl_lock_t));
+
+ /* Initialize. */
+ for (size_t i = 0; i < granularity; ++i) {
+ if (pthread_mutex_init(&rrl->lk[i].mx, 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);
+ }
+ free(rrl->lk);
+ rrl->lk_count = 0;
+ dbg_rrl("%s: failed to init locks\n", __func__);
+ return KNOT_ERROR;
+ }
+
+ dbg_rrl("%s: set granularity to '%zu'\n", __func__, granularity);
+ return KNOT_EOK;
+}
+
+int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req,
+ const knot_zone_t *zone)
+{
+ if (!rrl || !req || !a) return KNOT_EINVAL;
+
+ /* Calculate hash and fetch */
+ int ret = KNOT_EOK;
+ int lock = -1;
+ 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__);
+ return KNOT_ERROR;
+ }
+
+ /* Calculate rate for dT */
+ uint32_t dt = now - b->time;
+ if (dt > RRL_CAPACITY) {
+ dt = RRL_CAPACITY;
+ }
+ /* Visit bucket. */
+ b->time = now;
+ dbg_rrl("%s: bucket=0x%x tokens=%hu flags=%x dt=%u\n",
+ __func__, (unsigned)(b - rrl->arr), b->ntok, b->flags, dt);
+ if (dt > 0) { /* Window moved. */
+
+ /* Check state change. */
+ if ((b->ntok > 0 || dt > 1) && (b->flags & RRL_BF_ELIMIT)) {
+ b->flags &= ~RRL_BF_ELIMIT;
+ rrl_log_state(a, b->flags, b->cls);
+ }
+
+ /* 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));
+ }
+ b->ntok += dn;
+ if (b->ntok > RRL_CAPACITY * rrl->rate) {
+ b->ntok = RRL_CAPACITY * rrl->rate;
+ }
+ }
+
+ /* Last item taken. */
+ if (b->ntok == 1 && !(b->flags & RRL_BF_ELIMIT)) {
+ b->flags |= RRL_BF_ELIMIT;
+ rrl_log_state(a, b->flags, b->cls);
+ }
+
+ /* Decay current bucket. */
+ if (b->ntok > 0) {
+ --b->ntok;
+ } else if (b->ntok == 0) {
+ ret = KNOT_ELIMIT;
+ }
+
+ /* Unlock bucket. */
+ if (lock > -1) {
+ rrl_unlock_mx(rrl, lock);
+ }
+
+ return ret;
+}
+
+int rrl_destroy(rrl_table_t *rrl)
+{
+ if (rrl) {
+ dbg_rrl("%s: freeing table %p\n", __func__, rrl);
+ for (size_t i = 0; i < rrl->lk_count; ++i) {
+ pthread_mutex_destroy(&rrl->lk[i].mx);
+ }
+ free(rrl->lk);
+ }
+
+ free(rrl);
+ return KNOT_EOK;
+}
diff --git a/src/knot/server/rrl.h b/src/knot/server/rrl.h
new file mode 100644
index 0000000..e0f650d
--- /dev/null
+++ b/src/knot/server/rrl.h
@@ -0,0 +1,143 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/*!
+ * \file rrl.h
+ *
+ * \author Marek Vavrusa <marek.vavusa@nic.cz>
+ *
+ * \brief Response-rate limiting API.
+ *
+ * \addtogroup network
+ * @{
+ */
+
+#ifndef _KNOTD_RRL_H_
+#define _KNOTD_RRL_H_
+
+#include <stdint.h>
+#include <pthread.h>
+#include "common/sockaddr.h"
+#include "libknot/packet/packet.h"
+#include "libknot/zone/zone.h"
+
+/* Defaults */
+#define RRL_LOCK_GRANULARITY 10 /* Last digit granularity */
+
+/*!
+ * \brief RRL hash bucket.
+ */
+typedef struct rrl_item {
+ uint64_t pref; /* Prefix associated. */
+ uint16_t ntok; /* Tokens available */
+ uint8_t cls; /* Bucket class */
+ uint8_t flags; /* Flags */
+ 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.
+ *
+ * Table is fixed size, so collisions may occur and are dealt with
+ * in a way, that hashbucket rate is reset and enters slow-start for 1 dt.
+ * When a bucket is in a slow-start mode, it cannot reset again for the time
+ * period.
+ *
+ * To avoid lock contention, N locks are created and distributed amongst buckets.
+ * As of now lock K for bucket N is calculated as K = N % (num_buckets).
+ */
+
+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). */
+ size_t size; /* Number of buckets */
+ rrl_item_t arr[]; /* Buckets */
+} rrl_table_t;
+
+/*!
+ * \brief RRL request descriptor.
+ */
+typedef struct rrl_req {
+ const uint8_t *w;
+ uint16_t len;
+ unsigned flags;
+ const knot_question_t *qst;
+} rrl_req_t;
+
+/*!
+ * \brief Create a RRL table.
+ * \param size Fixed hashtable size (reasonable large prime is recommended).
+ * \return created table or NULL.
+ */
+rrl_table_t *rrl_create(size_t size);
+
+/*!
+ * \brief Get RRL table default rate.
+ * \param rrl RRL table.
+ * \return rate
+ */
+uint32_t rrl_rate(rrl_table_t *rrl);
+
+/*!
+ * \brief Set RRL table default rate.
+ *
+ * \note When changing the rate, it is NOT applied to all buckets immediately.
+ *
+ * \param rrl RRL table.
+ * \param rate New rate (in pkts/sec).
+ * \return old rate
+ */
+uint32_t rrl_setrate(rrl_table_t *rrl, uint32_t rate);
+
+/*!
+ * \brief Set N distributed locks for the RRL table.
+ *
+ * \param rrl RRL table.
+ * \param granularity Number of created locks.
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int rrl_setlocks(rrl_table_t *rrl, size_t granularity);
+
+/*!
+ * \brief Query the RRL table for accept or deny, when the rate limit is reached.
+ *
+ * \param rrl RRL table.
+ * \param a Source address.
+ * \param req RRL request (containing resp., flags and question).
+ * \param zone Zone related to the response (or NULL).
+ * \retval KNOT_EOK if passed.
+ * \retval KNOT_ELIMIT when the limit is reached.
+ */
+int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req,
+ const knot_zone_t *zone);
+
+/*!
+ * \brief Destroy RRL table.
+ * \param rrl RRL table.
+ * \return KNOT_EOK
+ */
+int rrl_destroy(rrl_table_t *rrl);
+
+
+#endif /* _KNOTD_RRL_H_ */
+
+/*! @} */
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
index e42460f..08abe11 100644
--- a/src/knot/server/server.c
+++ b/src/knot/server/server.c
@@ -351,12 +351,20 @@ static int server_bind_handlers(server_t *server)
WALK_LIST(n, *server->ifaces) {
iface_t *iface = (iface_t*)n;
+ assert(iface);
/* Create UDP handlers. */
dt_unit_t *unit = 0;
if (!iface->handler[UDP_ID]) {
unit = dt_create_coherent(thr_count, &udp_master, 0);
+ if (!unit) {
+ continue;
+ }
h = server_create_handler(server, iface->fd[UDP_ID], unit);
+ if (!h) {
+ dt_delete(&unit);
+ continue;
+ }
h->type = iface->type[UDP_ID];
h->iface = iface;
@@ -370,7 +378,14 @@ static int server_bind_handlers(server_t *server)
/* Create TCP handlers. */
if (!iface->handler[TCP_ID]) {
unit = dt_create(tcp_unit_size);
+ if (!unit) {
+ continue;
+ }
h = server_create_handler(server, iface->fd[TCP_ID], unit);
+ if (!h) {
+ dt_delete(&unit);
+ continue;
+ }
tcp_loop_unit(h, unit);
h->type = iface->type[TCP_ID];
h->iface = iface;
@@ -397,6 +412,7 @@ server_t *server_create()
ERR_ALLOC_FAILED;
return NULL;
}
+ memset(server, 0, sizeof(server_t));
server->state = ServerIdle;
init_list(&server->handlers);
@@ -408,6 +424,7 @@ server_t *server_create()
server->sched = evsched_new();
dt_unit_t *unit = dt_create_coherent(1, evsched_run, 0);
iohandler_t *h = server_create_handler(server, -1, unit);
+
h->data = server->sched;
// Create name server
@@ -738,6 +755,9 @@ void server_destroy(server_t **server)
// Delete event scheduler
evsched_delete(&(*server)->sched);
+
+ /* Delete rate limiting table. */
+ rrl_destroy((*server)->rrl);
free(*server);
@@ -753,6 +773,23 @@ int server_conf_hook(const struct conf_t *conf, void *data)
if (!server) {
return KNOT_EINVAL;
}
+
+ /* Rate limiting. */
+ if (!server->rrl && conf->rrl > 0) {
+ server->rrl = rrl_create(conf->rrl_size);
+ if (!server->rrl) {
+ log_server_error("Couldn't init rate limiting table.\n");
+ } else {
+ rrl_setlocks(server->rrl, RRL_LOCK_GRANULARITY);
+ }
+ }
+ if (server->rrl) {
+ if (rrl_rate(server->rrl) != conf->rrl) {
+ rrl_setrate(server->rrl, conf->rrl);
+ log_server_info("Rate limiting set to %u responses/sec.\n",
+ conf->rrl);
+ } /* At this point, old buckets will converge to new rate. */
+ }
/* Update bound sockets. */
int ret = KNOT_EOK;
diff --git a/src/knot/server/server.h b/src/knot/server/server.h
index fa7597f..6d2e06c 100644
--- a/src/knot/server/server.h
+++ b/src/knot/server/server.h
@@ -41,6 +41,7 @@
#include "knot/server/xfr-handler.h"
#include "knot/server/socket.h"
#include "knot/server/dthreads.h"
+#include "knot/server/rrl.h"
#include "libknot/zone/zonedb.h"
#include "common/evsched.h"
#include "common/lists.h"
@@ -118,6 +119,9 @@ typedef struct server_t {
/*! \brief List of interfaces. */
list* ifaces;
+
+ /*! \brief Rate limiting. */
+ rrl_table_t *rrl;
} server_t;
diff --git a/src/knot/server/socket.c b/src/knot/server/socket.c
index 6a32d4d..db39a0d 100644
--- a/src/knot/server/socket.c
+++ b/src/knot/server/socket.c
@@ -23,6 +23,7 @@
#include <stdio.h>
#include <netdb.h>
#include <time.h>
+#include <sys/types.h>
#include <sys/socket.h>
#ifdef HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
diff --git a/src/knot/server/socket.h b/src/knot/server/socket.h
index 8bbd6cc..153b066 100644
--- a/src/knot/server/socket.h
+++ b/src/knot/server/socket.h
@@ -34,6 +34,7 @@
#define _KNOTD_SOCKET_H_
/* POSIX only. */
+#include <sys/types.h>
#include <sys/socket.h>
#include "common/sockaddr.h"
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
index 06f42c2..8f3a324 100644
--- a/src/knot/server/tcp-handler.c
+++ b/src/knot/server/tcp-handler.c
@@ -18,10 +18,11 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
-#include <sys/socket.h>
-#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_CAP_NG_H
diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c
index e34c710..7b5e701 100644
--- a/src/knot/server/udp-handler.c
+++ b/src/knot/server/udp-handler.c
@@ -23,8 +23,9 @@
#include <time.h>
#include <unistd.h>
#include <errno.h>
-#include <arpa/inet.h>
+#include <sys/types.h>
#include <sys/socket.h>
+#include <arpa/inet.h>
#include <sys/poll.h>
#include <sys/syscall.h>
#include <netinet/in.h>
@@ -56,20 +57,52 @@
#endif
#endif
+/* UDP request struct. */
+struct udp_req_t {
+ sockaddr_t *addr;
+ uint8_t *qbuf;
+ size_t qbuflen;
+ size_t *resplen;
+ rrl_table_t *rrl;
+ unsigned slip;
+};
+
/*! \brief Pointer to selected UDP master implementation. */
static int (*_udp_master)(dthread_t *, stat_t *) = 0;
-///*! \brief Wrapper for UDP send. */
-//static int xfr_send_udp(int session, sockaddr_t *addr, uint8_t *msg, size_t msglen)
-//{
-// return sendto(session, msg, msglen, 0, addr->ptr, addr->len);
-//}
+/*! \brief RRL reject procedure. */
+static size_t udp_rrl_reject(const knot_nameserver_t *ns,
+ const knot_packet_t *packet,
+ uint8_t* resp, size_t rlen,
+ uint8_t rcode, unsigned *slip)
+{
+ int n_slip = conf()->rrl_slip; /* Check SLIP. */
+ if (n_slip > 0 && n_slip == ++*slip) {
+ knot_ns_error_response_from_query(ns, packet, rcode, resp, &rlen);
+ switch(rcode) { /* Do not set TC=1 to some RCODEs. */
+ case KNOT_RCODE_FORMERR:
+ case KNOT_RCODE_REFUSED:
+ case KNOT_RCODE_SERVFAIL:
+ case KNOT_RCODE_NOTIMPL:
+ break;
+ default:
+ knot_wire_set_tc(resp); /* Set TC=1 */
+ break;
+ }
+
+ *slip = 0; /* Restart SLIP interval. */
+ return rlen;
+ }
+
+ return 0; /* Discard response. */
+}
int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
- sockaddr_t* addr, knot_nameserver_t *ns)
+ sockaddr_t* addr, knot_nameserver_t *ns, rrl_table_t *rrl, unsigned *slip)
{
#ifdef DEBUG_ENABLE_BRIEF
char strfrom[SOCKADDR_STRLEN];
+ memset(strfrom, 0, sizeof(strfrom));
sockaddr_tostr(addr, strfrom, sizeof(strfrom));
dbg_net("udp: fd=%d received %zd bytes from '%s@%d'.\n", fd, qbuflen,
strfrom, sockaddr_portnum(addr));
@@ -93,44 +126,37 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
return KNOT_EOK; /* Created error response. */
}
-
+
+ /* Prepare RRL structs. */
+ rrl_req_t rrl_rq;
+ memset(&rrl_rq, 0, sizeof(rrl_req_t));
+ rrl_rq.w = qbuf; /* Wire */
+
/* Parse query. */
int res = knot_ns_parse_packet(qbuf, qbuflen, packet, &qtype);
+ if (rrl) rrl_rq.qst = &packet->question;
if (knot_unlikely(res != KNOT_EOK)) {
dbg_net("udp: failed to parse packet on fd=%d\n", fd);
if (res > 0) { /* Returned RCODE */
-// int ret = knot_ns_error_response_from_query_wire(ns,
-// qbuf, qbuflen, res, qbuf, resp_len);
- int ret = knot_ns_error_response_from_query(ns,
- packet, res, qbuf, resp_len);
-
- if (ret != KNOT_EOK) {
+ res = knot_ns_error_response_from_query(
+ ns, packet, res, qbuf, resp_len);
+ if (res != KNOT_EOK) {
knot_packet_free(&packet);
return KNOT_EMALF;
}
} else {
- assert(res < 0);
- int ret = knot_ns_error_response_from_query_wire(
+ res = knot_ns_error_response_from_query_wire(
ns, qbuf, qbuflen, KNOT_RCODE_SERVFAIL, qbuf,
resp_len);
-
- if (ret != KNOT_EOK) {
+ if (res != KNOT_EOK) {
knot_packet_free(&packet);
- return ret;
+ return res;
}
}
-
- knot_packet_free(&packet);
- return KNOT_EOK; /* Created error response. */
}
-
+
/* Handle query. */
-// server_t *srv = (server_t *)knot_ns_get_data(ns);
-// knot_ns_xfr_t xfr;
- res = KNOT_ERROR;
switch(qtype) {
-
- /* Query types. */
case KNOT_QUERY_NORMAL:
res = zones_normal_query_answer(ns, packet, addr, qbuf,
resp_len, NS_TRANSPORT_UDP);
@@ -139,33 +165,23 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
/* RFC1034, p.28 requires reliable transfer protocol.
* Bind responds with FORMERR.
*/
- /*! \note Draft exists for AXFR/UDP, but has not been standardized. */
knot_ns_error_response_from_query(ns, packet,
KNOT_RCODE_FORMERR, qbuf,
resp_len);
res = KNOT_EOK;
break;
case KNOT_QUERY_IXFR:
- /* According to RFC1035, respond with SOA.
- * Draft proposes trying to fit response into one packet,
- * but I have found no tool or slave server to actually attempt
- * IXFR/UDP.
- */
-// knot_packet_set_qtype(packet, KNOT_RRTYPE_SOA);
+ /* According to RFC1035, respond with SOA. */
res = zones_normal_query_answer(ns, packet, addr,
qbuf, resp_len,
NS_TRANSPORT_UDP);
break;
case KNOT_QUERY_NOTIFY:
res = notify_process_request(ns, packet, addr,
- qbuf, resp_len);
+ qbuf, resp_len);
break;
case KNOT_QUERY_UPDATE:
-// dbg_net("udp: UPDATE query on fd=%d not implemented\n", fd);
-// knot_ns_error_response_from_query(ns, packet,
-// KNOT_RCODE_NOTIMPL, qbuf,
-// resp_len);
res = zones_process_update(ns, packet, addr, qbuf, resp_len,
fd, NS_TRANSPORT_UDP);
break;
@@ -187,6 +203,20 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
res = KNOT_EOK;
break;
}
+
+ /* Process RRL. */
+ if (rrl) {
+ rcu_read_lock();
+ rrl_rq.flags = packet->flags;
+ if (rrl_query(rrl, addr, &rrl_rq, packet->zone) != KNOT_EOK) {
+ *resp_len = udp_rrl_reject(ns, packet, qbuf,
+ SOCKET_MTU_SZ,
+ knot_wire_get_rcode(qbuf),
+ slip);
+ }
+ rcu_read_unlock();
+ }
+
knot_packet_free(&packet);
@@ -239,6 +269,10 @@ static inline int udp_master_recvfrom(dthread_t *thread, stat_t *thread_stat)
} else {
sock = sock_dup;
}
+
+ /* Initialize RRL if configured. */
+ unsigned rrl_slip = 0;
+ rrl_table_t *rrl = h->server->rrl;
/* Loop until all data is read. */
ssize_t n = 0;
@@ -268,7 +302,8 @@ static inline int udp_master_recvfrom(dthread_t *thread, stat_t *thread_stat)
/* Handle received pkt. */
size_t resp_len = 0;
- int rc = udp_handle(sock, qbuf, n, &resp_len, &addr, ns);
+ int rc = udp_handle(sock, qbuf, n, &resp_len, &addr, ns,
+ rrl, &rrl_slip);
/* Send response. */
if (rc == KNOT_EOK && resp_len > 0) {
@@ -413,6 +448,10 @@ static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat)
cpu[0] = cpu[0] % cpcount;
dt_setaffinity(thread, cpu, 2);
}
+
+ /* Initialize RRL if configured. */
+ unsigned rrl_slip = 0;
+ rrl_table_t *rrl = h->server->rrl;
/* Loop until all data is read. */
ssize_t n = 0;
@@ -448,7 +487,7 @@ static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat)
struct iovec *cvec = msgs[i].msg_hdr.msg_iov;
size_t resp_len = msgs[i].msg_len;
ret = udp_handle(sock, cvec->iov_base, resp_len, &resp_len,
- addrs + i, ns);
+ addrs + i, ns, rrl, &rrl_slip);
if (ret == KNOT_EOK) {
msgs[i].msg_len = resp_len;
iov[i].iov_len = resp_len;
diff --git a/src/knot/server/udp-handler.h b/src/knot/server/udp-handler.h
index 073a4d8..8292072 100644
--- a/src/knot/server/udp-handler.h
+++ b/src/knot/server/udp-handler.h
@@ -53,7 +53,8 @@
* \retval KNOT_ENOMEM
*/
int udp_handle(int sock, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
- sockaddr_t* addr, knot_nameserver_t *ns);
+ sockaddr_t* addr, knot_nameserver_t *ns,
+ rrl_table_t *rrl, unsigned *slip);
/*!
* \brief UDP handler thread runnable.
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index 6a9a249..8e403c0 100644
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -17,9 +17,10 @@
#include <config.h>
#include <unistd.h>
#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
-#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index 935f3a4..a06484d 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -2527,6 +2527,7 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
int ret = knot_ns_prep_normal_response(nameserver, query, &resp, &zone,
(transport == NS_TRANSPORT_TCP)
? *rsize : 0);
+ query->zone = zone;
// check for TSIG in the query
// not required, TSIG is already found if it is there
@@ -2638,6 +2639,7 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
&answer_size,
transport ==
NS_TRANSPORT_UDP);
+ query->flags = resp->flags; /* Copy markers. */
}
dbg_zones_detail("rsize = %zu\n", *rsize);
diff --git a/src/knot/stat/stat.c b/src/knot/stat/stat.c
index a473085..01bbbc4 100644
--- a/src/knot/stat/stat.c
+++ b/src/knot/stat/stat.c
@@ -19,8 +19,9 @@
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
-#include <arpa/inet.h>
+#include <sys/types.h>
#include <sys/socket.h>
+#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
diff --git a/src/knot/stat/stat.h b/src/knot/stat/stat.h
index 0cf1454..1780c3f 100644
--- a/src/knot/stat/stat.h
+++ b/src/knot/stat/stat.h
@@ -30,8 +30,9 @@
#include <time.h>
#include <stdbool.h>
#include <pthread.h>
-#include <arpa/inet.h>
+#include <sys/types.h>
#include <sys/socket.h>
+#include <arpa/inet.h>
#include <netinet/in.h>
#include "knot/stat/gatherer.h"
diff --git a/src/knot/zone/zone-dump-text.c b/src/knot/zone/zone-dump-text.c
index f231a70..0a1dcfd 100644
--- a/src/knot/zone/zone-dump-text.c
+++ b/src/knot/zone/zone-dump-text.c
@@ -649,6 +649,7 @@ char *rdata_nsap_to_string(knot_rdata_item_t item)
char *converted = knot_hex_to_string(rdata_item_data(item),
rdata_item_size(item));
if (converted == NULL) {
+ free(ret);
return NULL;
}
diff --git a/src/knotc.8 b/src/knotc.8
index ba345ce..039a994 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-rc2"
+.TH knotc "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc3"
.SH NAME
.B knotc
\- Knot DNS control utility
diff --git a/src/knotd.8 b/src/knotd.8
index 4c96cff..22d0dac 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-rc2"
+.TH "knotd" "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc3"
.SH NAME
.B knotd
\- Knot DNS daemon
diff --git a/src/libknot/hash/cuckoo-hash-table.c b/src/libknot/hash/cuckoo-hash-table.c
index 7d454a9..a2a6d3f 100644
--- a/src/libknot/hash/cuckoo-hash-table.c
+++ b/src/libknot/hash/cuckoo-hash-table.c
@@ -96,7 +96,9 @@ static const uint8_t FLAG_REHASH = 0x4; // 00000100
/*! \brief Clears the table / item flags. */
static inline void CLEAR_FLAGS(uint8_t *flags)
{
- *flags = (uint8_t)0x0;
+ if (flags) {
+ *flags = (uint8_t)0x0;
+ }
}
/*! \brief Returns the generation stored in the flags. */
@@ -120,7 +122,9 @@ static inline int IS_GENERATION1(uint8_t flags)
/*! \brief Sets the generation stored in the flags to 1. */
static inline void SET_GENERATION1(uint8_t *flags)
{
- *flags = ((*flags) & ~FLAG_GENERATION2) | FLAG_GENERATION1;
+ if (flags) {
+ *flags = ((*flags) & ~FLAG_GENERATION2) | FLAG_GENERATION1;
+ }
}
/*! \brief Checks if the generation stored in the flags is 2. */
@@ -132,19 +136,26 @@ static inline int IS_GENERATION2(uint8_t flags)
/*! \brief Sets the generation stored in the flags to 2. */
static inline void SET_GENERATION2(uint8_t *flags)
{
- *flags = ((*flags) & ~FLAG_GENERATION1) | FLAG_GENERATION2;
+ if (flags) {
+ *flags = ((*flags) & ~FLAG_GENERATION1) | FLAG_GENERATION2;
+ }
}
/*! \brief Sets the generation stored in the flags to the given generation. */
static inline void SET_GENERATION(uint8_t *flags, uint8_t generation)
{
- *flags = ((*flags) & ~FLAG_GENERATION_BOTH) | generation;
+ if (flags) {
+ *flags = ((*flags) & ~FLAG_GENERATION_BOTH) | generation;
+ }
}
/*! \brief Sets the generation stored in the flags to the next one (cyclic). */
static inline uint8_t SET_NEXT_GENERATION(uint8_t *flags)
{
- return ((*flags) ^= FLAG_GENERATION_BOTH);
+ if (flags) {
+ return ((*flags) ^= FLAG_GENERATION_BOTH);
+ }
+ return 0;
}
/*! \brief Returns the next generation to the one stored in flags (cyclic). */
@@ -156,13 +167,17 @@ static inline uint8_t NEXT_GENERATION(uint8_t flags)
/*! \brief Sets the rehashing flag to the flags. */
static inline void SET_REHASHING_ON(uint8_t *flags)
{
- *flags = (*flags | FLAG_REHASH);
+ if (flags) {
+ *flags = (*flags | FLAG_REHASH);
+ }
}
/*! \brief Removes the rehashing flag from the flags. */
static inline void SET_REHASHING_OFF(uint8_t *flags)
{
- *flags = (*flags & ~FLAG_REHASH);
+ if (flags) {
+ *flags = (*flags & ~FLAG_REHASH);
+ }
}
/*! \brief Checks if the rehashing flag is set in the flags. */
@@ -1245,7 +1260,7 @@ int ck_shallow_copy(const ck_hash_table_t *from, ck_hash_table_t **to)
// copy the stash - we must explicitly copy each stash item, but do not
// copy the ck_hash_table_item_t within them.
ck_stash_item_t *si = from->stash;
- ck_stash_item_t **pos = &(*to)->stash;
+ ck_stash_item_t *last = NULL;
dbg_ck_verb("Copying hash table stash.\n");
while (si != NULL) {
ck_stash_item_t *si_new = (ck_stash_item_t *)
@@ -1272,8 +1287,13 @@ int ck_shallow_copy(const ck_hash_table_t *from, ck_hash_table_t **to)
si->item->key);
si_new->item = si->item;
- *pos = si_new;
- pos = &si_new->next;
+ si_new->next = NULL;
+ if (last == NULL) {
+ (*to)->stash = si_new;
+ } else {
+ last->next = si_new;
+ }
+ last = si_new;
si = si->next;
dbg_ck_exec_detail(
@@ -1290,8 +1310,6 @@ dbg_ck_exec_detail(
);
}
- *pos = NULL;
-
// there should be no item being hashed right now
/*! \todo This operation should not be done while inserting / rehashing.
*/
@@ -1534,6 +1552,9 @@ int ck_apply(ck_hash_table_t *table,
int ck_rehash(ck_hash_table_t *table)
{
dbg_ck_hash("Rehashing items in table.\n");
+ if (!table) {
+ return -1;
+ }
SET_REHASHING_ON(&table->generation);
ck_stash_item_t *free_stash_items = NULL;
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
index 8238d7e..b81a2c0 100644
--- a/src/libknot/nameserver/name-server.c
+++ b/src/libknot/nameserver/name-server.c
@@ -189,6 +189,7 @@ static int ns_check_wildcard(const knot_dname_t *name, knot_packet_t *resp,
assert(*rrset != NULL);
if (knot_dname_is_wildcard((*rrset)->owner)) {
+ resp->flags |= KNOT_PF_WILDCARD; /* Mark */
knot_rrset_t *synth_rrset =
ns_synth_from_wildcard(*rrset, name);
if (synth_rrset == NULL) {
@@ -254,7 +255,8 @@ static int ns_add_rrsigs(knot_rrset_t *rrset, knot_packet_t *resp,
dbg_ns_detail("RRSIGS: %p\n", knot_rrset_rrsigs(rrset));
if (DNSSEC_ENABLED
- && knot_query_dnssec_requested(knot_packet_query(resp))
+ && (knot_query_dnssec_requested(knot_packet_query(resp))
+ || knot_packet_qtype(resp) == KNOT_RRTYPE_ANY)
&& (rrsigs = knot_rrset_get_rrsigs(rrset)) != NULL) {
if (name != NULL) {
int ret = ns_check_wildcard(name, resp, &rrsigs);
diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c
index 9b7e7c7..b6381c4 100644
--- a/src/libknot/packet/packet.c
+++ b/src/libknot/packet/packet.c
@@ -291,7 +291,6 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
dbg_packet_verb("Parsing dname starting on position %zu and "
"%zu bytes long.\n", *pos, i - *pos + 1);
dbg_packet_verb("Alloc: %d\n", alloc);
- size_t bp = *pos;
if (alloc) {
question->qname = knot_dname_parse_from_wire(wire, pos,
i + 1,
@@ -300,6 +299,7 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
return KNOT_ENOMEM;
}
} else {
+ assert(question->qname != NULL); /* When alloc=0, must be set. */
void *parsed = knot_dname_parse_from_wire(wire, pos,
i + 1,
NULL, question->qname);
@@ -344,6 +344,7 @@ static int knot_packet_realloc_rrsets(const knot_rrset_t ***rrsets,
new_max_count * sizeof(knot_rrset_t *));
CHECK_ALLOC_LOG(new_rrsets, KNOT_ENOMEM);
+ memset(new_rrsets, 0, new_max_count * sizeof(knot_rrset_t *));
memcpy(new_rrsets, *rrsets, (*max_count) * sizeof(knot_rrset_t *));
*rrsets = new_rrsets;
diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h
index 5a95bae..ba85aed 100644
--- a/src/libknot/packet/packet.h
+++ b/src/libknot/packet/packet.h
@@ -34,6 +34,7 @@
#include "rrset.h"
#include "edns.h"
#include "zone/node.h"
+#include "zone/zone.h"
/*----------------------------------------------------------------------------*/
/*!
@@ -163,11 +164,24 @@ struct knot_packet {
size_t tsig_size; /*!< Space to reserve for the TSIG RR. */
knot_rrset_t *tsig_rr; /*!< TSIG RR stored in the packet. */
+ uint16_t flags; /*!< Packet flags. */
+ const knot_zone_t *zone; /*!< Associated zone. */
};
typedef struct knot_packet knot_packet_t;
/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Packet flags.
+ */
+enum {
+ KNOT_PF_NULL = 0 << 0, /*!< No flags. */
+ KNOT_PF_QUERY = 1 << 0, /*!< Packet is query. */
+ KNOT_PF_WILDCARD = 1 << 1, /*!< Query to wildcard name. */
+ KNOT_PF_RESPONSE = 1 << 2 /*!< Packet is response. */
+};
+
/*!
* \brief Default sizes for response structure parts and steps for increasing
* them.
diff --git a/src/libknot/packet/response.c b/src/libknot/packet/response.c
index 476c6b3..69678c7 100644
--- a/src/libknot/packet/response.c
+++ b/src/libknot/packet/response.c
@@ -368,6 +368,9 @@ static int knot_response_compress_dname(const knot_dname_t *dname,
knot_compr_t *compr, uint8_t *dname_wire, size_t max, int compr_cs)
{
int size = 0;
+ if (!dname || !compr || !dname_wire) {
+ return KNOT_EINVAL;
+ }
// try to find the name or one of its ancestors in the compr. table
#ifdef COMPRESSION_PEDANTIC
diff --git a/src/libknot/rdata.c b/src/libknot/rdata.c
index 352bb6c..d59b4e0 100644
--- a/src/libknot/rdata.c
+++ b/src/libknot/rdata.c
@@ -215,6 +215,9 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
int i = 0;
uint8_t item_type;
size_t parsed = 0;
+ if (!rdata || !wire || !pos || !desc) {
+ return KNOT_EINVAL;
+ }
if (rdlength == 0) {
rdata->items = NULL;
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
index 84d5075..f5a9f5f 100644
--- a/src/libknot/rrset.c
+++ b/src/libknot/rrset.c
@@ -346,6 +346,9 @@ knot_rdata_t *knot_rrset_get_rdata(knot_rrset_t *rrset)
knot_rdata_t *knot_rrset_rdata_get_next(knot_rrset_t *rrset,
knot_rdata_t *rdata)
{
+ if (!rdata || !rrset) {
+ return NULL;
+ }
if (rdata->next == rrset->rdata) {
return NULL;
} else {
diff --git a/src/libknot/updates/ddns.c b/src/libknot/updates/ddns.c
index 90a578f..79b28fb 100644
--- a/src/libknot/updates/ddns.c
+++ b/src/libknot/updates/ddns.c
@@ -2206,6 +2206,9 @@ static int knot_ddns_process_rr(const knot_rrset_t *rr,
if (knot_rrset_class(rr) == knot_zone_contents_class(zone)) {
return knot_ddns_process_add(rr, node, zone, changeset,
changes, rr_copy);
+ } else if (node == NULL) {
+ // Removing from non-existing node, just ignore the entry
+ return KNOT_EOK;
} else if (knot_rrset_class(rr) == KNOT_CLASS_NONE) {
return knot_ddns_process_rem_rr(rr, node, zone, changeset,
changes, qclass);
diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c
index bdc268b..90aee0d 100644
--- a/src/libknot/zone/zone-contents.c
+++ b/src/libknot/zone/zone-contents.c
@@ -3158,7 +3158,7 @@ static void knot_zc_integrity_check_parent(const knot_node_t *node,
!= node) {
char *wc = (knot_node_wildcard_child(
check_data->parent) == NULL)
- ? "none"
+ ? strdup("none")
: knot_dname_to_str(knot_node_owner(
knot_node_wildcard_child(
check_data->parent)));
@@ -3167,8 +3167,8 @@ static void knot_zc_integrity_check_parent(const knot_node_t *node,
pname, wc, name);
if (knot_node_wildcard_child(
check_data->parent) != NULL) {
- free(wc);
}
+ free(wc);
++check_data->errors;
}
@@ -3229,6 +3229,9 @@ static int knot_zc_integrity_check_find_dname(const knot_zone_contents_t *zone,
const char *node_name)
{
int ret = 0;
+ if (!zone || !to_find || !node_name) {
+ return KNOT_EINVAL;
+ }
knot_dname_t *found = knot_dname_table_find_dname(zone->dname_table,
(knot_dname_t *)to_find);
diff --git a/src/libknot/zone/zone-tree.c b/src/libknot/zone/zone-tree.c
index ceaa6a9..1d7991e 100644
--- a/src/libknot/zone/zone-tree.c
+++ b/src/libknot/zone/zone-tree.c
@@ -269,7 +269,7 @@ int knot_zone_tree_find_less_or_equal(knot_zone_tree_t *tree,
return KNOT_EINVAL;
}
- knot_node_t *f, *p;
+ knot_node_t *f = NULL, *p = NULL;
int ret = knot_zone_tree_get_less_or_equal(tree, owner, &f, &p);
*found = f;
diff --git a/src/libknot/zone/zone.h b/src/libknot/zone/zone.h
index 31ff2ac..26a5e2a 100644
--- a/src/libknot/zone/zone.h
+++ b/src/libknot/zone/zone.h
@@ -193,7 +193,11 @@ void knot_zone_set_dtor(knot_zone_t *zone, int (*dtor)(struct knot_zone *));
* \param zone Zone.
*/
static inline unsigned knot_zone_flags(knot_zone_t *zone) {
- return zone->flags;
+ if (zone) {
+ return zone->flags;
+ } else {
+ return 0;
+ }
}
/*!
diff --git a/src/tests/common/events_tests.c b/src/tests/common/events_tests.c
index 0acd706..a9b3de6 100644
--- a/src/tests/common/events_tests.c
+++ b/src/tests/common/events_tests.c
@@ -124,7 +124,7 @@ static int events_tests_run(int argc, char *argv[])
ok(s != 0, "evsched: new");
// 2. Schedule event to happen after N ms
- int msecs = 50;
+ int msecs = 200;
struct timeval st, rt;
gettimeofday(&st, 0);
e = evsched_schedule_cb(s, 0, (void*)0xcafe, msecs);
@@ -139,7 +139,7 @@ static int events_tests_run(int argc, char *argv[])
// 4. Check receive time
double passed = (rt.tv_sec - st.tv_sec) * 1000;
passed += (rt.tv_usec - st.tv_usec) / 1000;
- double margin = msecs * 0.2;
+ double margin = msecs * 0.4;
double lb = msecs - margin, ub = msecs + margin;
int in_bounds = (passed >= lb) && (passed <= ub);
ok(in_bounds, "evsched: receive time %.1lfms is in <%.1lf,%.1lf>",
diff --git a/src/tests/common/fdset_tests.c b/src/tests/common/fdset_tests.c
index 08e0577..627829e 100644
--- a/src/tests/common/fdset_tests.c
+++ b/src/tests/common/fdset_tests.c
@@ -83,10 +83,9 @@ void* thr_action(void *arg)
/* Write pattern. */
char pattern = WRITE_PATTERN;
- int ret = write(*fd, &pattern, WRITE_PATTERN_LEN);
- ret = ret; /* Use variable. */
+ (void)write(*fd, &pattern, WRITE_PATTERN_LEN);
- return 0;
+ return NULL;
}
static int fdset_tests_count(int argc, char *argv[])
diff --git a/src/tests/common/slab_tests.c b/src/tests/common/slab_tests.c
index 5724a23..a5de1c2 100644
--- a/src/tests/common/slab_tests.c
+++ b/src/tests/common/slab_tests.c
@@ -48,7 +48,7 @@ unit_api slab_tests_api = {
static int slab_tests_count(int argc, char *argv[])
{
- return 7;
+ return 5;
}
static int slab_tests_run(int argc, char *argv[])
@@ -113,34 +113,5 @@ static int slab_tests_run(int argc, char *argv[])
slab_cache_destroy(&cache);
ok(cache.bufsize == 0, "slab: freed cache");
- // 6. Greate GP allocator
- slab_alloc_t alloc;
- ret = slab_alloc_init(&alloc);
- ok(ret == 0, "slab: created GP allocator");
-
- // 7. Stress allocator
- unsigned ncount = 0;
- ptrs_i = 0;
- for(int i = 0; i < alloc_count; ++i) {
- double roll = rand() / (double) RAND_MAX;
- size_t bsize = roll * 2048;
- bsize = SLAB_MAX(bsize, 8);
- if ((ptrs_i == 0) || (roll < 0.6)) {
- void* m = slab_alloc_alloc(&alloc, bsize);
- if (m == 0) {
- ++ncount;
- } else {
- ptrs[ptrs_i++] = m;
- }
- } else {
- slab_free(ptrs[--ptrs_i]);
- }
- }
-
- cmp_ok(ncount, "==", 0, "slab: GP allocator alloc/free working");
-
- // 7. Destroy allocator
- slab_alloc_destroy(&alloc);
-
return 0;
}
diff --git a/src/tests/knot/dthreads_tests.c b/src/tests/knot/dthreads_tests.c
index 982329b..0b2cb01 100644
--- a/src/tests/knot/dthreads_tests.c
+++ b/src/tests/knot/dthreads_tests.c
@@ -159,8 +159,8 @@ static inline int dt_test_resize(dt_unit_t *unit, int size)
_runnable_i = 0;
for (int i = 0; i < size; ++i) {
ret += dt_repurpose(unit->threads[i], &runnable, 0);
- ret += dt_start_id(unit->threads[i]);
}
+ ret += dt_start(unit);
// Wait for finish
ret += dt_join(unit);
@@ -266,7 +266,7 @@ static int dt_tests_run(int argc, char *argv[])
pthread_mutex_init(&_runnable_mx, NULL);
/* Test 1: Create unit */
- dt_unit_t *unit = dt_test_create(dt_optimal_size());
+ dt_unit_t *unit = dt_test_create(2);
ok(unit != 0, "dthreads: create unit (optimal size %d)", unit->size);
skip(unit == 0, DT_TEST_COUNT - 1);
@@ -316,7 +316,8 @@ static int dt_tests_run(int argc, char *argv[])
"dthreads: result %d is => %d", _runnable_i, expected_lo);
/* Test 12: Compare counter #2. */
- int expected_hi = _runnable_cycles * unit->size;
+ /*! \note repurpose could trigger next run of the unit if both finished */
+ int expected_hi = _runnable_cycles * (unit->size + unit->size - 1);
cmp_ok(_runnable_i, "<=", expected_hi,
"dthreads: result %d is <= %d", _runnable_i, expected_hi);
diff --git a/src/tests/knot/rrl_tests.c b/src/tests/knot/rrl_tests.c
new file mode 100644
index 0000000..35b481e
--- /dev/null
+++ b/src/tests/knot/rrl_tests.c
@@ -0,0 +1,135 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "tests/knot/rrl_tests.h"
+#include "knot/server/rrl.h"
+#include "knot/common.h"
+#include "libknot/packet/response.h"
+#include "libknot/packet/query.h"
+#include "libknot/nameserver/name-server.h"
+
+/* Enable time-dependent tests. */
+//#define ENABLE_TIMED_TESTS
+
+static int rrl_tests_count(int argc, char *argv[]);
+static int rrl_tests_run(int argc, char *argv[]);
+
+/*
+ * Unit API.
+ */
+unit_api rrl_tests_api = {
+ "RRL",
+ &rrl_tests_count,
+ &rrl_tests_run
+};
+
+/*
+ * Unit implementation.
+ */
+
+static int rrl_tests_count(int argc, char *argv[])
+{
+ int c = 6;
+#ifndef ENABLE_TIMED_TESTS
+ c -= 2;
+#endif
+ return c;
+}
+
+static int rrl_tests_run(int argc, char *argv[])
+{
+ /* Prepare query. */
+ knot_question_t qst;
+ qst.qclass = KNOT_CLASS_IN;
+ qst.qtype = KNOT_RRTYPE_A;
+ 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);
+ knot_query_set_question(query, &qst);
+
+ /* Prepare response */
+ knot_nameserver_t *ns = knot_ns_create();
+ uint8_t rbuf[65535];
+ size_t rlen = sizeof(rbuf);
+ memset(rbuf, 0, sizeof(rbuf));
+ knot_ns_error_response_from_query(ns, query, KNOT_RCODE_NOERROR, rbuf, &rlen);
+
+ rrl_req_t rq;
+ rq.w = rbuf;
+ rq.len = rlen;
+ rq.qst = &qst;
+ rq.flags = 0;
+
+ /* 1. create rrl table */
+ rrl_table_t *rrl = rrl_create(101);
+ 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. 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;
+ for (unsigned i = 0; i < rate; ++i) {
+ if (rrl_query(rrl, &addr, &rq, zone) != KNOT_EOK ||
+ rrl_query(rrl, &addr6, &rq, zone) != KNOT_EOK) {
+ ret = KNOT_ELIMIT;
+ break;
+ }
+ }
+ ok(ret == 0, "rrl: unlimited IPv4/v6 requests");
+
+#ifdef ENABLE_TIMED_TESTS
+ /* 4. limited request */
+ ret = rrl_query(rrl, &addr, &rq, zone);
+ ok(ret != 0, "rrl: throttled IPv4 request");
+
+ /* 5. limited IPv6 request */
+ ret = rrl_query(rrl, &addr6, &rq, zone);
+ ok(ret != 0, "rrl: throttled IPv6 request");
+#endif
+
+ /* 6. invalid values. */
+ ret = 0;
+ lives_ok( {
+ rrl_create(0); // NULL
+ ret += rrl_setrate(0, 0); // 0
+ ret += rrl_rate(0); // 0
+ ret += rrl_setlocks(0,0); // -1
+ ret += rrl_query(0, 0, 0, 0); // -1
+ 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");
+
+ knot_dname_release(qst.qname);
+ knot_dname_release(apex);
+ knot_zone_deep_free(&zone, 0);
+ knot_ns_destroy(&ns);
+ knot_packet_free(&query);
+ rrl_destroy(rrl);
+ return 0;
+}
diff --git a/src/tests/knot/rrl_tests.h b/src/tests/knot/rrl_tests.h
new file mode 100644
index 0000000..447b735
--- /dev/null
+++ b/src/tests/knot/rrl_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_RRL_TESTS_H_
+#define _KNOTD_RRL_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api rrl_tests_api;
+
+#endif /* _KNOTD_RRL_TESTS_H_ */
diff --git a/src/tests/unittests_main.c b/src/tests/unittests_main.c
index 17ea3b4..aee4bf9 100644
--- a/src/tests/unittests_main.c
+++ b/src/tests/unittests_main.c
@@ -30,6 +30,7 @@
#include "tests/knot/journal_tests.h"
#include "tests/knot/server_tests.h"
#include "tests/knot/conf_tests.h"
+#include "tests/knot/rrl_tests.h"
// Run all loaded units
int main(int argc, char *argv[])
@@ -56,6 +57,7 @@ int main(int argc, char *argv[])
/* Server parts. */
&conf_tests_api, //! Configuration parser tests
&server_tests_api, //! Server unit
+ &rrl_tests_api, //! RRL tests
NULL
};
diff --git a/src/zcompile/parser-descriptor.c b/src/zcompile/parser-descriptor.c
index bc3ee16..466beb4 100644
--- a/src/zcompile/parser-descriptor.c
+++ b/src/zcompile/parser-descriptor.c
@@ -460,6 +460,9 @@ uint16_t parser_rrtype_from_string(const char *name)
char *end;
long rrtype;
parser_rrtype_descriptor_t *entry;
+ if (!name) {
+ return 0;
+ }
entry = parser_rrtype_descriptor_by_name(name);
if (entry) {
diff --git a/src/zcompile/parser-util.c b/src/zcompile/parser-util.c
index 4cdb4e3..b112ece 100644
--- a/src/zcompile/parser-util.c
+++ b/src/zcompile/parser-util.c
@@ -54,8 +54,9 @@
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
-#include <netinet/in.h>
+#include <sys/types.h>
#include <sys/socket.h>
+#include <netinet/in.h>
#include <netdb.h>
//#include "common.h"