diff options
334 files changed, 20609 insertions, 6204 deletions
diff --git a/ChangeLog b/ChangeLog index e69de29..e69de29 100644..100755 --- a/ChangeLog +++ b/ChangeLog diff --git a/CodingStyle b/CodingStyle index 986417c..986417c 100644..100755 --- a/CodingStyle +++ b/CodingStyle diff --git a/Doxy.page.h b/Doxy.page.h index 4d0704c..9d73024 100644..100755 --- a/Doxy.page.h +++ b/Doxy.page.h @@ -3,68 +3,65 @@ \defgroup server Server control module. \defgroup threading Threading API. \defgroup network Socket API. +\defgroup config Server configuration. \defgroup query_processing DNS query processing. \defgroup utils Utilities, constants and macros. \defgroup debugging Server debugging API. \defgroup logging Server logging API. \defgroup statistics Statistics module (optional). -\defgroup dnslib dnslib - Generic DNS library. +\defgroup libknot libknot - library of DNS-related functions \defgroup hashing Hash table and functions. \defgroup common_lib Common library. \defgroup alloc Memory allocation. \defgroup tests Unit tests. \defgroup zoneparser Zone compiler utility \defgroup ctl Control utility +\defgroup zone-load-dump Zone loading and dumping +\defgroup xfr Zone transfers \mainpage Knot API documentation. Knot is an open-source, high-performace, purely authoritative DNS server. -<h2>Requirements</h2> -- liburcu (at least 0.4.5): http://lttng.org/urcu -- automake -- autoconf -- libtool +<h2>Features</h2> -<h2>Installation</h2> -Knot uses autotools to generate makefiles. +Knot DNS supports the following DNS features: +- TCP/UDP protocols +- AXFR - master, slave +- IXFR - master (primary master experimental), slave +- TSIG +- ENDS0 +- DNSSEC, including NSEC3 +- NSID +- Unknown RR types -\todo Add some more info about usage and requirements. +Server features: +- Adding/removing zones on-the-fly +- Reconfiguring server instance on-the-fly +- IPv4 / IPv6 support +- Semantic checks of zones -\code -$ autoreconf -i -$ ./configure -$ make -\endcode - -<h2>Starting the server</h2> +<h2>Compiling and running the server</h2> -When compiled, the following executables are created (in the src/ directory): -- \em knotd - The server -- \em knotc - Control utility -- \em knot-zcompile - Zone compiler -- \em unittests - Unit tests for the server and dnslib -- \em unittests-zcompile - Unit tests for the zone compiler +See the User manual - links to current version are provided in the +<a href="https://git.nic.cz/redmine/projects/knot-dns/wiki">Knot DNS Wiki</a>. -1. Add path to knotd and knot-zcompile executables to PATH - -2. Prepare a configuration file. You may copy and edit the one provided with - the server (\em samples/knot.conf.sample). - -2. Compile zone +Alternatively you can generate the manual from the sources in Info format: \code -$ src/knotc -c path-to-config-file compile +$ make doc \endcode -3. Run the server +or in PDF: + \code -$ src/knotc -c path-to-config-file start +$ make pdf \endcode <h2>Server modules</h2> - \ref server - \ref threading - \ref network +- \ref config - \ref query_processing - \ref utils - \ref debugging @@ -72,17 +69,19 @@ $ src/knotc -c path-to-config-file start - \ref statistics <h2>DNS library</h2> - -- \ref dnslib +- \ref libknot - \ref hashing +- \ref xfr -<h2>Common library</h2> +<h2>Zone processing</h2> +- \ref zoneparser +- \ref zone-load-dump +<h2>Common library</h2> - \ref common_lib - \ref alloc <h2>Other modules</h2> - \ref tests -- \ref zoneparser - \ref ctl */ @@ -31,7 +31,7 @@ PROJECT_NAME = Knot # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.1.0 +PROJECT_NUMBER = 1.1.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. @@ -591,7 +591,11 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = src/ \ +INPUT = src/common \ + src/libknot \ + src/common \ + src/knot \ + src/zcompile \ Doxy.page.h # This tag can be used to specify the character encoding of the source files @@ -622,7 +626,7 @@ RECURSIVE = YES # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. -EXCLUDE = +EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded diff --git a/Doxyfile.devel b/Doxyfile.devel index 4b73045..4b73045 100644..100755 --- a/Doxyfile.devel +++ b/Doxyfile.devel diff --git a/KNOWN_ISSUES b/KNOWN_ISSUES index b10e4a3..fff55a8 100644..100755 --- a/KNOWN_ISSUES +++ b/KNOWN_ISSUES @@ -1,7 +1,7 @@ Features not supported ====================== -Here is a list of the most notable features that are not supported in the +Here is a list of the most notable features that are not supported in the current version of Knot. * Other DNS classes than IN (CH, CS, HS) @@ -10,6 +10,4 @@ current version of Knot. Known bugs ========== -* Slow start with too many zones. -* IXFR is still quite slow with large transfers (more than 50 000 RRs - changed), despite the previous improvements. +* ACL may not always find the best match so it may behave counter-intuitively. diff --git a/Knot.config b/Knot.config index 8cec188..8cec188 100644..100755 --- a/Knot.config +++ b/Knot.config diff --git a/Knot.creator b/Knot.creator index e94cbbd..e94cbbd 100644..100755 --- a/Knot.creator +++ b/Knot.creator diff --git a/Knot.files b/Knot.files index 71a588f..99c6d59 100644..100755 --- a/Knot.files +++ b/Knot.files @@ -7,7 +7,6 @@ configure.ac KNOWN_ISSUES README tests/querytcp.c -src/libknot/Makefile.am src/libknot/libknot.h src/libknot/common.h src/libknot/dname.h @@ -75,14 +74,14 @@ src/common/mempattern.c src/common/mempattern.h src/common/lists.h src/common/lists.c +src/common/heap.h +src/common/heap.c src/common/base32.h src/common/base32.c src/common/print.c src/common/print.h src/common/latency.c src/common/latency.h -src/common/dynamic-array.c -src/common/dynamic-array.h src/common/skip-list.c src/common/skip-list.h src/common/tree.h @@ -181,8 +180,6 @@ src/zcompile/zcompile-error.c src/tests/unittests_main.c src/tests/common/acl_tests.c src/tests/common/acl_tests.h -src/tests/common/da_tests.c -src/tests/common/da_tests.h src/tests/common/events_tests.c src/tests/common/events_tests.h src/tests/common/skiplist_tests.c @@ -254,11 +251,26 @@ src/libknot/tsig.h src/libknot/tsig.c src/libknot/tsig-op.c src/libknot/tsig-op.h -src/libknot/util/conv.c -src/libknot/util/conv.h src/tests/libknot/libknot/tsig_tests.h src/tests/libknot/libknot/tsig_tests.c src/knot/zone/semantic-check.c src/knot/zone/semantic-check.h src/tests/xfr_tests.h src/tests/xfr_tests.c +src/common/base64.c +src/common/base64.h +doc/knot.texi +doc/configuration.texi +doc/indices.texi +doc/installation.texi +doc/introduction.texi +doc/migration.texi +doc/reference.texi +doc/requirements.texi +doc/security.texi +doc/troubleshooting.texi +doc/version.texi +src/libknot/zone/zone-diff.h +src/libknot/zone/zone-diff.c + +doc/running.texi diff --git a/Knot.includes b/Knot.includes index 8184956..8184956 100644..100755 --- a/Knot.includes +++ b/Knot.includes diff --git a/Makefile.am b/Makefile.am index 30ea215..a3ae405 100644..100755 --- a/Makefile.am +++ b/Makefile.am @@ -1,2 +1,2 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src samples +SUBDIRS = src samples doc diff --git a/Makefile.in b/Makefile.in index 3511c13..f8bcf7d 100644..100755 --- a/Makefile.in +++ b/Makefile.in @@ -227,7 +227,7 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src samples +SUBDIRS = src samples doc all: all-recursive .SUFFIXES: @@ -40,7 +40,7 @@ $ sudo dpkg -i liburcu0_0.5.4-1_amd64.deb $ sudo dpkg -i liburcu-dev_0.5.4-1_amd64.deb # Go for the real thing -$ git clone git://git.nic.cz/knot +$ git clone git://git.nic.cz/knot-dns.git $ cd knot $ autoreconf -if $ ./configure @@ -75,6 +75,10 @@ $ autoreconf -if $ ./configure $ make && sudo make install +It is also present in port tree, so you can install it from there. +$ cd /usr/ports/dns/knot +$ sudo make install + Installation on OpenBSD/NetBSD ============================== Also works for OS X, if you don't want to install gcc from ports. @@ -82,6 +86,7 @@ Prerequisites: - liburcu needs patch for absent compiler TLS capability - patch is located in "scripts/urcu-tls-compat.patch" - patch compatible with liburcu-0.6.7 +- liburcu >= 0.7.0 works out of the box $ mkdir liburcu && cd liburcu $ wget "http://lttng.org/files/urcu/userspace-rcu-0.6.7.tar.bz2" @@ -124,6 +129,10 @@ is '-c' that specifies config file for our server. Compiled zones are saved to storage defined in 'storage' variable in configuration. $ knotc -h # see what it can do + +Copy zone to the specified directory and compile. + +$ mkdir -p /tmp/knot-minimal/samples; cp samples/example.com.zone /tmp/knot-minimal/samples/ $ knotc -c myserver.conf compile # compile zone files to binary format Third, lets load server. You can do this by running 'knotd' directly, or with @@ -1,3 +1,66 @@ +v1.1.0 - Aug 31, 2012 +--------------------- + +Bugfixes: + * Syncing journal to zone was not updating the compiled zone database. + +Other improvements: + * Better checks of corrupted zone database. + + +v1.1.0-rc2 - Aug 23, 2012 +------------------------- + +New features: + * Signing SOA with TSIG queries when checking zone version with master. + +Bugfixes: + * Fixed ixfr-from-differences journal generation in case of IPSECKEY + and APL records. + * Fixed possible leak on server shutdown with a pending transfer. + +Other improvements: + * Improved user manual. + + +v1.1.0-rc1 - Aug 17, 2012 +------------------------- + +New features: + * Optionally disable ANY queries for authoritative answers. + * Dropping identical records in zone and incoming transfers. + * Support for '/' in zone names. + * Generating journal from reloaded zone (EXPERIMENTAL). + * Outgoing-only interfaces in configuration file. + * Following DNAME if the synthetized name is in the same zone. + +Bugfixes: + * Crash when zone contained RRSIG signing a CNAME, but did not + contain the CNAME. + * Malformed packets parsing. + * Failed IXFR caused memory leaks. + * Failed IXFR might have resulted in inconsistent zone structures. + * Fixed answering to +dnssec queries when NSEC3 chain is corrupted. + * Fixed answering when transitioning from NSEC3 to NSEC. + * Fixed answering when zone contains multiple NSEC3 chains. + * Handling RRSets with different TTLs - TTL from the first RR is used. + * Synchronization of zone reload and zone transfers. + * Fixed build on NetBSD 5 and FreeBSD. + * Fixed binding to both IPv4 and IPv6 at the same time on special + interfaces. + * Fixed access rights of created files. + * Semantic checks corrupted RDATA domain names which are covered by + wildcard in the same zone. + +Other improvements: + * IXFR-in optimized. + * Many zones loading optimized. + * More detailed log messages (mostly transfer-related). + * Copying Question section to error responses. + * Using zone name from config file as default origin in zone file. + * Additional records are now added to response also from + wildcard-covered names. + v1.0.6 - Jun 13, 2012 --------------------- diff --git a/aclocal.m4 b/aclocal.m4 index 5184c03..5184c03 100644..100755 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for knot 1.0.6. +# Generated by GNU Autoconf 2.68 for knot 1.1.0. # # Report bugs to <knot-dns@labs.nic.cz>. # @@ -570,8 +570,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='knot' PACKAGE_TARNAME='knot' -PACKAGE_VERSION='1.0.6' -PACKAGE_STRING='knot 1.0.6' +PACKAGE_VERSION='1.1.0' +PACKAGE_STRING='knot 1.1.0' PACKAGE_BUGREPORT='knot-dns@labs.nic.cz' PACKAGE_URL='' @@ -749,6 +749,7 @@ enable_ldns enable_debug enable_debuglevel enable_recvmmsg +enable_lto ' ac_precious_vars='build_alias host_alias @@ -1303,7 +1304,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.0.6 to adapt to many kinds of systems. +\`configure' configures knot 1.1.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1373,7 +1374,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of knot 1.0.6:";; + short | recursive ) echo "Configuration of knot 1.1.0:";; esac cat <<\_ACEOF @@ -1391,7 +1392,7 @@ Optional Features: optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --enable-ldns=yes|no Enable tests with ldns [default=no] - --enable-debug=server,zones,xfr,packet,dname,rr,ns,hash,compiler + --enable-debug=server,zones,xfr,packet,dname,rr,ns,hash,compiler,stash compile selected debug modules [default=none] --enable-debuglevel=brief|verbose|details enable given debug level [default=disabled] @@ -1399,6 +1400,8 @@ Optional Features: enable recvmmsg() network API under Linux (kernel support required) (set to 'no' if you have trouble running server under valgrind) [default=yes] + --enable-lto=yes|no enable link-time optimizations, enable if not broken + for some extra speed [default=no] Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1491,7 +1494,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -knot configure 1.0.6 +knot configure 1.1.0 generated by GNU Autoconf 2.68 Copyright (C) 2010 Free Software Foundation, Inc. @@ -2041,7 +2044,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.0.6, which was +It was created by knot $as_me 1.1.0, which was generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -2859,7 +2862,7 @@ fi # Define the identity of the package. PACKAGE='knot' - VERSION='1.0.6' + VERSION='1.1.0' cat >>confdefs.h <<_ACEOF @@ -4769,6 +4772,677 @@ $as_echo "#define HAVE_SSSE3 /**/" >>confdefs.h +# Checks for programs. +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + # for automake 1.12 +AM_PROG_AR + # Enable maintainer mode by default for development { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to disable maintainer-specific portions of Makefiles" >&5 @@ -11944,675 +12618,6 @@ CC="$lt_save_CC" -# Checks for programs. -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <stdarg.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -depcc="$CC" am_compiler_list= - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 -$as_echo_n "checking dependency style of $depcc... " >&6; } -if ${am_cv_CC_dependencies_compiler_type+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then - # We make a subdir and do the tests there. Otherwise we can end up - # making bogus files that we don't know about and never remove. For - # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named `D' -- because `-MD' means `put the output - # in D'. - mkdir conftest.dir - # Copy depcomp to subdir because otherwise we won't find it if we're - # using a relative directory. - cp "$am_depcomp" conftest.dir - cd conftest.dir - # We will build objects and dependencies in a subdirectory because - # it helps to detect inapplicable dependency modes. For instance - # both Tru64's cc and ICC support -MD to output dependencies as a - # side effect of compilation, but ICC will put the dependencies in - # the current directory while Tru64 will put them in the object - # directory. - mkdir sub - - am_cv_CC_dependencies_compiler_type=none - if test "$am_compiler_list" = ""; then - am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` - fi - am__universal=false - case " $depcc " in #( - *\ -arch\ *\ -arch\ *) am__universal=true ;; - esac - - for depmode in $am_compiler_list; do - # Setup a source with many dependencies, because some compilers - # like to wrap large dependency lists on column 80 (with \), and - # we should not choose a depcomp mode which is confused by this. - # - # We need to recreate these files for each test, as the compiler may - # overwrite some of them when testing with obscure command lines. - # This happens at least with the AIX C compiler. - : > sub/conftest.c - for i in 1 2 3 4 5 6; do - echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with - # Solaris 8's {/usr,}/bin/sh. - touch sub/conftst$i.h - done - echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - - # We check with `-c' and `-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle `-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs - am__obj=sub/conftest.${OBJEXT-o} - am__minus_obj="-o $am__obj" - case $depmode in - gcc) - # This depmode causes a compiler race in universal mode. - test "$am__universal" = false || continue - ;; - nosideeffect) - # after this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested - if test "x$enable_dependency_tracking" = xyes; then - continue - else - break - fi - ;; - msvisualcpp | msvcmsys) - # This compiler won't grok `-c -o', but also, the minuso test has - # not run yet. These depmodes are late enough in the game, and - # so weak that their functioning should not be impacted. - am__obj=conftest.${OBJEXT-o} - am__minus_obj= - ;; - none) break ;; - esac - if depmode=$depmode \ - source=sub/conftest.c object=$am__obj \ - depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ - >/dev/null 2>conftest.err && - grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep $am__obj sub/conftest.Po > /dev/null 2>&1 && - ${MAKE-make} -s -f confmf > /dev/null 2>&1; then - # icc doesn't choke on unknown options, it will just issue warnings - # or remarks (even with -Werror). So we grep stderr for any message - # that says an option was ignored or not supported. - # When given -MP, icc 7.0 and 7.1 complain thusly: - # icc: Command line warning: ignoring option '-M'; no argument required - # The diagnosis changed in icc 8.0: - # icc: Command line remark: option '-MP' not supported - if (grep 'ignoring option' conftest.err || - grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else - am_cv_CC_dependencies_compiler_type=$depmode - break - fi - fi - done - - cd .. - rm -rf conftest.dir -else - am_cv_CC_dependencies_compiler_type=none -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 -$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } -CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type - - if - test "x$enable_dependency_tracking" != xno \ - && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then - am__fastdepCC_TRUE= - am__fastdepCC_FALSE='#' -else - am__fastdepCC_TRUE='#' - am__fastdepCC_FALSE= -fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for reentrant lex" >&5 $as_echo_n "checking for reentrant lex... " >&6; } if ${ac_cv_path_LEX+:} false; then : @@ -12883,8 +12888,8 @@ done test -n "$YACC" || YACC="yacc" YACC_BISON=`bison --version | awk '{print $1;exit}'` -if test "$YACC_BISON" != "bison"; then - as_fn_error $? "GNU bison needed for reentrant parsers, set the \$YACC variable before running configure" "$LINENO" 5 +if test "x$YACC_BISON" != "xbison"; then : + as_fn_error $? "GNU bison needed for reentrant parsers, set the \$YACC variable before running configure" "$LINENO" 5 fi @@ -13172,6 +13177,9 @@ $as_echo "#define KNOT_HASH_DEBUG 1" >>confdefs.h compiler) $as_echo "#define KNOT_COMPILER_DEBUG 1" >>confdefs.h ;; + stash) +$as_echo "#define KNOT_STASH_DEBUG 1" >>confdefs.h + ;; esac done @@ -13238,7 +13246,10 @@ fi # Check for link time optimizations support and predictive commoning - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts \"-flto\"" >&5 +# Check whether --enable-lto was given. +if test "${enable_lto+set}" = set; then : + enableval=$enable_lto; case "${enableval}" in + yes) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts \"-flto\"" >&5 $as_echo_n "checking whether C compiler accepts \"-flto\"... " >&6; } ax_save_FLAGS=$CFLAGS CFLAGS=""-flto"" @@ -13268,6 +13279,12 @@ if test "x$ax_check_compiler_flags" = xyes; then else : fi + ;; + no) ;; + *) as_fn_error $? "bad value ${enableval} for --enable-lto" "$LINENO" 5 ;; + esac +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts \"-fpredictive-commoning\"" >&5 $as_echo_n "checking whether C compiler accepts \"-fpredictive-commoning\"... " >&6; } @@ -13760,9 +13777,9 @@ if test "$ac_res" != no; then : fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing crc32" >&5 -$as_echo_n "checking for library containing crc32... " >&6; } -if ${ac_cv_search_crc32+:} false; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing adler32" >&5 +$as_echo_n "checking for library containing adler32... " >&6; } +if ${ac_cv_search_adler32+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS @@ -13775,11 +13792,11 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char crc32 (); +char adler32 (); int main () { -return crc32 (); +return adler32 (); ; return 0; } @@ -13792,25 +13809,25 @@ for ac_lib in '' z; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_crc32=$ac_res + ac_cv_search_adler32=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext - if ${ac_cv_search_crc32+:} false; then : + if ${ac_cv_search_adler32+:} false; then : break fi done -if ${ac_cv_search_crc32+:} false; then : +if ${ac_cv_search_adler32+:} false; then : else - ac_cv_search_crc32=no + ac_cv_search_adler32=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_crc32" >&5 -$as_echo "$ac_cv_search_crc32" >&6; } -ac_res=$ac_cv_search_crc32 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_adler32" >&5 +$as_echo "$ac_cv_search_adler32" >&6; } +ac_res=$ac_cv_search_adler32 if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" @@ -13845,7 +13862,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 cap-ng.h syslog.h unistd.h urcu.h ev.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 cap-ng.h syslog.h unistd.h urcu.h ev.h pthread_np.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" @@ -14526,7 +14543,63 @@ fi done -ac_config_files="$ac_config_files Makefile samples/Makefile src/Makefile" +# Check for cpu_set_t/cpuset_t compatibility +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <pthread.h> +int +main () +{ +cpu_set_t set; CPU_ZERO(&set); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +$as_echo "#define HAVE_CPUSET_LINUX 1" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <pthread_np.h> +int +main () +{ +cpuset_t set; CPU_ZERO(&set); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +$as_echo "#define HAVE_CPUSET_BSD 1" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sched.h> +int +main () +{ +cpuset_t* set = cpuset_create(); cpuset_destroy(set); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +$as_echo "#define HAVE_CPUSET_NETBSD 1" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +ac_config_files="$ac_config_files Makefile samples/Makefile src/Makefile doc/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -14653,14 +14726,14 @@ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then - as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 @@ -15070,7 +15143,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.0.6, which was +This file was extended by knot $as_me 1.1.0, which was generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -15136,7 +15209,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.0.6 +knot config.status 1.1.0 configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" @@ -15548,6 +15621,7 @@ do "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "samples/Makefile") CONFIG_FILES="$CONFIG_FILES samples/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac diff --git a/configure.ac b/configure.ac index 5875517..eeca939 100644..100755 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # -*- Autoconf -*- AC_PREREQ([2.65]) -AC_INIT([knot], [1.0.6], [knot-dns@labs.nic.cz]) +AC_INIT([knot], [1.1.0], [knot-dns@labs.nic.cz]) AM_INIT_AUTOMAKE([gnu -Wall -Werror]) AC_CONFIG_SRCDIR([src/knot/main.c]) AC_CONFIG_HEADERS([src/config.h]) @@ -9,6 +9,11 @@ AC_CONFIG_MACRO_DIR([m4]) AC_USE_SYSTEM_EXTENSIONS([_GNU_SOURCE]) AX_EXT +# Checks for programs. +AC_PROG_CC +m4_pattern_allow([AM_PROG_AR]) # for automake 1.12 +AM_PROG_AR + # Enable maintainer mode by default for development AM_MAINTAINER_MODE([enable]) @@ -16,9 +21,6 @@ AM_MAINTAINER_MODE([enable]) AC_PROG_LIBTOOL LT_INIT -# Checks for programs. -AC_PROG_CC - AC_CACHE_CHECK([for reentrant lex], [ac_cv_path_LEX], [AC_PATH_PROGS_FEATURE_CHECK([LEX], [$LEX flex gflex], [cat >conftest.l <<_ACEOF @@ -47,9 +49,8 @@ AM_PROG_LEX AC_PROG_YACC YACC_BISON=`bison --version | awk '{print $1;exit}'` -if test "$YACC_BISON" != "bison"; then - AC_MSG_ERROR([GNU bison needed for reentrant parsers, set the \$YACC variable before running configure]) -fi +AS_IF([test "x$YACC_BISON" != "xbison"], + [AC_MSG_ERROR([GNU bison needed for reentrant parsers, set the \$YACC variable before running configure])]) AC_PROG_INSTALL # Set compiler compatibility flags @@ -57,7 +58,7 @@ AC_PROG_CPP_WERROR AC_PROG_CC_C99 AC_ARG_ENABLE([ldns], - AC_HELP_STRING([--enable-ldns=yes|no], [Enable tests with ldns [default=no]]), + AS_HELP_STRING([--enable-ldns=yes|no], [Enable tests with ldns [default=no]]), [case "${enableval}" in yes) AC_SEARCH_LIBS([ldns_rr_list_pop_rrset], [ldns], [AC_DEFINE([HAVE_LDNS], [1], [ldns present])], AC_MSG_ERROR([ldns not found])) ;; @@ -67,7 +68,7 @@ AC_ARG_ENABLE([ldns], # Debug modules AC_ARG_ENABLE([debug], - AS_HELP_STRING([--enable-debug=server,zones,xfr,packet,dname,rr,ns,hash,compiler], + AS_HELP_STRING([--enable-debug=server,zones,xfr,packet,dname,rr,ns,hash,compiler,stash], [compile selected debug modules [default=none]]), [ echo ${enableval}|tr "," "\n"|while read val; do @@ -81,6 +82,7 @@ AC_ARG_ENABLE([debug], ns) AC_DEFINE([KNOT_NS_DEBUG], [1], [Nameserver debug.]) ;; hash) AC_DEFINE([KNOT_HASH_DEBUG], [1], [Hashtable debug.]) ;; compiler) AC_DEFINE([KNOT_COMPILER_DEBUG], [1], [Zone compiler debug.]) ;; + stash) AC_DEFINE([KNOT_STASH_DEBUG], [1], [Hash table stash debug.]) ;; esac done ], []) @@ -123,7 +125,15 @@ AC_ARG_ENABLE([recvmmsg], ]) # Check for link time optimizations support and predictive commoning -AX_CHECK_COMPILER_FLAGS("-flto", [CFLAGS="$CFLAGS -flto"], []) +AC_ARG_ENABLE([lto], + AS_HELP_STRING([--enable-lto=yes|no], [enable link-time optimizations, enable if not broken for some extra speed [default=no]]), + [case "${enableval}" in + yes) AX_CHECK_COMPILER_FLAGS("-flto", [CFLAGS="$CFLAGS -flto"], []) ;; + no) ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-lto]) ;; + esac], [ + ]) + AX_CHECK_COMPILER_FLAGS("-fpredictive-commoning", [CFLAGS="$CFLAGS -fpredictive-commoning"], []) # Checks for libraries. @@ -139,12 +149,12 @@ AC_SEARCH_LIBS([dlopen], [dl]) AC_SEARCH_LIBS([clock_gettime], [rt]) AC_SEARCH_LIBS([OpenSSL_add_all_digests], [crypto],[], [AC_MSG_ERROR([libcrypto not found])]) AC_SEARCH_LIBS([capng_apply], [cap-ng]) -AC_SEARCH_LIBS([crc32], [z]) +AC_SEARCH_LIBS([adler32], [z]) #AC_SEARCH_LIBS([ldns_rr_list_pop_rrset], [ldns], [], [AC_MSG_ERROR([libldns not found])]) # 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 cap-ng.h syslog.h unistd.h urcu.h ev.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 cap-ng.h syslog.h unistd.h urcu.h ev.h pthread_np.h]) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL @@ -166,7 +176,16 @@ AC_FUNC_FORK AC_FUNC_MMAP AC_CHECK_FUNCS([gethostbyname gettimeofday clock_gettime memalign memmove memset munmap regcomp pselect select socket sqrt strcasecmp strchr strdup strerror strncasecmp strtol strtoul poll epoll_wait kqueue setgroups sendmmsg madvise pthread_setaffinity_np]) +# Check for cpu_set_t/cpuset_t compatibility +AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], [[cpu_set_t set; CPU_ZERO(&set);]])], +[AC_DEFINE(HAVE_CPUSET_LINUX, 1, [Define if Linux-like cpu_set_t exists.])]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread_np.h>]], [[cpuset_t set; CPU_ZERO(&set);]])], +[AC_DEFINE(HAVE_CPUSET_BSD, 1, [Define if FreeBSD-like cpuset_t exists.])]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sched.h>]], [[cpuset_t* set = cpuset_create(); cpuset_destroy(set);]])], +[AC_DEFINE(HAVE_CPUSET_NETBSD, 1, [Define if cpuset_t and cpuset(3) exists.])]) + AC_CONFIG_FILES([Makefile samples/Makefile - src/Makefile]) + src/Makefile + doc/Makefile]) AC_OUTPUT diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100755 index 0000000..b9dc5b2 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,17 @@ +*.info +*.pdf +*.html +*.dvi +*.aux +*.cp +*.fn +*.ky +*.log +*.pg +*.toc +*.tp +*.vr +texinfo.tex +version.texi +mdate-sh +stamp-vti diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100755 index 0000000..9cec847 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,10 @@ +info_TEXINFOS = knot.texi +knot_TEXINFOS = \ + introduction.texi \ + requirements.texi \ + installation.texi \ + configuration.texi \ + reference.texi \ + security.texi \ + troubleshooting.texi \ + migration.texi diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100755 index 0000000..b45e192 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,664 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = doc +DIST_COMMON = $(knot_TEXINFOS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/stamp-vti \ + $(srcdir)/version.texi mdate-sh texinfo.tex +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \ + $(top_srcdir)/m4/ax_ext.m4 \ + $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +INFO_DEPS = $(srcdir)/knot.info +am__TEXINFO_TEX_DIR = $(srcdir) +DVIS = knot.dvi +PDFS = knot.pdf +PSS = knot.ps +HTMLS = knot.html +TEXINFOS = knot.texi +TEXI2DVI = texi2dvi +TEXI2PDF = $(TEXI2DVI) --pdf --batch +MAKEINFOHTML = $(MAKEINFO) --html +AM_MAKEINFOHTMLFLAGS = $(AM_MAKEINFOFLAGS) +DVIPS = dvips +am__installdirs = "$(DESTDIR)$(infodir)" +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIMD_FLAGS = @SIMD_FLAGS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +info_TEXINFOS = knot.texi +knot_TEXINFOS = \ + introduction.texi \ + requirements.texi \ + installation.texi \ + configuration.texi \ + reference.texi \ + security.texi \ + troubleshooting.texi \ + migration.texi + +all: all-am + +.SUFFIXES: +.SUFFIXES: .dvi .html .info .pdf .ps .texi +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +.texi.info: + restore=: && backupdir="$(am__leading_dot)am$$$$" && \ + am__cwd=`pwd` && $(am__cd) $(srcdir) && \ + rm -rf $$backupdir && mkdir $$backupdir && \ + if ($(MAKEINFO) --version) >/dev/null 2>&1; then \ + for f in $@ $@-[0-9] $@-[0-9][0-9] $(@:.info=).i[0-9] $(@:.info=).i[0-9][0-9]; do \ + if test -f $$f; then mv $$f $$backupdir; restore=mv; else :; fi; \ + done; \ + else :; fi && \ + cd "$$am__cwd"; \ + if $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ + -o $@ $<; \ + then \ + rc=0; \ + $(am__cd) $(srcdir); \ + else \ + rc=$$?; \ + $(am__cd) $(srcdir) && \ + $$restore $$backupdir/* `echo "./$@" | sed 's|[^/]*$$||'`; \ + fi; \ + rm -rf $$backupdir; exit $$rc + +.texi.dvi: + TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ + MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ + $(TEXI2DVI) $< + +.texi.pdf: + TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ + MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ + $(TEXI2PDF) $< + +.texi.html: + rm -rf $(@:.html=.htp) + if $(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ + -o $(@:.html=.htp) $<; \ + then \ + rm -rf $@; \ + if test ! -d $(@:.html=.htp) && test -d $(@:.html=); then \ + mv $(@:.html=) $@; else mv $(@:.html=.htp) $@; fi; \ + else \ + if test ! -d $(@:.html=.htp) && test -d $(@:.html=); then \ + rm -rf $(@:.html=); else rm -Rf $(@:.html=.htp) $@; fi; \ + exit 1; \ + fi +$(srcdir)/knot.info: knot.texi $(srcdir)/version.texi $(knot_TEXINFOS) +knot.dvi: knot.texi $(srcdir)/version.texi $(knot_TEXINFOS) +knot.pdf: knot.texi $(srcdir)/version.texi $(knot_TEXINFOS) +knot.html: knot.texi $(srcdir)/version.texi $(knot_TEXINFOS) +$(srcdir)/version.texi: @MAINTAINER_MODE_TRUE@ $(srcdir)/stamp-vti +$(srcdir)/stamp-vti: knot.texi $(top_srcdir)/configure + @(dir=.; test -f ./knot.texi || dir=$(srcdir); \ + set `$(SHELL) $(srcdir)/mdate-sh $$dir/knot.texi`; \ + echo "@set UPDATED $$1 $$2 $$3"; \ + echo "@set UPDATED-MONTH $$2 $$3"; \ + echo "@set EDITION $(VERSION)"; \ + echo "@set VERSION $(VERSION)") > vti.tmp + @cmp -s vti.tmp $(srcdir)/version.texi \ + || (echo "Updating $(srcdir)/version.texi"; \ + cp vti.tmp $(srcdir)/version.texi) + -@rm -f vti.tmp + @cp $(srcdir)/version.texi $@ + +mostlyclean-vti: + -rm -f vti.tmp + +maintainer-clean-vti: +@MAINTAINER_MODE_TRUE@ -rm -f $(srcdir)/stamp-vti $(srcdir)/version.texi +.dvi.ps: + TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ + $(DVIPS) -o $@ $< + +uninstall-dvi-am: + @$(NORMAL_UNINSTALL) + @list='$(DVIS)'; test -n "$(dvidir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(dvidir)/$$f'"; \ + rm -f "$(DESTDIR)$(dvidir)/$$f"; \ + done + +uninstall-html-am: + @$(NORMAL_UNINSTALL) + @list='$(HTMLS)'; test -n "$(htmldir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " rm -rf '$(DESTDIR)$(htmldir)/$$f'"; \ + rm -rf "$(DESTDIR)$(htmldir)/$$f"; \ + done + +uninstall-info-am: + @$(PRE_UNINSTALL) + @if test -d '$(DESTDIR)$(infodir)' && \ + (install-info --version && \ + install-info --version 2>&1 | sed 1q | grep -i -v debian) >/dev/null 2>&1; then \ + list='$(INFO_DEPS)'; \ + for file in $$list; do \ + relfile=`echo "$$file" | sed 's|^.*/||'`; \ + echo " install-info --info-dir='$(DESTDIR)$(infodir)' --remove '$(DESTDIR)$(infodir)/$$relfile'"; \ + if install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$$relfile"; \ + then :; else test ! -f "$(DESTDIR)$(infodir)/$$relfile" || exit 1; fi; \ + done; \ + else :; fi + @$(NORMAL_UNINSTALL) + @list='$(INFO_DEPS)'; \ + for file in $$list; do \ + relfile=`echo "$$file" | sed 's|^.*/||'`; \ + relfile_i=`echo "$$relfile" | sed 's|\.info$$||;s|$$|.i|'`; \ + (if test -d "$(DESTDIR)$(infodir)" && cd "$(DESTDIR)$(infodir)"; then \ + echo " cd '$(DESTDIR)$(infodir)' && rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]"; \ + rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]; \ + else :; fi); \ + done + +uninstall-pdf-am: + @$(NORMAL_UNINSTALL) + @list='$(PDFS)'; test -n "$(pdfdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(pdfdir)/$$f'"; \ + rm -f "$(DESTDIR)$(pdfdir)/$$f"; \ + done + +uninstall-ps-am: + @$(NORMAL_UNINSTALL) + @list='$(PSS)'; test -n "$(psdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(psdir)/$$f'"; \ + rm -f "$(DESTDIR)$(psdir)/$$f"; \ + done + +dist-info: $(INFO_DEPS) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + list='$(INFO_DEPS)'; \ + for base in $$list; do \ + case $$base in \ + $(srcdir)/*) base=`echo "$$base" | sed "s|^$$srcdirstrip/||"`;; \ + esac; \ + if test -f $$base; then d=.; else d=$(srcdir); fi; \ + base_i=`echo "$$base" | sed 's|\.info$$||;s|$$|.i|'`; \ + for file in $$d/$$base $$d/$$base-[0-9] $$d/$$base-[0-9][0-9] $$d/$$base_i[0-9] $$d/$$base_i[0-9][0-9]; do \ + if test -f $$file; then \ + relfile=`expr "$$file" : "$$d/\(.*\)"`; \ + test -f "$(distdir)/$$relfile" || \ + cp -p $$file "$(distdir)/$$relfile"; \ + else :; fi; \ + done; \ + done + +mostlyclean-aminfo: + -rm -rf knot.aux knot.cp knot.cps knot.fn knot.fns knot.ky knot.kys \ + knot.log knot.pg knot.pgs knot.st knot.sts knot.tmp knot.toc \ + knot.tp knot.tps knot.vr + +clean-aminfo: + -test -z "knot.dvi knot.pdf knot.ps knot.html" \ + || rm -rf knot.dvi knot.pdf knot.ps knot.html + +maintainer-clean-aminfo: + @list='$(INFO_DEPS)'; for i in $$list; do \ + i_i=`echo "$$i" | sed 's|\.info$$||;s|$$|.i|'`; \ + echo " rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]"; \ + rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]; \ + done +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-info +check-am: all-am +check: check-am +all-am: Makefile $(INFO_DEPS) +installdirs: + for dir in "$(DESTDIR)$(infodir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-aminfo clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: $(DVIS) + +html: html-am + +html-am: $(HTMLS) + +info: info-am + +info-am: $(INFO_DEPS) + +install-data-am: install-info-am + +install-dvi: install-dvi-am + +install-dvi-am: $(DVIS) + @$(NORMAL_INSTALL) + test -z "$(dvidir)" || $(MKDIR_P) "$(DESTDIR)$(dvidir)" + @list='$(DVIS)'; test -n "$(dvidir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dvidir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(dvidir)" || exit $$?; \ + done +install-exec-am: + +install-html: install-html-am + +install-html-am: $(HTMLS) + @$(NORMAL_INSTALL) + test -z "$(htmldir)" || $(MKDIR_P) "$(DESTDIR)$(htmldir)" + @list='$(HTMLS)'; list2=; test -n "$(htmldir)" || list=; \ + for p in $$list; do \ + if test -f "$$p" || test -d "$$p"; then d=; else d="$(srcdir)/"; fi; \ + $(am__strip_dir) \ + if test -d "$$d$$p"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)/$$f'"; \ + $(MKDIR_P) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \ + echo " $(INSTALL_DATA) '$$d$$p'/* '$(DESTDIR)$(htmldir)/$$f'"; \ + $(INSTALL_DATA) "$$d$$p"/* "$(DESTDIR)$(htmldir)/$$f" || exit $$?; \ + else \ + list2="$$list2 $$d$$p"; \ + fi; \ + done; \ + test -z "$$list2" || { echo "$$list2" | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \ + done; } +install-info: install-info-am + +install-info-am: $(INFO_DEPS) + @$(NORMAL_INSTALL) + test -z "$(infodir)" || $(MKDIR_P) "$(DESTDIR)$(infodir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + list='$(INFO_DEPS)'; test -n "$(infodir)" || list=; \ + for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + esac; \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + file_i=`echo "$$file" | sed 's|\.info$$||;s|$$|.i|'`; \ + for ifile in $$d/$$file $$d/$$file-[0-9] $$d/$$file-[0-9][0-9] \ + $$d/$$file_i[0-9] $$d/$$file_i[0-9][0-9] ; do \ + if test -f $$ifile; then \ + echo "$$ifile"; \ + else : ; fi; \ + done; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(infodir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(infodir)" || exit $$?; done + @$(POST_INSTALL) + @if (install-info --version && \ + install-info --version 2>&1 | sed 1q | grep -i -v debian) >/dev/null 2>&1; then \ + list='$(INFO_DEPS)'; test -n "$(infodir)" || list=; \ + for file in $$list; do \ + relfile=`echo "$$file" | sed 's|^.*/||'`; \ + echo " install-info --info-dir='$(DESTDIR)$(infodir)' '$(DESTDIR)$(infodir)/$$relfile'";\ + install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$$relfile" || :;\ + done; \ + else : ; fi +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: $(PDFS) + @$(NORMAL_INSTALL) + test -z "$(pdfdir)" || $(MKDIR_P) "$(DESTDIR)$(pdfdir)" + @list='$(PDFS)'; test -n "$(pdfdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pdfdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pdfdir)" || exit $$?; done +install-ps: install-ps-am + +install-ps-am: $(PSS) + @$(NORMAL_INSTALL) + test -z "$(psdir)" || $(MKDIR_P) "$(DESTDIR)$(psdir)" + @list='$(PSS)'; test -n "$(psdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(psdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(psdir)" || exit $$?; done +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-aminfo \ + maintainer-clean-generic maintainer-clean-vti + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-aminfo mostlyclean-generic \ + mostlyclean-libtool mostlyclean-vti + +pdf: pdf-am + +pdf-am: $(PDFS) + +ps: ps-am + +ps-am: $(PSS) + +uninstall-am: uninstall-dvi-am uninstall-html-am uninstall-info-am \ + uninstall-pdf-am uninstall-ps-am + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-aminfo clean-generic \ + clean-libtool dist-info distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-aminfo \ + maintainer-clean-generic maintainer-clean-vti mostlyclean \ + mostlyclean-aminfo mostlyclean-generic mostlyclean-libtool \ + mostlyclean-vti pdf pdf-am ps ps-am uninstall uninstall-am \ + uninstall-dvi-am uninstall-html-am uninstall-info-am \ + uninstall-pdf-am uninstall-ps-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/doc/configuration.texi b/doc/configuration.texi new file mode 100755 index 0000000..8b7a1ef --- /dev/null +++ b/doc/configuration.texi @@ -0,0 +1,196 @@ +@node Knot DNS Configuration, Running Knot DNS, Knot DNS Installation, Top +@chapter Knot DNS Configuration + +In this chapter we provide suggested configurations and explain the meaning of individual configuration options. + +@menu +* Minimal configuration:: +* Slave configuration:: +* Master configuration:: +* Configuring multiple interfaces:: +* Enabling zone semantic checks:: +* Creating IXFR differences from zone file changes:: +@end menu + +@node Minimal configuration +@section Minimal configuration + +The following configuration presents a minimal configuration +file which can be used as a base for your Knot DNS setup. + +@example + +# This is a sample of a minimal configuration file for Knot DNS. +# +# For exhaustive list of all options see samples/knot.full.conf +# in the source directory. +# + +system @{ + storage "/var/lib/knot"; +@} + +interfaces @{ + my_interface @{ address 127.0.0.1@@53; @} + second_int @{ address ::1; @} +@} + +log @{ + syslog @{ any notice, warning, error; @} +@} + +zones @{ + example.com @{ + file "/etc/knot/example.com"; + @} +@} + +@end example + +@page +Now let's go step by step through this minimal configuration file: + +@enumerate + +@item +In @code{system} statement we have configured @code{storage} +directory where Knot DNS will store compiled zone files, +PID file and for slave zone also their journal files. (See @ref{system} and @ref{storage}) + +@item +The @code{interfaces} statement defines interfaces where Knot +DNS will listen for incoming connections. We have defined two +interfaces: one IPv4 called @kbd{my_interface} explicitly listening +on port 53 and second IPv6 called @kbd{second_int} also listening on +port 53, which is the default port for the DNS. See @ref{interfaces}. + +@item +The @code{log} statement defines the log facilities for Knot DNS. +In this example we told Knot DNS to send its log messages with the severities +@code{debug}, @code{warning} and @code{notice} into the syslog. +If you omit this sections, all severities will printed to +either @code{stdout} or @code{stderr}, and the severities +from the @code{warning} and more serious to syslog. You can find all +possible combinations in the @ref{log}. + +@item +The @code{zones} statement is probably the most important one, +because it defines the zones that Knot DNS will serve. In its most simple +form you can define a zone by its name and zone file. +@end enumerate + +@page +@node Slave configuration +@section Slave configuration + +Knot DNS doesn't strictly differ between master and slave zones. +The only requirement is to have @code{xfr-in} @code{zones} statement set for given zone, +thus allowing both incoming XFR from that remote and using it as the +zone master. Note that you need to explicitly allow incoming NOTIFY, otherwise +the daemon would reject them. +Also, you can specify paths, relative to the storage directory. +See @ref{zones} and @ref{storage}. +If the zone file doesn't exist and @code{xfr-in} is set, it will be bootstrapped over AXFR. + +@example +remotes @{ + master @{ address 127.0.0.1@@53; @} +@} +zones @{ + example.com @{ + file "example.com"; # relative to 'storage' + xfr-in master; # define 'master' for this zone + notify-in master; # also allow NOTIFY from 'master' + @} +@} +@end example + +You can also use TSIG for access control. For this, you need to configure a TSIG key +and assign it to a remote. +Supported algorithms for TSIG key are:@* +@code{hmac-md5, hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384, hmac-sha512} +@* +Key secret is written in a base64 encoded format. See @ref{keys}. + +@example +keys @{ + key0 hmac-md5 "Wg=="; # keyname algorithm secret +@} +remotes @{ + master @{ address 127.0.0.1@@53; key key0; @} +@} +zones @{ + example.com @{ + file "example.com"; # relative to 'storage' + xfr-in master; # define 'master' for this zone + notify-in master; # also allow NOTIFY from 'master' + @} +@} +@end example + +As of now it is not possible to associate multiple keys with a remote. + +@page +@node Master configuration +@section Master configuration + +You can specify which remotes to allow for outgoing XFR and NOTIFY @code{zones}. + +@example +remotes @{ + slave @{ address 127.0.0.1@@53; @} + any @{ address 0.0.0.0/0; @} + subnet1 @{ address 192.168.1.0/8; @} + subnet2 @{ address 192.168.2.0/8; @} +@} +zones @{ + example.com @{ + file "/var/zones/example.com"; + xfr-out subnet1, subnet2; # allow outgoing transfers + notify-out slave; + @} +@} +@end example + +You can also secure outgoing XFRs with TSIG. + +@example +keys @{ + key0 hmac-md5 "Wg=="; # keyname algorithm secret +@} +remotes @{ + any @{ address 0.0.0.0/0; key key0; @} +@} +zones @{ + example.com @{ + file "/var/zones/example.com"; + xfr-out any; # uses 'any' remote secured with TSIG key 'key0' + @} +@} +@end example + +@node Configuring multiple interfaces +@section Configuring multiple interfaces + +Knot DNS support binding to multiple available interfaces in the @code{interfaces} section. +@*You can also use the special addresses for "any address" like @code{0.0.0.0} or @code{[::]}. + +@example +interfaces @{ + if1 @{ address 192.168.1.2@@53; @} + anyv6 @{ address [::]@@53; @} +@} +@end example + +@node Enabling zone semantic checks +@section Enabling zone semantic checks +You can turn on more detailed semantic +checks of zone file in this @code{zones} statement (@pxref{zones}). Refer to @ref{zones List of zone semantic checks} to see +which checks are enabled by default and which are optional. + +@node Creating IXFR differences from zone file changes +@section Creating IXFR differences from zone file changes +If Knot is being run as a master server, experimental feature @code{ixfr-from-differences} +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}. + diff --git a/doc/indices.texi b/doc/indices.texi new file mode 100755 index 0000000..f3a1e88 --- /dev/null +++ b/doc/indices.texi @@ -0,0 +1,4 @@ +@node Statement Index, Knot DNS Configuration Reference, Troubleshooting, Top +@unnumbered Statement Index + +@printindex st diff --git a/doc/installation.texi b/doc/installation.texi new file mode 100755 index 0000000..0d88f06 --- /dev/null +++ b/doc/installation.texi @@ -0,0 +1,297 @@ +@node Knot DNS Installation, Knot DNS Configuration, Knot DNS Resource Requirements, Top +@chapter Knot DNS Installation + +@menu +* Required build environment:: +* Required libraries:: +* Installation from the sources:: +* Installation from packages:: +@end menu + +@node Required build environment +@section Required build environment + +GCC at least 4.1 is strictly required for atomic built-ins, but 4.2 or newer is recommended. +Another requirement is @code{_GNU_SOURCE} support, otherwise it adapts to the compiler available features. +Clang should work, but it is not officially supported. + +Knot DNS build system relies on these standard tools: +@itemize +@item +make +@item +libtool +@item +autoconf >= 2.65 +@item +flex >= 2.5.31 +@item +bison >= 2.3 +@end itemize + +@node Required libraries +@section Required libraries + +Knot DNS requires few libraries to be compiled: + +@itemize + +@item +OpenSSL, at least 0.9.8 +@item +zlib +@item +Userspace RCU, at least 0.5.4 +@item +libcap-ng, at least 0.6.4 (optional library) +@end itemize + +If libcap-ng library is available, Knot DNS will take advantage of +the POSIX 1003.1e capabilites(7) by sandboxing the exposed threads. +Most rights are stripped from the exposed threads for security reasons. + +You can probably find OpenSSL and zlib libraries already included in +your system or distribution. If not, zlib resides at +@url{http://zlib.net}, and OpenSSL can be found at +@url{http://www.openssl.org}. + +@menu +* Userspace RCU:: +@end menu + +@node Userspace RCU +@subsection Userspace RCU + +liburcu is a LGPLv2.1 userspace RCU (read-copy-update) +library. This data synchronization library provides read-side +access which scales linearly with the number of cores. It does +so by allowing multiple copies of a given data structure to +live at the same time, and by monitoring the data structure +accesses to detect grace periods after which memory reclamation +is possible. (@url{http://lttng.org/urcu,Userspace RCU}) + +Binary packages for Debian can be found under @code{liburcu1} for the +library and @code{liburcu-dev} for development files. + +Minimum supported version of Userspace RCU library is 0.5.4, +but we recommend using latest available version. +It is crucial especially on non-Linux systems, as we got some compatibility +patches accepted to later releases of Userspace RCU. +OpenBSD, NetBSD and OS X platforms are supported from version 0.7.0. + +@node Installation from the sources +@section Installation from the sources + +You can find the source files for the latest release on @url{www.knot-dns.cz}. +Alternatively, you can fetch the sources from git repository @url{git://git.nic.cz/knot-dns.git} + +After unpacking the sources, the compilation and installation is +a quite straightforward process using autotools. + +@menu +* Configuring and generating Makefiles:: +* Compilation:: +* Installation:: +@end menu + +@node Configuring and generating Makefiles +@subsection Configuring and generating Makefiles + +If you want to compile from Git sources, you need to bootstrap the +@command{./configure} file first. + +@example +$ autoreconf -i -f +@end example + +For all available configure options run: + +@example +$ ./configure --help +@end example + +If you have trouble with unknown syscalls under valgrind, disable recvmmsg by +adding a parameter @command{--enable-recvmmsg=no} to configure. + +Knot DNS has also support for link time optimizations. +You can enable it by the configure parameter @command{./configure --enable-lto=yes}. +It is however disabled by default as it is known to be broken in some compiler +versions and may result in an unexpected behaviour. + +If you want to add debug messages, there are two steps to do that. +First you have to enable modules you are interested in. +Available are: @code{server, zones, xfr, packet, dname, rr, ns, hash, compiler}. +You can combine multiple modules as a comma-separated list. +Then you can narrow the verbosity of the debugging message by specifying the +verbosity as @code{brief, verbose, details}. + +For example: +@example +$ ./configure --enable-debug=server,packet --enable-debuglevel=brief +$ ./configure --enable-debug=server,packet --enable-debuglevel=verbose +@end example + +For more detailed information, see @ref{Debug messages}. + +In most simple case you can just run configure without any options. + +@example +$ ./configure +@end example + +@node Compilation +@subsection Compilation + +After running @command{./configure} you can compile +Knot DNS by running @command{make} command, which will produce binaries +and other related files. + +@example +$ make +@end example + +Knot DNS build process is safe to parallelize +using @command{make -j N}, where N is number of +concurrent processes. Using this option can increase speed of +the compilation. + +For example to use maximum 8 concurrent processes you would use: + +@example +$ make -j 8 +@end example + +@node Installation +@subsection Installation + +When you have finished building the Knot DNS, it's time to +install the binaries and configuration files into the +operation system hierarchy. You can do so by +executing @command{make install} command. When installing as a +non-root user you might have to gain elevated privileges by +switching to root user, e.g. @command{sudo make install} +or @command{su -c 'make install'}. + +@example +$ make install +@end example + +@node Installation from packages +@section Installation from packages + +In addition to providing the packages in .DEB and .RPM format, +the Knot DNS might already be available in your favourite +distribution, or in a ports tree. + +@menu +* Installing Knot DNS packages on Debian:: +* Installing Knot DNS packages on Ubuntu:: +* Installing Knot DNS RPMs on Fedora:: +* Installing Knot DNS from ports on FreeBSD:: +@end menu + +@node Installing Knot DNS packages on Debian +@subsection Installing Knot DNS packages on Debian + +Knot DNS is already available from Debian wheezy upwards. In +addition to the official packages we also provide custom +repository, which can be used by adding: + +@example +deb @url{http://deb.knot-dns.cz/debian/} <codename> main +deb-src @url{http://deb.knot-dns.cz/debian/} <codename> main +@end example + +@noindent +to your @file{/etc/apt/sources.list} or into separate file in +@file{/etc/apt/sources.list.d/}. + +As an example, for Debian squeeze (current stable) the Knot +DNS packages can be added by executing following command as +the root user. + +@example + +$ cat >/etc/apt/sources.list.d/knot.list <<EOF +deb http://deb.knot-dns.cz/debian/ <codename> main +deb-src http://deb.knot-dns.cz/debian/ <codename> main +EOF +$ apt-get update +$ apt-get install knot + +@end example + +@node Installing Knot DNS packages on Ubuntu +@subsection Installing Knot DNS packages on Ubuntu + +Prepackaged version of the Knot DNS can be found in Ubuntu +from version 12.10 (Quantal Quetzal). In addition to the +package included in the main archive, we provide Personal +Package Archive (PPA) as an option to upgrade to last stable +version of the Knot DNS or to install it on older versions of +Ubuntu Linux. + +We typically provide packages for all supported versions of Ubuntu +Linux including 5 year support for +@url{https://wiki.ubuntu.com/LTS,LTS} versions of Ubuntu Linux. At +the time of writing this manual this includes Ubuntu 10.04 LTS, 11.04, +11.10 and 12.04 LTS. + +@menu +* Adding official PPA repository for Knot DNS:: +@end menu + +@node Adding official PPA repository for Knot DNS +@subsubsection Adding official PPA repository for Knot DNS + +To start installing and using software from a Personal +Package Archive, you first need to tell Ubuntu where to find +the PPA. + +@example + +$ sudo add-apt-repository ppa:cz.nic-labs/knot-dns +$ sudo apt-get update +$ sudo apt-get install knot + +@end example + +@noindent +Running this sequence of commands will ensure that you will +install Knot DNS on your system and keep it up-to-date +in the future, when new versions are released. + +@page +@node Installing Knot DNS RPMs on Fedora +@subsection Installing Knot DNS RPMs on Fedora + +There are RPM packages for @code{Knot DNS} available for i386 and amd64 targets. +If you want use the Fedora repository, add a file with the +following lines into @file{/etc/yum.repos.d/} + +@example +[knot] +name=Network.CZ Repository +baseurl=ftp://repo.network.cz/pub/redhat/ +enabled=1 +gpgcheck=0 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-network.cz +@end example + +When you have added a new repository, you can install Knot DNS as a +regular package. + +@example +$ yum install knot +@end example + +@node Installing Knot DNS from ports on FreeBSD +@subsection Installing Knot DNS from ports on FreeBSD + +Knot DNS is in ports tree under @code{dns/knot}. + +@example +$ cd /usr/ports/dns/knot +$ sudo make install +@end example + diff --git a/doc/introduction.texi b/doc/introduction.texi new file mode 100755 index 0000000..4217001 --- /dev/null +++ b/doc/introduction.texi @@ -0,0 +1,70 @@ +@node Introduction, Knot DNS Resource Requirements, Top, Top +@chapter Introduction + +The reader of this document is assumed to know the principles of +Domain Name System. + +@menu +* What is Knot DNS:: +* Knot DNS features:: +* Scope of this document:: +@end menu + +@node What is Knot DNS +@section What is Knot DNS + +Knot DNS is a high-performance open source DNS server. It +implements only authoritative domain name service. Knot DNS +is best suited for use on TLD domains but can reliably serve +any other zones as well. + +Knot DNS benefits from its multi-threaded and mostly lock-free +implementation which allows it to scale well on SMP systems and +operate non-stop even when adding or removing zones. + +@node Knot DNS features +@section Knot DNS features + +Knot DNS supports the following DNS features: + +@itemize +@item TCP/UDP protocols +@item AXFR - master, slave +@item IXFR - master (primary master experimental), slave +@item TSIG +@item ENDS0 +@item DNSSEC, including NSEC3 +@item NSID +@item Unknown RR types +@end itemize + +@* +Server features: + +@itemize +@item Adding/removing zones on-the-fly +@item Reconfiguring server instance on-the-fly +@item IPv4 / IPv6 support +@item Semantic checks of zones +@end itemize + +@* +For more info and downloads see +@url{http://www.knot-dns.cz, www.knot-dns.cz}. + +Git repository: +@url{git://git.nic.cz/knot-dns.git, git://git.nic.cz/knot-dns.git} + +Knot DNS issue tracker: +@url{https://git.nic.cz/redmine/projects/knot-dns, +git.nic.cz/redmine/projects/knot-dns} + +Knot DNS users mailing list: +@url{mailto:knot-dns-users@@lists.nic.cz, knot-dns-users@@lists.nic.cz} + +@node Scope of this document +@section Scope of this document + +This document covers the basic information on installing, +configuring and troubleshooting the Knot DNS server. + diff --git a/doc/knot.texi b/doc/knot.texi new file mode 100755 index 0000000..b0929ae --- /dev/null +++ b/doc/knot.texi @@ -0,0 +1,229 @@ +\input texinfo @c -*-texinfo-*- +@setfilename knot.info +@include version.texi +@documentencoding utf-8 +@settitle Knot DNS @value{VERSION} + +@paragraphindent 0 + +@defindex st +@syncodeindex vr st + +@copying +This manual is for Knot DNS (version @value{VERSION}, @value{UPDATED}), +which is a high-performance authoritative-only DNS server. + +Copyright @copyright{} 2012 CZ.NIC, z.s.p.o. + +@quotation +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/>. +@end quotation +@end copying + +@dircategory Internet-application/server +@direntry +* Knot DNS: (Knot DNS) An authoritative-only DNS server +@end direntry + +@titlepage +@title Knot DNS Reference Manual +@subtitle for version @value{VERSION}, @value{UPDATED} +@author Jan Kadlec (@email{jan.kadlec@@nic.cz}) +@author Lubos Slovak (@email{lubos.slovak@@nic.cz}) +@author Ondrej Sury (@email{ondrej@@sury.org}) +@author Marek Vavrusa (@email{marek.vavrusa@@nic.cz}) +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@contents + +@ifnottex +@node Top, Introduction, (dir), (dir) +@top Knot DNS + +This manual is for Knot DNS (version @value{VERSION}, @value{UPDATED}). +@end ifnottex + +@menu +* Introduction:: +* Knot DNS Resource Requirements:: +* Knot DNS Installation:: +* Knot DNS Configuration:: +* Running Knot DNS:: +* Troubleshooting:: +* Statement Index:: +* Knot DNS Configuration Reference:: + +@detailmenu + --- The Detailed Node Listing --- + +Introduction + +* What is Knot DNS:: +* Knot DNS features:: +* Scope of this document:: + +Knot DNS Resource Requirements + +* Hardware requirements:: +* CPU requirements:: +* Memory requirements:: +* Supported operating system:: + +Knot DNS Installation + +* Required build environment:: +* Required libraries:: +* Installation from the sources:: +* Installation from packages:: + +Required libraries + +* Userspace RCU:: + +Installation from the sources + +* Configuring and generating Makefiles:: +* Compilation:: +* Installation:: + +Installation from packages + +* Installing Knot DNS packages on Debian:: +* Installing Knot DNS packages on Ubuntu:: +* Installing Knot DNS RPMs on Fedora:: +* Installing Knot DNS from ports on FreeBSD:: + +Installing Knot DNS packages on Ubuntu + +* Adding official PPA repository for Knot DNS:: + +Knot DNS Configuration + +* Minimal configuration:: +* Slave configuration:: +* Master configuration:: +* Configuring multiple interfaces:: + +Sample Configurations + +* Minimal configuration:: +* Slave configuration:: +* Master configuration:: +* Configuring multiple interfaces:: +* Enabling zone semantic checks:: +* Creating IXFR differences from zone file changes:: + +Running Knot DNS + +* Running a slave server:: +* Running a master server:: +* Controlling running daemon:: + +Troubleshooting + +* Submitting a bugreport:: +* Generating backtrace:: +* Debug messages:: + +Debug messages + +* Enabling debug messages in server:: + +Enabling debug messages in server + +* Example:: + +Knot DNS Configuration Reference + +* system:: +* keys:: +* interfaces:: +* remotes:: +* zones:: +* log:: + +@code{system} Statement + +* system Syntax:: +* system Statement Definition and Usage:: +* system Example:: + +Statement Definition and Usage + +* identity:: +* version:: +* nsid:: +* storage:: +* pidfile:: +* workers:: +* user:: + +@code{keys} Statement + +* keys Syntax:: +* keys Statement Definition and Usage:: +* Example:: + +Statement Definition and Usage + +* key_id:: + +interfaces + +* interfaces Syntax:: +* interfaces Statement Definition and Usage:: +* interfaces Examples:: + +Statement Definition and Usage + +* interface_id:: + +@code{remotes} Statement + +* remotes Syntax:: +* remotes Statement Definition and Grammar:: + +@code{zones} Statement + +* zones Syntax:: +* zones Statement Definition and Grammar:: +* zones List of zone semantic checks:: + +@code{log} Statement + +* log Syntax:: +* log Statement Definition and Grammar:: + +@end detailmenu +@end menu + +@c main chapters + +@include introduction.texi +@include requirements.texi +@include installation.texi +@include configuration.texi +@include running.texi +@include troubleshooting.texi + +@c indices +@include indices.texi + +@c appendixes +@include reference.texi + +@bye diff --git a/doc/mdate-sh b/doc/mdate-sh new file mode 100755 index 0000000..e631b22 --- /dev/null +++ b/doc/mdate-sh @@ -0,0 +1,205 @@ +#!/bin/sh +# Get modification time of a file or directory and pretty-print it. + +scriptversion=2009-04-28.21; # UTC + +# Copyright (C) 1995, 1996, 1997, 2003, 2004, 2005, 2007, 2009 Free +# Software Foundation, Inc. +# written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, June 1995 +# +# 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 2, 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/>. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to <bug-automake@gnu.org> or send patches to +# <automake-patches@gnu.org>. + +case $1 in + '') + echo "$0: No file. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: mdate-sh [--help] [--version] FILE + +Pretty-print the modification time of FILE. + +Report bugs to <bug-automake@gnu.org>. +EOF + exit $? + ;; + -v | --v*) + echo "mdate-sh $scriptversion" + exit $? + ;; +esac + +# Prevent date giving response in another language. +LANG=C +export LANG +LC_ALL=C +export LC_ALL +LC_TIME=C +export LC_TIME + +# GNU ls changes its time format in response to the TIME_STYLE +# variable. Since we cannot assume `unset' works, revert this +# variable to its documented default. +if test "${TIME_STYLE+set}" = set; then + TIME_STYLE=posix-long-iso + export TIME_STYLE +fi + +save_arg1=$1 + +# Find out how to get the extended ls output of a file or directory. +if ls -L /dev/null 1>/dev/null 2>&1; then + ls_command='ls -L -l -d' +else + ls_command='ls -l -d' +fi +# Avoid user/group names that might have spaces, when possible. +if ls -n /dev/null 1>/dev/null 2>&1; then + ls_command="$ls_command -n" +fi + +# A `ls -l' line looks as follows on OS/2. +# drwxrwx--- 0 Aug 11 2001 foo +# This differs from Unix, which adds ownership information. +# drwxrwx--- 2 root root 4096 Aug 11 2001 foo +# +# To find the date, we split the line on spaces and iterate on words +# until we find a month. This cannot work with files whose owner is a +# user named `Jan', or `Feb', etc. However, it's unlikely that `/' +# will be owned by a user whose name is a month. So we first look at +# the extended ls output of the root directory to decide how many +# words should be skipped to get the date. + +# On HPUX /bin/sh, "set" interprets "-rw-r--r--" as options, so the "x" below. +set x`$ls_command /` + +# Find which argument is the month. +month= +command= +until test $month +do + shift + # Add another shift to the command. + command="$command shift;" + case $1 in + Jan) month=January; nummonth=1;; + Feb) month=February; nummonth=2;; + Mar) month=March; nummonth=3;; + Apr) month=April; nummonth=4;; + May) month=May; nummonth=5;; + Jun) month=June; nummonth=6;; + Jul) month=July; nummonth=7;; + Aug) month=August; nummonth=8;; + Sep) month=September; nummonth=9;; + Oct) month=October; nummonth=10;; + Nov) month=November; nummonth=11;; + Dec) month=December; nummonth=12;; + esac +done + +# Get the extended ls output of the file or directory. +set dummy x`eval "$ls_command \"\$save_arg1\""` + +# Remove all preceding arguments +eval $command + +# Because of the dummy argument above, month is in $2. +# +# On a POSIX system, we should have +# +# $# = 5 +# $1 = file size +# $2 = month +# $3 = day +# $4 = year or time +# $5 = filename +# +# On Darwin 7.7.0 and 7.6.0, we have +# +# $# = 4 +# $1 = day +# $2 = month +# $3 = year or time +# $4 = filename + +# Get the month. +case $2 in + Jan) month=January; nummonth=1;; + Feb) month=February; nummonth=2;; + Mar) month=March; nummonth=3;; + Apr) month=April; nummonth=4;; + May) month=May; nummonth=5;; + Jun) month=June; nummonth=6;; + Jul) month=July; nummonth=7;; + Aug) month=August; nummonth=8;; + Sep) month=September; nummonth=9;; + Oct) month=October; nummonth=10;; + Nov) month=November; nummonth=11;; + Dec) month=December; nummonth=12;; +esac + +case $3 in + ???*) day=$1;; + *) day=$3; shift;; +esac + +# Here we have to deal with the problem that the ls output gives either +# the time of day or the year. +case $3 in + *:*) set `date`; eval year=\$$# + case $2 in + Jan) nummonthtod=1;; + Feb) nummonthtod=2;; + Mar) nummonthtod=3;; + Apr) nummonthtod=4;; + May) nummonthtod=5;; + Jun) nummonthtod=6;; + Jul) nummonthtod=7;; + Aug) nummonthtod=8;; + Sep) nummonthtod=9;; + Oct) nummonthtod=10;; + Nov) nummonthtod=11;; + Dec) nummonthtod=12;; + esac + # For the first six month of the year the time notation can also + # be used for files modified in the last year. + if (expr $nummonth \> $nummonthtod) > /dev/null; + then + year=`expr $year - 1` + fi;; + *) year=$3;; +esac + +# The result. +echo $day $month $year + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/doc/migration.texi b/doc/migration.texi new file mode 100755 index 0000000..9d4e0fa --- /dev/null +++ b/doc/migration.texi @@ -0,0 +1,29 @@ +@node Migration for other DNS servers, , Knot DNS Configuration Reference, Top +@appendix Migration for other DNS servers + +@menu +* Knot DNS for BIND users:: +* Knot DNS for NSD users:: +* Knot DNS for PowerDNS users:: +* Knot DNS for djbdns users:: +@end menu + +@node Knot DNS for BIND users +@appendixsec Knot DNS for BIND users + +[TODO] + +@node Knot DNS for NSD users +@appendixsec Knot DNS for NSD users + +[TODO] + +@node Knot DNS for PowerDNS users +@appendixsec Knot DNS for PowerDNS users + +[TODO] + +@node Knot DNS for djbdns users +@appendixsec Knot DNS for djbdns users + +[TODO] diff --git a/doc/reference.texi b/doc/reference.texi new file mode 100755 index 0000000..9dffd00 --- /dev/null +++ b/doc/reference.texi @@ -0,0 +1,774 @@ +@node Knot DNS Configuration Reference, , Statement Index, Top +@appendix Knot DNS Configuration Reference + +This reference describes every configuration option in Knot DNS server. + +@menu +* system:: +* keys:: +* interfaces:: +* remotes:: +* zones:: +* log:: +@end menu + +@node system +@section @code{system} Statement +@stindex system + +The @code{system} statement contains general options related to the +operating system and other general options which do not fit anywhere +else. + +@menu +* system Syntax:: +* system Statement Definition and Usage:: +* system Example:: +@end menu + +@node system Syntax +@subsection @code{system} Syntax + +@example +@code{system} @code{@{} + [ @code{identity} @code{"}@kbd{string}@code{";} ] + [ @code{version} @code{"}@kbd{string}@code{";} ] + [ @code{nsid} ( @code{"}@kbd{string}@code{"} | @kbd{hex_string} )@code{;} ] + [ @code{storage} @code{"}@kbd{string}@code{";} ] + [ @code{pidfile} @code{"}@kbd{string}@code{";} ] + [ @code{workers} @kbd{integer}@code{;} ] + [ @code{user} @kbd{string}[@code{.}@kbd{string}]@code{;} ] +@code{@}} +@end example + +@node system Statement Definition and Usage +@subsection Statement Definition and Usage + +@menu +* identity:: +* version:: +* nsid:: +* storage:: +* pidfile:: +* workers:: +* user:: +@end menu + +@node identity +@subsubsection identity +@vindex identity + +Identity of the server (see @url{http://tools.ietf.org/html/rfc4892,RFC 4892}). Not used yet. + +@example +system @{ + identity "Knot DNS"; +@} +@end example + +@node version +@subsubsection version +@vindex version + +Version of the server (see @url{http://tools.ietf.org/html/rfc4892,RFC 4892}). Not used yet. + +@example +system @{ + version "1.1.0"; +@} +@end example + +@node nsid +@subsubsection nsid +@vindex nsid + +DNS Name Server Identifier (see @url{http://tools.ietf.org/html/rfc5001,RFC 5001}). + +Use a string format "text" or a hexstring (e.g. 0x01ab00) + +@example +system @{ + nsid 0x00cafe; +@} +@end example + +@node storage +@subsubsection storage +@vindex storage + +The working directory of Knot DNS, it is used to store compiled zone files and it is also a default location of the PID file. + +@example +system @{ + storage "/var/lib/knot"; +@} +@end example + +@node pidfile +@subsubsection pidfile +@vindex pidfile + +Specifies a custom PID file location. + +Default value: @file{knot.pid} in @code{storage} directory. + +@example +system @{ + pidfile "/var/run/knot.pid"; +@} +@end example + +@node workers +@subsubsection workers +@vindex workers + +Number of workers (threads) per server interface. This option is used to +force number of threads used per interface. + +Default value: unset (auto-estimates optimal value from the number of online CPUs) + +@example +system @{ + workers 16; +@} +@end example + +@node user +@subsubsection user +@vindex user + +System @kbd{user} or @kbd{user}.@kbd{group} under which the Knot DNS +is run after starting and binding to interfaces. +Linux capabilities (@pxref{Required libraries}) are employed if supported +and this configuration option is set. + +Default value: @kbd{root.root} + +@example +system @{ + user knot.knot; +@} +@end example + +@node system Example +@subsection system Example + +@example +system @{ + identity "Knot DNS @value{VERSION}"; + version "@value{VERSION}"; + nsid "amaterasu"; + storage "/var/lib/knot"; + pidfile "/var/run/knot.pid"; + workers 16; + user knot.knot; +@} +@end example + +@node keys +@section @code{keys} Statement +@stindex keys + +The @code{keys} statement sets up the TSIG keys used to authenticate +zone transfers. + +@menu +* keys Syntax:: +* keys Statement Definition and Usage:: +* Example:: +@end menu + +@node keys Syntax +@subsection keys Syntax + +@example +keys @{ + key_id algorithm "string"; ] + [ key_id algorithm "string"; ... ] +@} +@end example + +@node keys Statement Definition and Usage +@subsection Statement Definition and Usage + +@menu +* key_id:: +@end menu + +@node key_id +@subsubsection @code{key_id} Statement +@vindex key_id + +The @kbd{key_id} statement defines a secret shared key for use with +TSIG. It consists of its @kbd{name}, @kbd{algorithm} and @kbd{key} contents. + +Supported algoritms: + +@itemize +@item +hmac-md5 +@item +hmac-sha1 +@item +hmac-sha224 +@item +hmac-sha256 +@item +hmac-sha384 +@item +hmac-sha512 +@end itemize + +You need to use bind or ldns utils to generate TSIG keys. Unfortunately, Knot DNS does not have any own generation utilities yet. + +@example + +$ dnssec-keygen -a HMAC-SHA256 -b 256 -n HOST foobar.example.com +Kfoobar.example.com.+163+21239 +$ cat Kfoobar.example.com.+163+21239.key +foobar.example.com. ( IN KEY 512 3 163 + rqv2WRyDgIUaHcJi03Zssor9jtG1kOpb3dPywxZfTeo= ) + +@end example + +Key generated in previous paragraph would be written as: + +@example + +keys @{ + foobar.example.com. hmac-sha256 + "rqv2WRyDgIUaHcJi03Zssor9jtG1kOpb3dPywxZfTeo="; +@} + +@end example + +@node Example +@subsection keys Example + + +@example + +keys @{ + key0.server0 hmac-md5 "Wg=="; + foobar.example.com. hmac-sha256 "RQ=="; +@} + +@end example + +@node interfaces +@section interfaces +@stindex interfaces + +The @code{interfaces} statement contains IP interfaces where Knot DNS listens for incoming queries. + +@menu +* interfaces Syntax:: +* interfaces Statement Definition and Usage:: +* interfaces Examples:: +@end menu + +@node interfaces Syntax +@subsection Syntax + +@example +@code{interfaces} @code{@{} + @kbd{interface_id} + ( @kbd{ip_address}[@@@kbd{port_number}] | + @code{@{} @code{address} @kbd{ip_address}@code{;} [ @code{port} @kbd{port_number}@code{;} ] @code{@}} ) + [ @kbd{interface_id ...}@code{;} @kbd{...}@code{;} ] +@code{@}} +@end example + +@node interfaces Statement Definition and Usage +@subsection Statement Definition and Usage + +@menu +* interface_id:: +@end menu + +@node interface_id +@subsubsection @kbd{interface_id} +@vindex interface_id + +The @kbd{interface_id} is a textual identifier of an IP interface, +which consists of an IP address and a port. + +The definition of an interface can be written in long or a short form and +it always contains IP (IPv4 or IPv6) address. + +@node interfaces Examples +@subsection interfaces Examples + +Long form: + +@example + +interfaces @{ + my_ip @{ + address 192.0.2.1; + port 53; + @} +@} + +@end example + +Short form: + +@example + +interfaces @{ + my_second_ip @{ address 198.51.100.1@@53; @} +@} + +@end example + +Short form without port (defaults to 53): + +@example + +interfaces @{ + my_third_ip @{ address 203.0.113.1; @} +@} + +@end example + +@node remotes +@section @code{remotes} Statement +@stindex remotes + +The @code{remotes} statement sets up all remote servers for zone +transfers. Knot DNS does not distinguish between client or server in +this section. Role of the server is determined at the time of its +usage in the @code{zones} section. One server may act as a +client for one zone (e.g. downloading the updates) and as a master +server for a different zone. + +@menu +* remotes Syntax:: +* remotes Statement Definition and Grammar:: +* remotes Examples:: +@end menu + +@node remotes Syntax +@subsection Syntax + +@example +@code{remotes} @code{@{} + @kbd{remote_id} + ( @kbd{ip_address}[@code{@@}@kbd{port_number}] | + @code{@{} @code{address} @kbd{ip_address}; + [ @code{port} @kbd{port_number}; ] + [ @code{key} @kbd{key_id}; ] + [ @code{via} [ @kbd{interface_id} | @kbd{ip_address} ]; ] + @code{@}} + ) + [ @kbd{remote_id} @dots{}; @dots{}; ] +@code{@}} +@end example + +@node remotes Statement Definition and Grammar +@subsection Statement Definition and Grammar + +@menu +* remote_id:: +* address:: +* port:: +* key:: +* via:: +@end menu + +@node remote_id +@subsubsection @kbd{remote_id} +@vindex remote_id + +@kbd{remote_id} contains a symbolic name for a remote server. + +@node address +@subsubsection address +@vindex address + +@kbd{address} sets an IPv4 or an IPv6 address for this particular @code{remote}. + +@node port +@subsubsection port +@vindex port + +@code{port} section contains a port number for the current @code{remote}. This section is optional with default port set to 53. + +@node key +@subsubsection key +@vindex key + +@code{key} section contains a key associated with this @code{remote}. This section is optional. + + +@node via +@subsubsection via +@vindex via + +@code{via} section specifies which interface will be used to communicate with this @code{remote}. This section is optional. + +@node remotes Examples +@subsection remotes Examples + +@example + +remotes @{ + + # Format 1: + server0 @{ + address 127.0.0.1; + port 53531; + key key0.server0; + via ipv4; # reference to 'remotes' + # via 82.35.64.59; # direct IPv4 + # via [::cafe]; # direct IPv6 + @} + + # Format 2: + server1 @{ + address 127.0.0.1@@53001; + @} +@} + +@end example + +@node zones +@section @code{zones} Statement + +The @code{zones} statement contains definition of zones served by Knot DNS. + +@menu +* zones Syntax:: +* zones Statement Definition and Grammar:: +* zones Example:: +* zones List of zone semantic checks:: +@end menu + +@node zones Syntax +@subsection Syntax + +@example +@code{zones} @code{@{} + [ @kbd{zone_options} ] + @kbd{zone_id} @code{@{} + @code{file} @code{"}@kbd{string}@code{";} + [ @code{xfr-in} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ] + [ @code{xfr-out} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ] + [ @code{notify-in} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ] + [ @code{notify-out} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ] + [ @kbd{zone_options} ] + @code{@}} +@code{@}} + +@kbd{zone_options} := + [ @code{semantic-checks} @kbd{boolean}@code{;} ] + [ @code{ixfr-from-differences} @kbd{boolean}@code{;} ] + [ @code{disable-any} @kbd{boolean}@code{;} ] + [ @code{notify-timeout} @kbd{integer}@code{;} ] + [ @code{notify-retries} @kbd{integer}@code{;} ] + [ @code{zonefile-sync} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ] + [ @code{ixfr-fslimit} ( @kbd{integer} | @kbd{integer}(@code{k} | @code{M} | @code{G}) )@code{;} ] + [ @code{ixfr-from-differences} @kbd{boolean}@code{;} ] +@end example + +@node zones Statement Definition and Grammar +@subsection Statement Definition and Grammar + +@menu +* zone_id:: +* file:: +* xfr-in:: +* xfr-out:: +* notify-in:: +* notify-out:: +* semantic-checks:: +* ixfr-from-differences:: +* disable-any:: +* notify-timeout:: +* notify-retries:: +* zonefile-sync:: +* ixfr-fslimit:: +@end menu + +@node zone_id +@subsubsection @kbd{zone_id} +@vindex zone_id + +@code{zone_id} is a zone origin, and as such is a domain name that may or may not end with a ".". +If no $ORIGIN directive is found inside actual zone file, this domain name will be used in place of "@@". +SOA record in the zone must have this name as its owner. + +@node file +@subsubsection file +@vindex file + +The @code{file} statement defines a path to the zone file. +You can either use an absolute path or a relative path. +In that case, the zone file path will be relative to the @code{storage} directory (@pxref{storage}). + +@node xfr-in +@subsubsection xfr-in +@vindex xfr-in + +In @code{xfr-in} statement user specifies which remotes will be permitted to perform a zone transfer to update the zone. +Remotes are defined in @code{remotes} section of configuration file (@pxref{remotes}). + +@node xfr-out +@subsubsection xfr-out +@vindex xfr-out + +In @code{xfr-out} statement user specifies which remotes will be permitted to obtain zone's contents via zone transfer. +Remotes are defined in @code{remotes} section of configuration file (@pxref{remotes}). + +@node notify-in +@subsubsection notify-in +@vindex notify-in + +@code{notify-in} defines which remotes will be permitted to send NOTIFY for this particular zone. + +@node notify-out +@subsubsection notify-out +@vindex notify-out + +@code{notify-out} defines to which remotes will your server send NOTIFYs about this particular zone. + +@node semantic-checks +@subsubsection semantic-checks +@vindex semantic-checks + +@code{semantic-checks} statement turns on optional semantic checks for this particular zone. +See @ref{zones List of zone semantic checks} for more information. Possible values are @code{on} and @code{off}. +Most checks are disabled by default. + +@node ixfr-from-differences +@subsubsection ixfr-from-differences +@vindex ixfr-from-differences + +EXPERIMENTAL: option @code{ixfr-from-differences} is only relevant if you are running Knot DNS as a master for this zone. +By turning the feature on you tell Knot to create differences from changes you made to a zone file upon server reload. +See @ref{Controlling running daemon} for more information. Possible values are @code{on} and @code{off}. Disabled by default. + +@node disable-any +@subsubsection disable-any +@vindex disable-any + +If you enable @code{disable-any}, all authoritative ANY queries sent over UDP will be answered with an empty response and with the TC bit set. +Use to minimize the risk of DNS replay attack. Disabled by default. + +@node notify-timeout +@subsubsection notify-timeout +@vindex notify-timeout + +@code{notify-timeout} in seconds specifies how long will server wait for NOTIFY response. Possible values are 1 to INT_MAX. +By default, this value is set to 60 seconds. + +@node notify-retries +@subsubsection notify-retries +@vindex notify-retries + +@code{notify-retries} tells the server how many times it can retry to send a NOTIFY. Possible values +are 1 to INT_MAX and default value is 5. + +@node zonefile-sync +@subsubsection zonefile-sync +@vindex zonefile-sync + +@code{zonefile-sync} is only relevant in a slave server scenario and only after receiving IXFR. It is a time in seconds after which current zone in memory will be synced to its file on a disk (as set in @ref{file}). Knot DNS will serve the latest zone even after restart, but zone file on a disk will only be synced after @code{zonefile-sync} time has expired. Possible values are 1 to INT_MAX, optionally suffixed by unit size (s/m/h/d) - @emph{1s} is one second, @emph{1m} one minute, @emph{1h} one hour and @emph{1d} one day with default value set to @emph{1h}. + +@node ixfr-fslimit +@subsubsection ixfr-fslimit +@vindex ixfr-fslimit + +@code{ixfr-fslimit} sets a maximum file size for zone's journal in bytes. Possible values are 1 to INT_MAX, with optional suffixes k, m and G. I.e. @emph{1k}, @emph{1m} and @emph{1G} with default value not being set, meaning that journal file can grow without limitations. + +@node zones Example +@subsection zones Example + +@example +@group +zones @{ + + # Shared options for all listed zones + ixfr-from-differences off; + semantic-checks off; + disable-any off; + notify-timeout 60; + notify-retries 5; + zonefile-sync 1h; + ixfr-fslimit 1G; + example.com @{ + file "samples/example.com.zone"; + ixfr-from-differences off; #experimental + disable-any off; + semantic-checks on; + notify-timeout 60; + notify-retries 5; + zonefile-sync 1h; + xfr-in server0; + xfr-out server0, server1; + notify-in server0; + notify-out server0, server1; + @} +@} + +@end group +@end example + +@node zones List of zone semantic checks +@subsection List of zone semantic checks + +The @code{semantic-checks} statement turns on extra zone file semantic +checks. Several checks are enabled by default and cannot be turned +off. If an error is found using these mandatory checks, the zone file +will not be loaded. Upon loading a zone file, occurred +errors and counts of their occurrence will be logged to @emph{stderr}. +These checks are the following: + +@example +- An extra record together with CNAME record (except for RRSIG and DS) +- CNAME link chain length greater than 10 (including infinite cycles) +- DNAME and CNAME records under the same owner (RFC 2672) +- CNAME and DNAME wildcards pointing to themselves +- SOA record missing in the zone (RFC 1034) +- DNAME records having records under it (DNAME children) (RFC 2672) +@end example + +Following checks have to be turned on using @code{semantic-checks} and +a zone containing following errors will be +loaded even upon discovering an error: + +@example +- Missing NS record at the zone apex +- Missing glue A or AAAA records +- Broken or non-cyclic NSEC(3) chain +- Wrong NSEC(3) type bitmap +- Multiple NSEC records at the same node +- Missing NSEC records at authoritative nodes +- Extra record types under same name as NSEC3 record +(this is RFC-valid, but Knot will not serve such a zone correctly) +- NSEC3-unsecured delegation that is not part of Opt-out span +- Wrong original TTL value in NSEC3 records +- Wrong RDATA TTL value in RRSIG record +- Signer name in RRSIG RR not the same as in DNSKEY +- Signed RRSIG +- Not all RRs in node are signed +- Wrong key flags or wrong key in RRSIG record (not the same as ZSK) +@end example + +@node log +@section @code{log} Statement +@stindex log + +@menu +* log Syntax:: +* log Statement Definition and Grammar:: +* log Example:: +@end menu + +@node log Syntax +@subsection Syntax + +@example +@code{log} @code{@{} + [ @kbd{log_name} @code{@{} + [ @kbd{category} @kbd{severity} [ @kbd{severity} @dots{} ]@code{;} ] + @code{@}} ] + [ @code{log_file} @kbd{filename} @{ + [ @kbd{category} @kbd{severity} [ @kbd{severity} @dots{} ]@code{;} ] + @} ] +@code{@}} + +@end example + +@node log Statement Definition and Grammar +@subsection Statement Definition and Grammar + +@menu +* log_name:: +* category:: +* severity:: +* log_file:: +@end menu + +The @code{log} statement configures logging output of Knot DNS. You +can configure Knot DNS to log into file or system log. There are several +logging categories to choose from. Each log +message has its severity and user can configure severities for each +log destination. + +In case of missing log section, severities from @kbd{warning} and more serious +will be logged to both @kbd{stderr} and @kbd{syslog}. The @kbd{info} and @kbd{notice} +severities will be logged to the @kbd{stdout}. + +@node log_name +@subsubsection @kbd{log_name} +@vindex @kbd{log_name} + +@kbd{log_name} should be replaced with one of 3 symbolic log names : +@itemize +@item @emph{stdout} - logging to standard output +@item @emph{stderr} - logging to standard error output +@item @emph{syslog} - logging to syslog +@end itemize + +@node category +@subsubsection @kbd{category} +@vindex category + +Knot DNS allows user to choose from these logging categories: + +@itemize +@item @emph{server} - Messages related to general operation of the server. +@item @emph{zone} - Messages related to zones, zone parsing and loading. +@item @emph{answering} - Messages regarding query processing and response creation. +@item @emph{any} - All categories. +@end itemize + +@node severity +@subsubsection @kbd{severity} +@vindex severity + +Knot DNS has the following logging severities: +@itemize +@item @emph{debug} - Debug messages, must be turned on at compile time (@pxref{Enabling debug messages in server}). +@item @emph{info} - Informational message. +@item @emph{notice} - Server notices and hints. +@item @emph{warning} - Warnings that might require user action. +@item @emph{error} - Recoverable error. Action should be taken. +@item @emph{all} - All severities. +@end itemize + +More severities may be listed for each category, but all severities have to be listed explicitly, i.e. using @emph{warning} severity does not mean that @emph{error} severity messages will be logged as well. + +@node log_file +@subsubsection @kbd{log_file} +@vindex @kbd{log_file} + +@kbd{log_file} is either absolute or relative path to file user wants to log to. +See following example for clarification. + +@node log Example +@subsection log Example + +@example + +log @{ + + syslog @{ + any error; + zone warning, notice; + server info; + @} + + stderr @{ + any error, warning; + @} + + file "/tmp/knot-sample/knotd.debug" @{ + server debug; + @} +@} + +@end example + diff --git a/doc/requirements.texi b/doc/requirements.texi new file mode 100755 index 0000000..136f3c6 --- /dev/null +++ b/doc/requirements.texi @@ -0,0 +1,52 @@ +@node Knot DNS Resource Requirements, Knot DNS Installation, Introduction, Top +@chapter Knot DNS Resource Requirements + +@menu +* Hardware requirements:: +* CPU requirements:: +* Memory requirements:: +* Supported operating system:: +@end menu + +@node Hardware requirements +@section Hardware requirements + +Knot DNS requirements are not very demanding for typical +installations, and a commodity server or a virtual solution +will be sufficient in most cases. + +However please note that there are some scenarios that will +require administrator attention and testing of exact +requirements before deploying Knot DNS in production. These +cases include deployment for a large number of zones (DNS +hosting), a large number of records in one or more zones (TLD) +or large number of requests. + +@node CPU requirements +@section CPU requirements + +Knot DNS scales with processing power and also with the number of available cores/CPUs. + +There is no lower bound on the CPU requirements, but it should support memory barriers +and CAS (i586 and newer). + +@node Memory requirements +@section Memory requirements + +Knot DNS implementation focuses on performance and thus can +be quite demanding for memory. The rough estimate for memory +requirements is 5-7 times of the size of the zone in text +format. Again this is only an estimate and you are advised to do +your own measurements before deploying Knot DNS into production. + +Also note that to ensure uninterrupted serving of the zone, Knot DNS employs +a Read-Copy-Update mechanism instead of locking and thus requires +twice the amount of memory for the duration of incoming transfers. + +@node Supported operating system +@section Supported operating system + +Knot DNS itself is written in a portable way, but it depends on +several libraries. Namely userspace-rcu, which could be a constraint when it +comes to the operating system support. As far as we know the +Knot DNS can be compiled and run on Linux, FreeBSD, OpenBSD, NetBSD and Mac OS X. diff --git a/doc/running.texi b/doc/running.texi new file mode 100755 index 0000000..628cd7d --- /dev/null +++ b/doc/running.texi @@ -0,0 +1,187 @@ +@node Running Knot DNS, Troubleshooting, Knot DNS Configuration, Top +@chapter Running Knot DNS + +@menu +* Running a slave server:: +* Running a master server:: +* Controlling running daemon:: +@end menu + +Knot DNS is designed to compile zone files before loading them into server. +The reason for this is to speed up server startup, but requires a bit of user +effort, so each time the zone file changes you need to compile it. +@example +$ knotc -c knot.conf compile +@end example +Or alternatively, you can compile automatically using the @code{-a} flag. +@example +$ knotc -a -c knot.conf start|reload|restart +@end example + +The tool @code{knotc} is designed as a front-end for user, making it easier +to do everything from zone compilation to controlling the server daemon. +To communicate with the binary, it reads the PID from the @emph{PID file} specified in the configuration @code{pidfile} and sends POSIX signals to it (@pxref{pidfile}). +If you want to control the daemon directly, use @code{SIGINT} to quit the process or @code{SIGHUP} to reload configuration. Signal @code{SIGUSR2} is currently used to refresh slave zones. + +@example +Usage: knotc [parameters] start|stop|restart|reload|running|compile +Parameters: + -c [file], --config=[file] + Select configuration file. + -j [num], --jobs=[num] + Number of parallel tasks to run when compiling. + -f, --force + Force operation - override some checks. + -v, --verbose + Verbose mode - additional runtime information. + -V, --version + Print knot server version. + -w, --wait + Wait for the server to finish start/stop operations. + -i, --interactive + Interactive mode (do not daemonize). + -a, --auto + Enable automatic recompilation (start or reload). + -h, --help + Print help and usage. + +Actions: + start Start knot server zone (no-op if running). + stop Stop knot server (no-op if not running). + restart Stops and then starts knot server. + reload Reload knot configuration and compiled zones. + refresh Refresh all slave zones. + running Check if server is running. + checkconf Check server configuration. + checkzone Check zones (accepts specific zones, + e.g. 'knotc checkzone example1.com example2.com'). + compile Compile zones (accepts specific zones, see above). +@end example + +If you want to run Knot DNS daemon directly, you can use @code{knotd} binary +to do that. It accepts just configuration file and option to run in background. +@example +Usage: knotd [parameters] + +Parameters: + -c, --config [file] Select configuration file. + -d, --daemonize Run server as a daemon. + -v, --verbose Verbose mode - additional runtime information. + -V, --version Print version of the server. + -h, --help Print help and usage. +@end example + +Also, the server needs to create several files in order to run properly. +All files are placed in the directory described by @code{storage} (@pxref{storage}). +PID file can be placed elsewhere using the @code{pidfile} statement (@pxref{pidfile}). +Slave zones with relative path specified will be placed in the @code{storage} as well. +@itemize @bullet +@item +@emph{Compiled zones} - preprocessed zones, for example zone @code{example.com} will be +placed in @file{STORAGE/example.com.db}. +@item +@emph{Journal files} - each zone has a journal file to store differences for IXFR and +dynamic updates. Journal for zone @code{example.com} will be +placed in @file{STORAGE/example.com.diff.db}. +@item +@emph{PID file} - unless specified differently by the @code{pidfile}, it will be placed +in the @file{STORAGE/knot.pid}. +@item +@emph{Checksum files} - in order to identify compiled zone corruption, it +has a separate checksum file. For @code{example.com} will be +placed in @file{STORAGE/example.com.db.crc}. +@end itemize + +@node Running a slave server +@section Running a slave server + +Running the server as a slave is very straightforward as you usually bootstrap +zones over AXFR and thus avoid any manual zone compilation. +When a zone is transferred over AXFR, both the compiled zone and the zone file is +updated, so no further compilation is needed. +However when IXFR transfer finishes, it stores the differences in a journal file +and doesn't update the zone file nor compiled zone immediately, +but there is a timer that checks periodically for new differences and +updates both zone file and the compiled zone. You can configure this timer +with the @code{zonefile-sync} statement in @code{zones} (@pxref{zones}). + +There are two ways to start the server - directly or with the @code{knotc} controller tool. +First, let us start it directly. If you do not pass any configuration, it will try to +search configuration in default path that is @code{SYSCONFDIR/knot.conf}. The @code{SYSCONFDIR} +depends on what you passed to the @code{./configure}, usually @code{/etc}. + +@example +$ knotc -c slave.conf checkconf # check configuration +$ knotd -c slave.conf +@end example + +However to start it as a daemon, @code{knotc} tool should be used. +The @code{knotc} tool accepts parameter @code{-w} to wait until the requested operation finishes. +When the action is "start" for example, it waits until the server starts to serve zones. +@example +$ knotc -w -c slave.conf start # start the daemon +$ knotc -c slave.conf stop # stop the daemon +@end example + +When the server is running, you can control the daemon, see @ref{Controlling running daemon}. + +@node Running a master server +@section Running a master server + +Knot DNS first needs to compile the zones before it can load them, therefore you need to +compile them with the @code{knotc compile} action or use flag @code{-a} to compile the zones automatically. + +If you want to just check the zone files first before starting, +you can use @code{knotc checkzone} action. +@example +$ knotc -c master.conf checkzone example.com +@end example + +Starting and stopping the daemon is the same as with the slave server in the previous section. +@example +$ knotc -c master.conf compile +$ knotc -w -c master.conf start +@end example + +Or you can compile it automatically: +@example +$ knotc -c master.conf checkconf # check configuration +$ knotc -a -w -c master.conf start +@end example + +@node Controlling running daemon +@section Controlling running daemon + +Knot DNS was designed to allow server reconfiguration on-the-fly without interrupting +its operation. Thus it is possible to change both configuration and zone files and +also add or remove zones without restarting the server. This can be done with the +@code{knotc reload} action. + +@example +$ knotc -c master.conf compile # compile updated zones +$ knotc -c master.conf reload # reconfigure and load updated zones +@end example + +Or use the @code{-a} again. +@example +$ knotc -a -c master.conf reload # compile zones and reconfigure +@end example + +If you want @emph{IXFR-out} differences created from changes you make to a zone file, enable @code{ixfr-from-differences} +in @code{zones} statement, then compile the zone and reload your server as seen above. +If @emph{SOA}'s @emph{serial} is not changed no differences will be created. Please note +that this feature is in @emph{experimental} stage and should be used with care. +If you encounter a bug using this feature, please send it to Knot developers (@pxref{Submitting a bugreport}). + +You can also choose to tear-down the server fully and restart with the @code{knotc restart} action. +@example +$ knotc -c master.conf running # check if running +$ knotc -c master.conf restart # fully restart +@end example + +If you want to force refresh the slave zones, you can do this with the @code{knotc refresh} action. +@example +$ knotc -c slave.conf refresh +@end example + +For a complete list of actions refer to @code{knotc --help} command output. diff --git a/doc/security.texi b/doc/security.texi new file mode 100755 index 0000000..3716521 --- /dev/null +++ b/doc/security.texi @@ -0,0 +1,15 @@ +@node Security Considerations, Troubleshooting, Running Knot DNS, Top +@chapter Security Considerations + +[TODO] +- faces the internet + +If libcap-ng is available, Knot DNS on Linux takes advantage of +the POSIX 1003.1e capabilities. This mechanism breaks the a set of privileges +traditionally associated with the root into groups that can be set per-thread +and independently enabled or disabled. For more information, look up manual page +for capabilities(7). + +Knot DNS uses strips exposed threads of most capabilities like file access, +privileged socket operations and such. +This mitigates potential remote exploits or at least the impact. diff --git a/doc/texinfo.tex b/doc/texinfo.tex new file mode 100755 index 0000000..9140826 --- /dev/null +++ b/doc/texinfo.tex @@ -0,0 +1,9291 @@ +% texinfo.tex -- TeX macros to handle Texinfo files. +% +% Load plain if necessary, i.e., if running under initex. +\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi +% +\def\texinfoversion{2009-08-14.15} +% +% Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, +% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +% 2007, 2008, 2009 Free Software Foundation, Inc. +% +% This texinfo.tex file 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 texinfo.tex file 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/>. +% +% As a special exception, when this file is read by TeX when processing +% a Texinfo source document, you may use the result without +% restriction. (This has been our intent since Texinfo was invented.) +% +% Please try the latest version of texinfo.tex before submitting bug +% reports; you can get the latest version from: +% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or +% ftp://tug.org/tex/texinfo.tex +% (and all CTAN mirrors, see http://www.ctan.org). +% The texinfo.tex in any given distribution could well be out +% of date, so if that's what you're using, please check. +% +% Send bug reports to bug-texinfo@gnu.org. Please include including a +% complete document in each bug report with which we can reproduce the +% problem. Patches are, of course, greatly appreciated. +% +% To process a Texinfo manual with TeX, it's most reliable to use the +% texi2dvi shell script that comes with the distribution. For a simple +% manual foo.texi, however, you can get away with this: +% tex foo.texi +% texindex foo.?? +% tex foo.texi +% tex foo.texi +% dvips foo.dvi -o # or whatever; this makes foo.ps. +% The extra TeX runs get the cross-reference information correct. +% Sometimes one run after texindex suffices, and sometimes you need more +% than two; texi2dvi does it as many times as necessary. +% +% It is possible to adapt texinfo.tex for other languages, to some +% extent. You can get the existing language-specific files from the +% full Texinfo distribution. +% +% The GNU Texinfo home page is http://www.gnu.org/software/texinfo. + + +\message{Loading texinfo [version \texinfoversion]:} + +% If in a .fmt file, print the version number +% and turn on active characters that we couldn't do earlier because +% they might have appeared in the input file name. +\everyjob{\message{[Texinfo version \texinfoversion]}% + \catcode`+=\active \catcode`\_=\active} + + +\chardef\other=12 + +% We never want plain's \outer definition of \+ in Texinfo. +% For @tex, we can use \tabalign. +\let\+ = \relax + +% Save some plain tex macros whose names we will redefine. +\let\ptexb=\b +\let\ptexbullet=\bullet +\let\ptexc=\c +\let\ptexcomma=\, +\let\ptexdot=\. +\let\ptexdots=\dots +\let\ptexend=\end +\let\ptexequiv=\equiv +\let\ptexexclam=\! +\let\ptexfootnote=\footnote +\let\ptexgtr=> +\let\ptexhat=^ +\let\ptexi=\i +\let\ptexindent=\indent +\let\ptexinsert=\insert +\let\ptexlbrace=\{ +\let\ptexless=< +\let\ptexnewwrite\newwrite +\let\ptexnoindent=\noindent +\let\ptexplus=+ +\let\ptexrbrace=\} +\let\ptexslash=\/ +\let\ptexstar=\* +\let\ptext=\t +\let\ptextop=\top +{\catcode`\'=\active +\global\let\ptexquoteright'}% Math-mode def from plain.tex. +\let\ptexraggedright=\raggedright + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Pre-3.0. +\else + \def\linenumber{l.\the\inputlineno:\space} +\fi + +% Set up fixed words for English if not already set. +\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi +\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi +\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi +\ifx\putwordin\undefined \gdef\putwordin{in}\fi +\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi +\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi +\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi +\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi +\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi +\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi +\ifx\putwordof\undefined \gdef\putwordof{of}\fi +\ifx\putwordon\undefined \gdef\putwordon{on}\fi +\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi +\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi +\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi +\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi +\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi +\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi +\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi +% +\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi +\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi +\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi +\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi +\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi +\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi +\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi +\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi +\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi +\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi +\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi +\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi +% +\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi +\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi +\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi +\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi +\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi + +% Since the category of space is not known, we have to be careful. +\chardef\spacecat = 10 +\def\spaceisspace{\catcode`\ =\spacecat} + +% sometimes characters are active, so we need control sequences. +\chardef\colonChar = `\: +\chardef\commaChar = `\, +\chardef\dashChar = `\- +\chardef\dotChar = `\. +\chardef\exclamChar= `\! +\chardef\lquoteChar= `\` +\chardef\questChar = `\? +\chardef\rquoteChar= `\' +\chardef\semiChar = `\; +\chardef\underChar = `\_ + +% Ignore a token. +% +\def\gobble#1{} + +% The following is used inside several \edef's. +\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} + +% Hyphenation fixes. +\hyphenation{ + Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script + ap-pen-dix bit-map bit-maps + data-base data-bases eshell fall-ing half-way long-est man-u-script + man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm + par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces + spell-ing spell-ings + stand-alone strong-est time-stamp time-stamps which-ever white-space + wide-spread wrap-around +} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen\bindingoffset +\newdimen\normaloffset +\newdimen\pagewidth \newdimen\pageheight + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. We also make +% some effort to order the tracing commands to reduce output in the log +% file; cf. trace.sty in LaTeX. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\def\loggingall{% + \tracingstats2 + \tracingpages1 + \tracinglostchars2 % 2 gives us more in etex + \tracingparagraphs1 + \tracingoutput1 + \tracingmacros2 + \tracingrestores1 + \showboxbreadth\maxdimen \showboxdepth\maxdimen + \ifx\eTeXversion\undefined\else % etex gives us more logging + \tracingscantokens1 + \tracingifs1 + \tracinggroups1 + \tracingnesting2 + \tracingassigns1 + \fi + \tracingcommands3 % 3 gives us more in etex + \errorcontextlines16 +}% + +% add check for \lastpenalty to plain's definitions. If the last thing +% we did was a \nobreak, we don't want to insert more space. +% +\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount + \removelastskip\penalty-50\smallskip\fi\fi} +\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount + \removelastskip\penalty-100\medskip\fi\fi} +\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount + \removelastskip\penalty-200\bigskip\fi\fi} + +% For @cropmarks command. +% Do @cropmarks to get crop marks. +% +\newif\ifcropmarks +\let\cropmarks = \cropmarkstrue +% +% Dimensions to add cropmarks at corners. +% Added by P. A. MacKay, 12 Nov. 1986 +% +\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines +\newdimen\cornerlong \cornerlong=1pc +\newdimen\cornerthick \cornerthick=.3pt +\newdimen\topandbottommargin \topandbottommargin=.75in + +% Output a mark which sets \thischapter, \thissection and \thiscolor. +% We dump everything together because we only have one kind of mark. +% This works because we only use \botmark / \topmark, not \firstmark. +% +% A mark contains a subexpression of the \ifcase ... \fi construct. +% \get*marks macros below extract the needed part using \ifcase. +% +% Another complication is to let the user choose whether \thischapter +% (\thissection) refers to the chapter (section) in effect at the top +% of a page, or that at the bottom of a page. The solution is +% described on page 260 of The TeXbook. It involves outputting two +% marks for the sectioning macros, one before the section break, and +% one after. I won't pretend I can describe this better than DEK... +\def\domark{% + \toks0=\expandafter{\lastchapterdefs}% + \toks2=\expandafter{\lastsectiondefs}% + \toks4=\expandafter{\prevchapterdefs}% + \toks6=\expandafter{\prevsectiondefs}% + \toks8=\expandafter{\lastcolordefs}% + \mark{% + \the\toks0 \the\toks2 + \noexpand\or \the\toks4 \the\toks6 + \noexpand\else \the\toks8 + }% +} +% \topmark doesn't work for the very first chapter (after the title +% page or the contents), so we use \firstmark there -- this gets us +% the mark with the chapter defs, unless the user sneaks in, e.g., +% @setcolor (or @url, or @link, etc.) between @contents and the very +% first @chapter. +\def\gettopheadingmarks{% + \ifcase0\topmark\fi + \ifx\thischapter\empty \ifcase0\firstmark\fi \fi +} +\def\getbottomheadingmarks{\ifcase1\botmark\fi} +\def\getcolormarks{\ifcase2\topmark\fi} + +% Avoid "undefined control sequence" errors. +\def\lastchapterdefs{} +\def\lastsectiondefs{} +\def\prevchapterdefs{} +\def\prevsectiondefs{} +\def\lastcolordefs{} + +% Main output routine. +\chardef\PAGE = 255 +\output = {\onepageout{\pagecontents\PAGE}} + +\newbox\headlinebox +\newbox\footlinebox + +% \onepageout takes a vbox as an argument. Note that \pagecontents +% does insertions, but you have to call it yourself. +\def\onepageout#1{% + \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi + % + \ifodd\pageno \advance\hoffset by \bindingoffset + \else \advance\hoffset by -\bindingoffset\fi + % + % Do this outside of the \shipout so @code etc. will be expanded in + % the headline as they should be, not taken literally (outputting ''code). + \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi + \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}% + \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi + \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}% + % + {% + % Have to do this stuff outside the \shipout because we want it to + % take effect in \write's, yet the group defined by the \vbox ends + % before the \shipout runs. + % + \indexdummies % don't expand commands in the output. + \normalturnoffactive % \ in index entries must not stay \, e.g., if + % the page break happens to be in the middle of an example. + % We don't want .vr (or whatever) entries like this: + % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}} + % "\acronym" won't work when it's read back in; + % it needs to be + % {\code {{\tt \backslashcurfont }acronym} + \shipout\vbox{% + % Do this early so pdf references go to the beginning of the page. + \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi + % + \ifcropmarks \vbox to \outervsize\bgroup + \hsize = \outerhsize + \vskip-\topandbottommargin + \vtop to0pt{% + \line{\ewtop\hfil\ewtop}% + \nointerlineskip + \line{% + \vbox{\moveleft\cornerthick\nstop}% + \hfill + \vbox{\moveright\cornerthick\nstop}% + }% + \vss}% + \vskip\topandbottommargin + \line\bgroup + \hfil % center the page within the outer (page) hsize. + \ifodd\pageno\hskip\bindingoffset\fi + \vbox\bgroup + \fi + % + \unvbox\headlinebox + \pagebody{#1}% + \ifdim\ht\footlinebox > 0pt + % Only leave this space if the footline is nonempty. + % (We lessened \vsize for it in \oddfootingyyy.) + % The \baselineskip=24pt in plain's \makefootline has no effect. + \vskip 24pt + \unvbox\footlinebox + \fi + % + \ifcropmarks + \egroup % end of \vbox\bgroup + \hfil\egroup % end of (centering) \line\bgroup + \vskip\topandbottommargin plus1fill minus1fill + \boxmaxdepth = \cornerthick + \vbox to0pt{\vss + \line{% + \vbox{\moveleft\cornerthick\nsbot}% + \hfill + \vbox{\moveright\cornerthick\nsbot}% + }% + \nointerlineskip + \line{\ewbot\hfil\ewbot}% + }% + \egroup % \vbox from first cropmarks clause + \fi + }% end of \shipout\vbox + }% end of group with \indexdummies + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi +} + +\newinsert\margin \dimen\margin=\maxdimen + +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +% marginal hacks, juha@viisa.uucp (Juha Takala) +\ifvoid\margin\else % marginal info is present + \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi +\dimen@=\dp#1\relax \unvbox#1\relax +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% +\def\parsearg{\parseargusing{}} +\def\parseargusing#1#2{% + \def\argtorun{#2}% + \begingroup + \obeylines + \spaceisspace + #1% + \parseargline\empty% Insert the \empty token, see \finishparsearg below. +} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + \argremovecomment #1\comment\ArgTerm% + }% +} + +% First remove any @comment, then any @c comment. +\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} +\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} + +% Each occurrence of `\^^M' or `<space>\^^M' is replaced by a single space. +% +% \argremovec might leave us with trailing space, e.g., +% @end itemize @c foo +% This space token undergoes the same procedure and is eventually removed +% by \finishparsearg. +% +\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} +\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} +\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% + \def\temp{#3}% + \ifx\temp\empty + % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: + \let\temp\finishparsearg + \else + \let\temp\argcheckspaces + \fi + % Put the space token in: + \temp#1 #3\ArgTerm +} + +% If a _delimited_ argument is enclosed in braces, they get stripped; so +% to get _exactly_ the rest of the line, we had to prevent such situation. +% We prepended an \empty token at the very beginning and we expand it now, +% just before passing the control to \argtorun. +% (Similarly, we have to think about #3 of \argcheckspacesY above: it is +% either the null string, or it ends with \^^M---thus there is no danger +% that a pair of braces would be stripped. +% +% But first, we have to remove the trailing space token. +% +\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} + +% \parseargdef\foo{...} +% is roughly equivalent to +% \def\foo{\parsearg\Xfoo} +% \def\Xfoo#1{...} +% +% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my +% favourite TeX trick. --kasal, 16nov03 + +\def\parseargdef#1{% + \expandafter \doparseargdef \csname\string#1\endcsname #1% +} +\def\doparseargdef#1#2{% + \def#2{\parsearg#1}% + \def#1##1% +} + +% Several utility definitions with active space: +{ + \obeyspaces + \gdef\obeyedspace{ } + + % Make each space character in the input produce a normal interword + % space in the output. Don't allow a line break at this space, as this + % is used only in environments like @example, where each line of input + % should produce a line of output anyway. + % + \gdef\sepspaces{\obeyspaces\let =\tie} + + % If an index command is used in an @example environment, any spaces + % therein should become regular spaces in the raw index file, not the + % expansion of \tie (\leavevmode \penalty \@M \ ). + \gdef\unsepspaces{\let =\space} +} + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +% Define the framework for environments in texinfo.tex. It's used like this: +% +% \envdef\foo{...} +% \def\Efoo{...} +% +% It's the responsibility of \envdef to insert \begingroup before the +% actual body; @end closes the group after calling \Efoo. \envdef also +% defines \thisenv, so the current environment is known; @end checks +% whether the environment name matches. The \checkenv macro can also be +% used to check whether the current environment is the one expected. +% +% Non-false conditionals (@iftex, @ifset) don't fit into this, so they +% are not treated as environments; they don't open a group. (The +% implementation of @end takes care not to call \endgroup in this +% special case.) + + +% At run-time, environments start with this: +\def\startenvironment#1{\begingroup\def\thisenv{#1}} +% initialize +\let\thisenv\empty + +% ... but they get defined via ``\envdef\foo{...}'': +\long\def\envdef#1#2{\def#1{\startenvironment#1#2}} +\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} + +% Check whether we're in the right environment: +\def\checkenv#1{% + \def\temp{#1}% + \ifx\thisenv\temp + \else + \badenverr + \fi +} + +% Environment mismatch, #1 expected: +\def\badenverr{% + \errhelp = \EMsimple + \errmessage{This command can appear only \inenvironment\temp, + not \inenvironment\thisenv}% +} +\def\inenvironment#1{% + \ifx#1\empty + out of any environment% + \else + in environment \expandafter\string#1% + \fi +} + +% @end foo executes the definition of \Efoo. +% But first, it executes a specialized version of \checkenv +% +\parseargdef\end{% + \if 1\csname iscond.#1\endcsname + \else + % The general wording of \badenverr may not be ideal, but... --kasal, 06nov03 + \expandafter\checkenv\csname#1\endcsname + \csname E#1\endcsname + \endgroup + \fi +} + +\newhelp\EMsimple{Press RETURN to continue.} + + +%% Simple single-character @ commands + +% @@ prints an @ +% Kludge this until the fonts are right (grr). +\def\@{{\tt\char64}} + +% This is turned off because it was never documented +% and you can use @w{...} around a quote to suppress ligatures. +%% Define @` and @' to be the same as ` and ' +%% but suppressing ligatures. +%\def\`{{`}} +%\def\'{{'}} + +% Used to generate quoted braces. +\def\mylbrace {{\tt\char123}} +\def\myrbrace {{\tt\char125}} +\let\{=\mylbrace +\let\}=\myrbrace +\begingroup + % Definitions to produce \{ and \} commands for indices, + % and @{ and @} for the aux/toc files. + \catcode`\{ = \other \catcode`\} = \other + \catcode`\[ = 1 \catcode`\] = 2 + \catcode`\! = 0 \catcode`\\ = \other + !gdef!lbracecmd[\{]% + !gdef!rbracecmd[\}]% + !gdef!lbraceatcmd[@{]% + !gdef!rbraceatcmd[@}]% +!endgroup + +% @comma{} to avoid , parsing problems. +\let\comma = , + +% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent +% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. +\let\, = \c +\let\dotaccent = \. +\def\ringaccent#1{{\accent23 #1}} +\let\tieaccent = \t +\let\ubaraccent = \b +\let\udotaccent = \d + +% Other special characters: @questiondown @exclamdown @ordf @ordm +% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. +\def\questiondown{?`} +\def\exclamdown{!`} +\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}} +\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}} + +% Dotless i and dotless j, used for accents. +\def\imacro{i} +\def\jmacro{j} +\def\dotless#1{% + \def\temp{#1}% + \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi + \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi + \else \errmessage{@dotless can be used only with i or j}% + \fi\fi +} + +% The \TeX{} logo, as in plain, but resetting the spacing so that a +% period following counts as ending a sentence. (Idea found in latex.) +% +\edef\TeX{\TeX \spacefactor=1000 } + +% @LaTeX{} logo. Not quite the same results as the definition in +% latex.ltx, since we use a different font for the raised A; it's most +% convenient for us to use an explicitly smaller font, rather than using +% the \scriptstyle font (since we don't reset \scriptstyle and +% \scriptscriptstyle). +% +\def\LaTeX{% + L\kern-.36em + {\setbox0=\hbox{T}% + \vbox to \ht0{\hbox{\selectfonts\lllsize A}\vss}}% + \kern-.15em + \TeX +} + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + % Avoid using \@M directly, because that causes trouble + % if the definition is written into an index file. + \global\let\tiepenalty = \@M + \gdef\tie{\leavevmode\penalty\tiepenalty\ } +} + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\hfil\break\hbox{}\ignorespaces} + +% @/ allows a line break. +\let\/=\allowbreak + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=\endofsentencespacefactor\space} + +% @! is an end-of-sentence bang. +\def\!{!\spacefactor=\endofsentencespacefactor\space} + +% @? is an end-of-sentence query. +\def\?{?\spacefactor=\endofsentencespacefactor\space} + +% @frenchspacing on|off says whether to put extra space after punctuation. +% +\def\onword{on} +\def\offword{off} +% +\parseargdef\frenchspacing{% + \def\temp{#1}% + \ifx\temp\onword \plainfrenchspacing + \else\ifx\temp\offword \plainnonfrenchspacing + \else + \errhelp = \EMsimple + \errmessage{Unknown @frenchspacing option `\temp', must be on/off}% + \fi\fi +} + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +% Another complication is that the group might be very large. This can +% cause the glue on the previous page to be unduly stretched, because it +% does not have much material. In this case, it's better to add an +% explicit \vfill so that the extra space is at the bottom. The +% threshold for doing this is if the group is more than \vfilllimit +% percent of a page (\vfilllimit can be changed inside of @tex). +% +\newbox\groupbox +\def\vfilllimit{0.7} +% +\envdef\group{% + \ifnum\catcode`\^^M=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + \startsavinginserts + % + \setbox\groupbox = \vtop\bgroup + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% The \vtop produces a box with normal height and large depth; thus, TeX puts +% \baselineskip glue before it, and (when the next line of text is done) +% \lineskip glue after it. Thus, space below is not quite equal to space +% above. But it's pretty close. +\def\Egroup{% + % To get correct interline space between the last line of the group + % and the first line afterwards, we have to propagate \prevdepth. + \endgraf % Not \par, as it may have been set to \lisppar. + \global\dimen1 = \prevdepth + \egroup % End the \vtop. + % \dimen0 is the vertical size of the group's box. + \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox + % \dimen2 is how much space is left on the page (more or less). + \dimen2 = \pageheight \advance\dimen2 by -\pagetotal + % if the group doesn't fit on the current page, and it's a big big + % group, force a page break. + \ifdim \dimen0 > \dimen2 + \ifdim \pagetotal < \vfilllimit\pageheight + \page + \fi + \fi + \box\groupbox + \prevdepth = \dimen1 + \checkinserts +} +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +% Old definition--didn't work. +%\parseargdef\need{\par % +%% This method tries to make TeX break the page naturally +%% if the depth of the box does not fit. +%{\baselineskip=0pt% +%\vtop to #1\mil{\vfil}\kern -#1\mil\nobreak +%\prevdepth=-1000pt +%}} + +\parseargdef\need{% + % Ensure vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % If the @need value is less than one line space, it's useless. + \dimen0 = #1\mil + \dimen2 = \ht\strutbox + \advance\dimen2 by \dp\strutbox + \ifdim\dimen0 > \dimen2 + % + % Do a \strut just to make the height of this box be normal, so the + % normal leading is inserted relative to the preceding line. + % And a page break here is fine. + \vtop to #1\mil{\strut\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak + \fi +} + +% @br forces paragraph break (and is undocumented). + +\let\br = \par + +% @page forces the start of a new page. +% +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} + +% This defn is used inside nofill environments such as @example. +\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount + \leftline{\hskip\leftskip{\rm#1}}}} + +% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current +% paragraph. For more general purposes, use the \margin insertion +% class. WHICH is `l' or `r'. +% +\newskip\inmarginspacing \inmarginspacing=1cm +\def\strutdepth{\dp\strutbox} +% +\def\doinmargin#1#2{\strut\vadjust{% + \nobreak + \kern-\strutdepth + \vtop to \strutdepth{% + \baselineskip=\strutdepth + \vss + % if you have multiple lines of stuff to put here, you'll need to + % make the vbox yourself of the appropriate size. + \ifx#1l% + \llap{\ignorespaces #2\hskip\inmarginspacing}% + \else + \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% + \fi + \null + }% +}} +\def\inleftmargin{\doinmargin l} +\def\inrightmargin{\doinmargin r} +% +% @inmargin{TEXT [, RIGHT-TEXT]} +% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; +% else use TEXT for both). +% +\def\inmargin#1{\parseinmargin #1,,\finish} +\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \def\lefttext{#1}% have both texts + \def\righttext{#2}% + \else + \def\lefttext{#1}% have only one text + \def\righttext{#1}% + \fi + % + \ifodd\pageno + \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin + \else + \def\temp{\inleftmargin\lefttext}% + \fi + \temp +} + +% @include FILE -- \input text of FILE. +% +\def\include{\parseargusing\filenamecatcodes\includezzz} +\def\includezzz#1{% + \pushthisfilestack + \def\thisfile{#1}% + {% + \makevalueexpandable % we want to expand any @value in FILE. + \turnoffactive % and allow special characters in the expansion + \indexnofonts % Allow `@@' and other weird things in file names. + \edef\temp{\noexpand\input #1 }% + % + % This trickery is to read FILE outside of a group, in case it makes + % definitions, etc. + \expandafter + }\temp + \popthisfilestack +} +\def\filenamecatcodes{% + \catcode`\\=\other + \catcode`~=\other + \catcode`^=\other + \catcode`_=\other + \catcode`|=\other + \catcode`<=\other + \catcode`>=\other + \catcode`+=\other + \catcode`-=\other + \catcode`\`=\other + \catcode`\'=\other +} + +\def\pushthisfilestack{% + \expandafter\pushthisfilestackX\popthisfilestack\StackTerm +} +\def\pushthisfilestackX{% + \expandafter\pushthisfilestackY\thisfile\StackTerm +} +\def\pushthisfilestackY #1\StackTerm #2\StackTerm {% + \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% +} + +\def\popthisfilestack{\errthisfilestackempty} +\def\errthisfilestackempty{\errmessage{Internal error: + the stack of filenames is empty.}} + +\def\thisfile{} + +% @center line +% outputs that line, centered. +% +\parseargdef\center{% + \ifhmode + \let\next\centerH + \else + \let\next\centerV + \fi + \next{\hfil \ignorespaces#1\unskip \hfil}% +} +\def\centerH#1{% + {% + \hfil\break + \advance\hsize by -\leftskip + \advance\hsize by -\rightskip + \line{#1}% + \break + }% +} +\def\centerV#1{\line{\kern\leftskip #1\kern\rightskip}} + +% @sp n outputs n lines of vertical space + +\parseargdef\sp{\vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment + +\def\comment{\begingroup \catcode`\^^M=\other% +\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% +\commentxxx} +{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}} + +\let\c=\comment + +% @paragraphindent NCHARS +% We'll use ems for NCHARS, close enough. +% NCHARS can also be the word `asis' or `none'. +% We cannot feasibly implement @paragraphindent asis, though. +% +\def\asisword{asis} % no translation, these are keywords +\def\noneword{none} +% +\parseargdef\paragraphindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \defaultparindent = 0pt + \else + \defaultparindent = #1em + \fi + \fi + \parindent = \defaultparindent +} + +% @exampleindent NCHARS +% We'll use ems for NCHARS like @paragraphindent. +% It seems @exampleindent asis isn't necessary, but +% I preserve it to make it similar to @paragraphindent. +\parseargdef\exampleindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \lispnarrowing = 0pt + \else + \lispnarrowing = #1em + \fi + \fi +} + +% @firstparagraphindent WORD +% If WORD is `none', then suppress indentation of the first paragraph +% after a section heading. If WORD is `insert', then do indent at such +% paragraphs. +% +% The paragraph indentation is suppressed or not by calling +% \suppressfirstparagraphindent, which the sectioning commands do. +% We switch the definition of this back and forth according to WORD. +% By default, we suppress indentation. +% +\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} +\def\insertword{insert} +% +\parseargdef\firstparagraphindent{% + \def\temp{#1}% + \ifx\temp\noneword + \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent + \else\ifx\temp\insertword + \let\suppressfirstparagraphindent = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @firstparagraphindent option `\temp'}% + \fi\fi +} + +% Here is how we actually suppress indentation. Redefine \everypar to +% \kern backwards by \parindent, and then reset itself to empty. +% +% We also make \indent itself not actually do anything until the next +% paragraph. +% +\gdef\dosuppressfirstparagraphindent{% + \gdef\indent{% + \restorefirstparagraphindent + \indent + }% + \gdef\noindent{% + \restorefirstparagraphindent + \noindent + }% + \global\everypar = {% + \kern -\parindent + \restorefirstparagraphindent + }% +} + +\gdef\restorefirstparagraphindent{% + \global \let \indent = \ptexindent + \global \let \noindent = \ptexnoindent + \global \everypar = {}% +} + + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math outputs its argument in math mode. +% +% One complication: _ usually means subscripts, but it could also mean +% an actual _ character, as in @math{@var{some_variable} + 1}. So make +% _ active, and distinguish by seeing if the current family is \slfam, +% which is what @var uses. +{ + \catcode`\_ = \active + \gdef\mathunderscore{% + \catcode`\_=\active + \def_{\ifnum\fam=\slfam \_\else\sb\fi}% + } +} +% Another complication: we want \\ (and @\) to output a \ character. +% FYI, plain.tex uses \\ as a temporary control sequence (why?), but +% this is not advertised and we don't care. Texinfo does not +% otherwise define @\. +% +% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. +\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} +% +\def\math{% + \tex + \mathunderscore + \let\\ = \mathbackslash + \mathactive + % make the texinfo accent commands work in math mode + \let\"=\ddot + \let\'=\acute + \let\==\bar + \let\^=\hat + \let\`=\grave + \let\u=\breve + \let\v=\check + \let\~=\tilde + \let\dotaccent=\dot + $\finishmath +} +\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. + +% Some active characters (such as <) are spaced differently in math. +% We have to reset their definitions in case the @math was an argument +% to a command which sets the catcodes (such as @item or @section). +% +{ + \catcode`^ = \active + \catcode`< = \active + \catcode`> = \active + \catcode`+ = \active + \catcode`' = \active + \gdef\mathactive{% + \let^ = \ptexhat + \let< = \ptexless + \let> = \ptexgtr + \let+ = \ptexplus + \let' = \ptexquoteright + } +} + +% Some math mode symbols. +\def\bullet{$\ptexbullet$} +\def\geq{\ifmmode \ge\else $\ge$\fi} +\def\leq{\ifmmode \le\else $\le$\fi} +\def\minus{\ifmmode -\else $-$\fi} + +% @dots{} outputs an ellipsis using the current font. +% We do .5em per period so that it has the same spacing in the cm +% typewriter fonts as three actual period characters; on the other hand, +% in other typewriter fonts three periods are wider than 1.5em. So do +% whichever is larger. +% +\def\dots{% + \leavevmode + \setbox0=\hbox{...}% get width of three periods + \ifdim\wd0 > 1.5em + \dimen0 = \wd0 + \else + \dimen0 = 1.5em + \fi + \hbox to \dimen0{% + \hskip 0pt plus.25fil + .\hskip 0pt plus1fil + .\hskip 0pt plus1fil + .\hskip 0pt plus.5fil + }% +} + +% @enddots{} is an end-of-sentence ellipsis. +% +\def\enddots{% + \dots + \spacefactor=\endofsentencespacefactor +} + +% @comma{} is so commas can be inserted into text without messing up +% Texinfo's parsing. +% +\let\comma = , + +% @refill is a no-op. +\let\refill=\relax + +% If working on a large document in chapters, it is convenient to +% be able to disable indexing, cross-referencing, and contents, for test runs. +% This is done with @novalidate (before @setfilename). +% +\newif\iflinks \linkstrue % by default we want the aux files. +\let\novalidate = \linksfalse + +% @setfilename is done at the beginning of every texinfo file. +% So open here the files we need to have open while reading the input. +% This makes it possible to make a .fmt file for texinfo. +\def\setfilename{% + \fixbackslash % Turn off hack to swallow `\input texinfo'. + \iflinks + \tryauxfile + % Open the new aux file. TeX will close it automatically at exit. + \immediate\openout\auxfile=\jobname.aux + \fi % \openindices needs to do some work in any case. + \openindices + \let\setfilename=\comment % Ignore extra @setfilename cmds. + % + % If texinfo.cnf is present on the system, read it. + % Useful for site-wide @afourpaper, etc. + \openin 1 texinfo.cnf + \ifeof 1 \else \input texinfo.cnf \fi + \closein 1 + % + \comment % Ignore the actual filename. +} + +% Called from \setfilename. +% +\def\openindices{% + \newindex{cp}% + \newcodeindex{fn}% + \newcodeindex{vr}% + \newcodeindex{tp}% + \newcodeindex{ky}% + \newcodeindex{pg}% +} + +% @bye. +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + + +\message{pdf,} +% adobe `portable' document format +\newcount\tempnum +\newcount\lnkcount +\newtoks\filename +\newcount\filenamelength +\newcount\pgn +\newtoks\toksA +\newtoks\toksB +\newtoks\toksC +\newtoks\toksD +\newbox\boxA +\newcount\countA +\newif\ifpdf +\newif\ifpdfmakepagedest + +% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 +% can be set). So we test for \relax and 0 as well as \undefined, +% borrowed from ifpdf.sty. +\ifx\pdfoutput\undefined +\else + \ifx\pdfoutput\relax + \else + \ifcase\pdfoutput + \else + \pdftrue + \fi + \fi +\fi + +% PDF uses PostScript string constants for the names of xref targets, +% for display in the outlines, and in other places. Thus, we have to +% double any backslashes. Otherwise, a name like "\node" will be +% interpreted as a newline (\n), followed by o, d, e. Not good. +% http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html +% (and related messages, the final outcome is that it is up to the TeX +% user to double the backslashes and otherwise make the string valid, so +% that's what we do). + +% double active backslashes. +% +{\catcode`\@=0 \catcode`\\=\active + @gdef@activebackslashdouble{% + @catcode`@\=@active + @let\=@doublebackslash} +} + +% To handle parens, we must adopt a different approach, since parens are +% not active characters. hyperref.dtx (which has the same problem as +% us) handles it with this amazing macro to replace tokens, with minor +% changes for Texinfo. It is included here under the GPL by permission +% from the author, Heiko Oberdiek. +% +% #1 is the tokens to replace. +% #2 is the replacement. +% #3 is the control sequence with the string. +% +\def\HyPsdSubst#1#2#3{% + \def\HyPsdReplace##1#1##2\END{% + ##1% + \ifx\\##2\\% + \else + #2% + \HyReturnAfterFi{% + \HyPsdReplace##2\END + }% + \fi + }% + \xdef#3{\expandafter\HyPsdReplace#3#1\END}% +} +\long\def\HyReturnAfterFi#1\fi{\fi#1} + +% #1 is a control sequence in which to do the replacements. +\def\backslashparens#1{% + \xdef#1{#1}% redefine it as its expansion; the definition is simply + % \lastnode when called from \setref -> \pdfmkdest. + \HyPsdSubst{(}{\realbackslash(}{#1}% + \HyPsdSubst{)}{\realbackslash)}{#1}% +} + +\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images +with PDF output, and none of those formats could be found. (.eps cannot +be supported due to the design of the PDF format; use regular TeX (DVI +output) for that.)} + +\ifpdf + % + % Color manipulation macros based on pdfcolor.tex, + % except using rgb instead of cmyk; the latter is said to render as a + % very dark gray on-screen and a very dark halftone in print, instead + % of actual black. + \def\rgbDarkRed{0.50 0.09 0.12} + \def\rgbBlack{0 0 0} + % + % k sets the color for filling (usual text, etc.); + % K sets the color for stroking (thin rules, e.g., normal _'s). + \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}} + % + % Set color, and create a mark which defines \thiscolor accordingly, + % so that \makeheadline knows which color to restore. + \def\setcolor#1{% + \xdef\lastcolordefs{\gdef\noexpand\thiscolor{#1}}% + \domark + \pdfsetcolor{#1}% + } + % + \def\maincolor{\rgbBlack} + \pdfsetcolor{\maincolor} + \edef\thiscolor{\maincolor} + \def\lastcolordefs{} + % + \def\makefootline{% + \baselineskip24pt + \line{\pdfsetcolor{\maincolor}\the\footline}% + } + % + \def\makeheadline{% + \vbox to 0pt{% + \vskip-22.5pt + \line{% + \vbox to8.5pt{}% + % Extract \thiscolor definition from the marks. + \getcolormarks + % Typeset the headline with \maincolor, then restore the color. + \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% + }% + \vss + }% + \nointerlineskip + } + % + % + \pdfcatalog{/PageMode /UseOutlines} + % + % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). + \def\dopdfimage#1#2#3{% + \def\imagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% + \def\imageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% + % + % pdftex (and the PDF format) support .png, .jpg, .pdf (among + % others). Let's try in that order. + \let\pdfimgext=\empty + \begingroup + \openin 1 #1.png \ifeof 1 + \openin 1 #1.jpg \ifeof 1 + \openin 1 #1.jpeg \ifeof 1 + \openin 1 #1.JPG \ifeof 1 + \openin 1 #1.pdf \ifeof 1 + \openin 1 #1.PDF \ifeof 1 + \errhelp = \nopdfimagehelp + \errmessage{Could not find image file #1 for pdf}% + \else \gdef\pdfimgext{PDF}% + \fi + \else \gdef\pdfimgext{pdf}% + \fi + \else \gdef\pdfimgext{JPG}% + \fi + \else \gdef\pdfimgext{jpeg}% + \fi + \else \gdef\pdfimgext{jpg}% + \fi + \else \gdef\pdfimgext{png}% + \fi + \closein 1 + \endgroup + % + % without \immediate, ancient pdftex seg faults when the same image is + % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) + \ifnum\pdftexversion < 14 + \immediate\pdfimage + \else + \immediate\pdfximage + \fi + \ifdim \wd0 >0pt width \imagewidth \fi + \ifdim \wd2 >0pt height \imageheight \fi + \ifnum\pdftexversion<13 + #1.\pdfimgext + \else + {#1.\pdfimgext}% + \fi + \ifnum\pdftexversion < 14 \else + \pdfrefximage \pdflastximage + \fi} + % + \def\pdfmkdest#1{{% + % We have to set dummies so commands such as @code, and characters + % such as \, aren't expanded when present in a section title. + \indexnofonts + \turnoffactive + \activebackslashdouble + \makevalueexpandable + \def\pdfdestname{#1}% + \backslashparens\pdfdestname + \safewhatsit{\pdfdest name{\pdfdestname} xyz}% + }} + % + % used to mark target names; must be expandable. + \def\pdfmkpgn#1{#1} + % + % by default, use a color that is dark enough to print on paper as + % nearly black, but still distinguishable for online viewing. + \def\urlcolor{\rgbDarkRed} + \def\linkcolor{\rgbDarkRed} + \def\endlink{\setcolor{\maincolor}\pdfendlink} + % + % Adding outlines to PDF; macros for calculating structure of outlines + % come from Petr Olsak + \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% + \else \csname#1\endcsname \fi} + \def\advancenumber#1{\tempnum=\expnumber{#1}\relax + \advance\tempnum by 1 + \expandafter\xdef\csname#1\endcsname{\the\tempnum}} + % + % #1 is the section text, which is what will be displayed in the + % outline by the pdf viewer. #2 is the pdf expression for the number + % of subentries (or empty, for subsubsections). #3 is the node text, + % which might be empty if this toc entry had no corresponding node. + % #4 is the page number + % + \def\dopdfoutline#1#2#3#4{% + % Generate a link to the node text if that exists; else, use the + % page number. We could generate a destination for the section + % text in the case where a section has no node, but it doesn't + % seem worth the trouble, since most documents are normally structured. + \def\pdfoutlinedest{#3}% + \ifx\pdfoutlinedest\empty + \def\pdfoutlinedest{#4}% + \else + % Doubled backslashes in the name. + {\activebackslashdouble \xdef\pdfoutlinedest{#3}% + \backslashparens\pdfoutlinedest}% + \fi + % + % Also double the backslashes in the display string. + {\activebackslashdouble \xdef\pdfoutlinetext{#1}% + \backslashparens\pdfoutlinetext}% + % + \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}% + } + % + \def\pdfmakeoutlines{% + \begingroup + % Thanh's hack / proper braces in bookmarks + \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace + \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace + % + % Read toc silently, to get counts of subentries for \pdfoutline. + \def\numchapentry##1##2##3##4{% + \def\thischapnum{##2}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + }% + \def\numsecentry##1##2##3##4{% + \advancenumber{chap\thischapnum}% + \def\thissecnum{##2}% + \def\thissubsecnum{0}% + }% + \def\numsubsecentry##1##2##3##4{% + \advancenumber{sec\thissecnum}% + \def\thissubsecnum{##2}% + }% + \def\numsubsubsecentry##1##2##3##4{% + \advancenumber{subsec\thissubsecnum}% + }% + \def\thischapnum{0}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + % + % use \def rather than \let here because we redefine \chapentry et + % al. a second time, below. + \def\appentry{\numchapentry}% + \def\appsecentry{\numsecentry}% + \def\appsubsecentry{\numsubsecentry}% + \def\appsubsubsecentry{\numsubsubsecentry}% + \def\unnchapentry{\numchapentry}% + \def\unnsecentry{\numsecentry}% + \def\unnsubsecentry{\numsubsecentry}% + \def\unnsubsubsecentry{\numsubsubsecentry}% + \readdatafile{toc}% + % + % Read toc second time, this time actually producing the outlines. + % The `-' means take the \expnumber as the absolute number of + % subentries, which we calculated on our first read of the .toc above. + % + % We use the node names as the destinations. + \def\numchapentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% + \def\numsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% + \def\numsubsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% + \def\numsubsubsecentry##1##2##3##4{% count is always zero + \dopdfoutline{##1}{}{##3}{##4}}% + % + % PDF outlines are displayed using system fonts, instead of + % document fonts. Therefore we cannot use special characters, + % since the encoding is unknown. For example, the eogonek from + % Latin 2 (0xea) gets translated to a | character. Info from + % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. + % + % xx to do this right, we have to translate 8-bit characters to + % their "best" equivalent, based on the @documentencoding. Right + % now, I guess we'll just let the pdf reader have its way. + \indexnofonts + \setupdatafile + \catcode`\\=\active \otherbackslash + \input \tocreadfilename + \endgroup + } + % + \def\skipspaces#1{\def\PP{#1}\def\D{|}% + \ifx\PP\D\let\nextsp\relax + \else\let\nextsp\skipspaces + \ifx\p\space\else\addtokens{\filename}{\PP}% + \advance\filenamelength by 1 + \fi + \fi + \nextsp} + \def\getfilename#1{\filenamelength=0\expandafter\skipspaces#1|\relax} + \ifnum\pdftexversion < 14 + \let \startlink \pdfannotlink + \else + \let \startlink \pdfstartlink + \fi + % make a live url in pdf output. + \def\pdfurl#1{% + \begingroup + % it seems we really need yet another set of dummies; have not + % tried to figure out what each command should do in the context + % of @url. for now, just make @/ a no-op, that's the only one + % people have actually reported a problem with. + % + \normalturnoffactive + \def\@{@}% + \let\/=\empty + \makevalueexpandable + % do we want to go so far as to use \indexnofonts instead of just + % special-casing \var here? + \def\var##1{##1}% + % + \leavevmode\setcolor{\urlcolor}% + \startlink attr{/Border [0 0 0]}% + user{/Subtype /Link /A << /S /URI /URI (#1) >>}% + \endgroup} + \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} + \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} + \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} + \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} + \def\maketoks{% + \expandafter\poptoks\the\toksA|ENDTOKS|\relax + \ifx\first0\adn0 + \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 + \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 + \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 + \else + \ifnum0=\countA\else\makelink\fi + \ifx\first.\let\next=\done\else + \let\next=\maketoks + \addtokens{\toksB}{\the\toksD} + \ifx\first,\addtokens{\toksB}{\space}\fi + \fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \next} + \def\makelink{\addtokens{\toksB}% + {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} + \def\pdflink#1{% + \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} + \setcolor{\linkcolor}#1\endlink} + \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +\else + % non-pdf mode + \let\pdfmkdest = \gobble + \let\pdfurl = \gobble + \let\endlink = \relax + \let\setcolor = \gobble + \let\pdfsetcolor = \gobble + \let\pdfmakeoutlines = \relax +\fi % \ifx\pdfoutput + + +\message{fonts,} + +% Change the current font style to #1, remembering it in \curfontstyle. +% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in +% italics, not bold italics. +% +\def\setfontstyle#1{% + \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. + \csname ten#1\endcsname % change the current font +} + +% Select #1 fonts with the current style. +% +\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname} + +\def\rm{\fam=0 \setfontstyle{rm}} +\def\it{\fam=\itfam \setfontstyle{it}} +\def\sl{\fam=\slfam \setfontstyle{sl}} +\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} +\def\tt{\fam=\ttfam \setfontstyle{tt}} + +% Unfortunately, we have to override this for titles and the like, since +% in those cases "rm" is bold. Sigh. +\def\rmisbold{\rm\def\curfontstyle{bf}} + +% Texinfo sort of supports the sans serif font style, which plain TeX does not. +% So we set up a \sf. +\newfam\sffam +\def\sf{\fam=\sffam \setfontstyle{sf}} +\let\li = \sf % Sometimes we call it \li, not \sf. + +% We don't need math for this font style. +\def\ttsl{\setfontstyle{ttsl}} + + +% Default leading. +\newdimen\textleading \textleading = 13.2pt + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +% can get a sort of poor man's double spacing by redefining this. +\def\baselinefactor{1} +% +\def\setleading#1{% + \dimen0 = #1\relax + \normalbaselineskip = \baselinefactor\dimen0 + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% PDF CMaps. See also LaTeX's t1.cmap. +% +% do nothing with this by default. +\expandafter\let\csname cmapOT1\endcsname\gobble +\expandafter\let\csname cmapOT1IT\endcsname\gobble +\expandafter\let\csname cmapOT1TT\endcsname\gobble + +% if we are producing pdf, and we have \pdffontattr, then define cmaps. +% (\pdffontattr was introduced many years ago, but people still run +% older pdftex's; it's easy to conditionalize, so we do.) +\ifpdf \ifx\pdffontattr\undefined \else + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1-0) +%%Title: (TeX-OT1-0 TeX OT1 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1) +/Supplement 0 +>> def +/CMapName /TeX-OT1-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<23> <26> <0023> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +40 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1IT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1IT-0) +%%Title: (TeX-OT1IT-0 TeX OT1IT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1IT) +/Supplement 0 +>> def +/CMapName /TeX-OT1IT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<25> <26> <0025> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +42 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<23> <0023> +<24> <00A3> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1IT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1TT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1TT-0) +%%Title: (TeX-OT1TT-0 TeX OT1TT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1TT) +/Supplement 0 +>> def +/CMapName /TeX-OT1TT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +5 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<21> <26> <0021> +<28> <5F> <0028> +<61> <7E> <0061> +endbfrange +32 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <2191> +<0C> <2193> +<0D> <0027> +<0E> <00A1> +<0F> <00BF> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<20> <2423> +<27> <2019> +<60> <2018> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1TT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +\fi\fi + + +% Set the font macro #1 to the font named #2, adding on the +% specified font prefix (normally `cm'). +% #3 is the font's design size, #4 is a scale factor, #5 is the CMap +% encoding (currently only OT1, OT1IT and OT1TT are allowed, pass +% empty to omit). +\def\setfont#1#2#3#4#5{% + \font#1=\fontprefix#2#3 scaled #4 + \csname cmap#5\endcsname#1% +} +% This is what gets called when #5 of \setfont is empty. +\let\cmap\gobble +% emacs-page end of cmaps + +% Use cm as the default font prefix. +% To specify the font prefix, you must define \fontprefix +% before you read in texinfo.tex. +\ifx\fontprefix\undefined +\def\fontprefix{cm} +\fi +% Support font families that don't use the same naming scheme as CM. +\def\rmshape{r} +\def\rmbshape{bx} %where the normal face is bold +\def\bfshape{b} +\def\bxshape{bx} +\def\ttshape{tt} +\def\ttbshape{tt} +\def\ttslshape{sltt} +\def\itshape{ti} +\def\itbshape{bxti} +\def\slshape{sl} +\def\slbshape{bxsl} +\def\sfshape{ss} +\def\sfbshape{ss} +\def\scshape{csc} +\def\scbshape{csc} + +% Definitions for a main text size of 11pt. This is the default in +% Texinfo. +% +\def\definetextfontsizexi{% +% Text fonts (11.2pt, magstep1). +\def\textnominalsize{11pt} +\edef\mainmagstep{\magstephalf} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep +\def\textecsize{1095} + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstep1}{OT1} +\setfont\deftt\ttshape{10}{\magstep1}{OT1TT} +\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 +\def\smallecsize{0900} + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 +\def\smallerecsize{0800} + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\titleecsize{2074} + +% Chapter (and unnumbered) fonts (17.28pt). +\def\chapnominalsize{17pt} +\setfont\chaprm\rmbshape{12}{\magstep2}{OT1} +\setfont\chapit\itbshape{10}{\magstep3}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep3}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT} +\setfont\chapsf\sfbshape{17}{1000}{OT1} +\let\chapbf=\chaprm +\setfont\chapsc\scbshape{10}{\magstep3}{OT1} +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 +\def\chapecsize{1728} + +% Section fonts (14.4pt). +\def\secnominalsize{14pt} +\setfont\secrm\rmbshape{12}{\magstep1}{OT1} +\setfont\secit\itbshape{10}{\magstep2}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep2}{OT1} +\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\secsf\sfbshape{12}{\magstep1}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep2}{OT1} +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 +\def\sececsize{1440} + +% Subsection fonts (13.15pt). +\def\ssecnominalsize{13pt} +\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1} +\setfont\ssecit\itbshape{10}{1315}{OT1IT} +\setfont\ssecsl\slbshape{10}{1315}{OT1} +\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT} +\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1315}{OT1} +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled 1315 +\def\ssececsize{1200} + +% Reduced fonts for @acro in text (10pt). +\def\reducednominalsize{10pt} +\setfont\reducedrm\rmshape{10}{1000}{OT1} +\setfont\reducedtt\ttshape{10}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{1000}{OT1} +\setfont\reducedit\itshape{10}{1000}{OT1IT} +\setfont\reducedsl\slshape{10}{1000}{OT1} +\setfont\reducedsf\sfshape{10}{1000}{OT1} +\setfont\reducedsc\scshape{10}{1000}{OT1} +\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT} +\font\reducedi=cmmi10 +\font\reducedsy=cmsy10 +\def\reducedecsize{1000} + +% reset the current fonts +\textfonts +\rm +} % end of 11pt text font size definitions + + +% Definitions to make the main text be 10pt Computer Modern, with +% section, chapter, etc., sizes following suit. This is for the GNU +% Press printing of the Emacs 22 manual. Maybe other manuals in the +% future. Used with @smallbook, which sets the leading to 12pt. +% +\def\definetextfontsizex{% +% Text fonts (10pt). +\def\textnominalsize{10pt} +\edef\mainmagstep{1000} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep +\def\textecsize{1000} + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstephalf}{OT1} +\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT} +\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 +\def\smallecsize{0900} + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 +\def\smallerecsize{0800} + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\titleecsize{2074} + +% Chapter fonts (14.4pt). +\def\chapnominalsize{14pt} +\setfont\chaprm\rmbshape{12}{\magstep1}{OT1} +\setfont\chapit\itbshape{10}{\magstep2}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep2}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\chapsf\sfbshape{12}{\magstep1}{OT1} +\let\chapbf\chaprm +\setfont\chapsc\scbshape{10}{\magstep2}{OT1} +\font\chapi=cmmi12 scaled \magstep1 +\font\chapsy=cmsy10 scaled \magstep2 +\def\chapecsize{1440} + +% Section fonts (12pt). +\def\secnominalsize{12pt} +\setfont\secrm\rmbshape{12}{1000}{OT1} +\setfont\secit\itbshape{10}{\magstep1}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep1}{OT1} +\setfont\sectt\ttbshape{12}{1000}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT} +\setfont\secsf\sfbshape{12}{1000}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep1}{OT1} +\font\seci=cmmi12 +\font\secsy=cmsy10 scaled \magstep1 +\def\sececsize{1200} + +% Subsection fonts (10pt). +\def\ssecnominalsize{10pt} +\setfont\ssecrm\rmbshape{10}{1000}{OT1} +\setfont\ssecit\itbshape{10}{1000}{OT1IT} +\setfont\ssecsl\slbshape{10}{1000}{OT1} +\setfont\ssectt\ttbshape{10}{1000}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT} +\setfont\ssecsf\sfbshape{10}{1000}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1000}{OT1} +\font\sseci=cmmi10 +\font\ssecsy=cmsy10 +\def\ssececsize{1000} + +% Reduced fonts for @acro in text (9pt). +\def\reducednominalsize{9pt} +\setfont\reducedrm\rmshape{9}{1000}{OT1} +\setfont\reducedtt\ttshape{9}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{900}{OT1} +\setfont\reducedit\itshape{9}{1000}{OT1IT} +\setfont\reducedsl\slshape{9}{1000}{OT1} +\setfont\reducedsf\sfshape{9}{1000}{OT1} +\setfont\reducedsc\scshape{10}{900}{OT1} +\setfont\reducedttsl\ttslshape{10}{900}{OT1TT} +\font\reducedi=cmmi9 +\font\reducedsy=cmsy9 +\def\reducedecsize{0900} + +% reduce space between paragraphs +\divide\parskip by 2 + +% reset the current fonts +\textfonts +\rm +} % end of 10pt text font size definitions + + +% We provide the user-level command +% @fonttextsize 10 +% (or 11) to redefine the text font size. pt is assumed. +% +\def\xword{10} +\def\xiword{11} +% +\parseargdef\fonttextsize{% + \def\textsizearg{#1}% + \wlog{doing @fonttextsize \textsizearg}% + % + % Set \globaldefs so that documents can use this inside @tex, since + % makeinfo 4.8 does not support it, but we need it nonetheless. + % + \begingroup \globaldefs=1 + \ifx\textsizearg\xword \definetextfontsizex + \else \ifx\textsizearg\xiword \definetextfontsizexi + \else + \errhelp=\EMsimple + \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} + \fi\fi + \endgroup +} + + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. Since +% texinfo doesn't allow for producing subscripts and superscripts except +% in the main text, we don't bother to reset \scriptfont and +% \scriptscriptfont (which would also require loading a lot more fonts). +% +\def\resetmathfonts{% + \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy + \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf + \textfont\ttfam=\tentt \textfont\sffam=\tensf +} + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this because \STYLE needs to also set the +% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire +% \tenSTYLE to set the current font. +% +% Each font-changing command also sets the names \lsize (one size lower) +% and \lllsize (three sizes lower). These relative commands are used in +% the LaTeX logo and acronyms. +% +% This all needs generalizing, badly. +% +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy + \let\tenttsl=\textttsl + \def\curfontsize{text}% + \def\lsize{reduced}\def\lllsize{smaller}% + \resetmathfonts \setleading{\textleading}} +\def\titlefonts{% + \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl + \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc + \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy + \let\tenttsl=\titlettsl + \def\curfontsize{title}% + \def\lsize{chap}\def\lllsize{subsec}% + \resetmathfonts \setleading{25pt}} +\def\titlefont#1{{\titlefonts\rmisbold #1}} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy + \let\tenttsl=\chapttsl + \def\curfontsize{chap}% + \def\lsize{sec}\def\lllsize{text}% + \resetmathfonts \setleading{19pt}} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy + \let\tenttsl=\secttsl + \def\curfontsize{sec}% + \def\lsize{subsec}\def\lllsize{reduced}% + \resetmathfonts \setleading{16pt}} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy + \let\tenttsl=\ssecttsl + \def\curfontsize{ssec}% + \def\lsize{text}\def\lllsize{small}% + \resetmathfonts \setleading{15pt}} +\let\subsubsecfonts = \subsecfonts +\def\reducedfonts{% + \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl + \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc + \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy + \let\tenttsl=\reducedttsl + \def\curfontsize{reduced}% + \def\lsize{small}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallfonts{% + \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl + \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc + \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy + \let\tenttsl=\smallttsl + \def\curfontsize{small}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallerfonts{% + \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl + \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc + \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy + \let\tenttsl=\smallerttsl + \def\curfontsize{smaller}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{9.5pt}} + +% Fonts for short table of contents. +\setfont\shortcontrm\rmshape{12}{1000}{OT1} +\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12 +\setfont\shortcontsl\slshape{12}{1000}{OT1} +\setfont\shortconttt\ttshape{12}{1000}{OT1TT} + +% Define these just so they can be easily changed for other fonts. +\def\angleleft{$\langle$} +\def\angleright{$\rangle$} + +% Set the fonts to use with the @small... environments. +\let\smallexamplefonts = \smallfonts + +% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample +% can fit this many characters: +% 8.5x11=86 smallbook=72 a4=90 a5=69 +% If we use \scriptfonts (8pt), then we can fit this many characters: +% 8.5x11=90+ smallbook=80 a4=90+ a5=77 +% For me, subjectively, the few extra characters that fit aren't worth +% the additional smallness of 8pt. So I'm making the default 9pt. +% +% By the way, for comparison, here's what fits with @example (10pt): +% 8.5x11=71 smallbook=60 a4=75 a5=58 +% --karl, 24jan03. + +% Set up the default fonts, so we can use them for creating boxes. +% +\definetextfontsizexi + + +\message{markup,} + +% Check if we are currently using a typewriter font. Since all the +% Computer Modern typewriter fonts have zero interword stretch (and +% shrink), and it is reasonable to expect all typewriter fonts to have +% this property, we can check that font parameter. +% +\def\ifmonospace{\ifdim\fontdimen3\font=0pt } + +% Markup style infrastructure. \defmarkupstylesetup\INITMACRO will +% define and register \INITMACRO to be called on markup style changes. +% \INITMACRO can check \currentmarkupstyle for the innermost +% style and the set of \ifmarkupSTYLE switches for all styles +% currently in effect. +\newif\ifmarkupvar +\newif\ifmarkupsamp +\newif\ifmarkupkey +%\newif\ifmarkupfile % @file == @samp. +%\newif\ifmarkupoption % @option == @samp. +\newif\ifmarkupcode +\newif\ifmarkupkbd +%\newif\ifmarkupenv % @env == @code. +%\newif\ifmarkupcommand % @command == @code. +\newif\ifmarkuptex % @tex (and part of @math, for now). +\newif\ifmarkupexample +\newif\ifmarkupverb +\newif\ifmarkupverbatim + +\let\currentmarkupstyle\empty + +\def\setupmarkupstyle#1{% + \csname markup#1true\endcsname + \def\currentmarkupstyle{#1}% + \markupstylesetup +} + +\let\markupstylesetup\empty + +\def\defmarkupstylesetup#1{% + \expandafter\def\expandafter\markupstylesetup + \expandafter{\markupstylesetup #1}% + \def#1% +} + +% Markup style setup for left and right quotes. +\defmarkupstylesetup\markupsetuplq{% + \expandafter\let\expandafter \temp \csname markupsetuplq\currentmarkupstyle\endcsname + \ifx\temp\relax \markupsetuplqdefault \else \temp \fi +} + +\defmarkupstylesetup\markupsetuprq{% + \expandafter\let\expandafter \temp \csname markupsetuprq\currentmarkupstyle\endcsname + \ifx\temp\relax \markupsetuprqdefault \else \temp \fi +} + +{ +\catcode`\'=\active +\catcode`\`=\active + +\gdef\markupsetuplqdefault{\let`\lq} +\gdef\markupsetuprqdefault{\let'\rq} + +\gdef\markupsetcodequoteleft{\let`\codequoteleft} +\gdef\markupsetcodequoteright{\let'\codequoteright} + +\gdef\markupsetnoligaturesquoteleft{\let`\noligaturesquoteleft} +} + +\let\markupsetuplqcode \markupsetcodequoteleft +\let\markupsetuprqcode \markupsetcodequoteright +\let\markupsetuplqexample \markupsetcodequoteleft +\let\markupsetuprqexample \markupsetcodequoteright +\let\markupsetuplqverb \markupsetcodequoteleft +\let\markupsetuprqverb \markupsetcodequoteright +\let\markupsetuplqverbatim \markupsetcodequoteleft +\let\markupsetuprqverbatim \markupsetcodequoteright + +\let\markupsetuplqsamp \markupsetnoligaturesquoteleft +\let\markupsetuplqkbd \markupsetnoligaturesquoteleft + +% Allow an option to not replace quotes with a regular directed right +% quote/apostrophe (char 0x27), but instead use the undirected quote +% from cmtt (char 0x0d). The undirected quote is ugly, so don't make it +% the default, but it works for pasting with more pdf viewers (at least +% evince), the lilypond developers report. xpdf does work with the +% regular 0x27. +% +\def\codequoteright{% + \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax + \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax + '% + \else \char'15 \fi + \else \char'15 \fi +} +% +% and a similar option for the left quote char vs. a grave accent. +% Modern fonts display ASCII 0x60 as a grave accent, so some people like +% the code environments to do likewise. +% +\def\codequoteleft{% + \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax + \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax + % [Knuth] pp. 380,381,391 + % \relax disables Spanish ligatures ?` and !` of \tt font. + \relax`% + \else \char'22 \fi + \else \char'22 \fi +} + +% [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font. +\def\noligaturesquoteleft{\relax\lq} + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +%% Add scribe-like font environments, plus @l for inline lisp (usually sans +%% serif) and @ii for TeX italic + +% \smartitalic{ARG} outputs arg in italics, followed by an italic correction +% unless the following character is such as not to need one. +\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else + \ptexslash\fi\fi\fi} +\def\smartslanted#1{{\ifusingtt\ttsl\sl #1}\futurelet\next\smartitalicx} +\def\smartitalic#1{{\ifusingtt\ttsl\it #1}\futurelet\next\smartitalicx} + +% like \smartslanted except unconditionally uses \ttsl. +% @var is set to this for defun arguments. +\def\ttslanted#1{{\ttsl #1}\futurelet\next\smartitalicx} + +% @cite is like \smartslanted except unconditionally use \sl. We never want +% ttsl for book titles, do we? +\def\cite#1{{\sl #1}\futurelet\next\smartitalicx} + +\let\i=\smartitalic +\let\slanted=\smartslanted +\def\var#1{{\setupmarkupstyle{var}\smartslanted{#1}}} +\let\dfn=\smartslanted +\let\emph=\smartitalic + +% Explicit font changes: @r, @sc, undocumented @ii. +\def\r#1{{\rm #1}} % roman font +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +% @b, explicit bold. Also @strong. +\def\b#1{{\bf #1}} +\let\strong=\b + +% @sansserif, explicit sans. +\def\sansserif#1{{\sf #1}} + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +% Set sfcode to normal for the chars that usually have another value. +% Can't use plain's \frenchspacing because it uses the `\x notation, and +% sometimes \x has an active definition that messes things up. +% +\catcode`@=11 + \def\plainfrenchspacing{% + \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m + \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m + \def\endofsentencespacefactor{1000}% for @. and friends + } + \def\plainnonfrenchspacing{% + \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 + \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 + \def\endofsentencespacefactor{3000}% for @. and friends + } +\catcode`@=\other +\def\endofsentencespacefactor{3000}% default + +% @t, explicit typewriter. +\def\t#1{% + {\tt \rawbackslash \plainfrenchspacing #1}% + \null +} + +% @samp. +\def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}} + +% definition of @key that produces a lozenge. Doesn't adjust to text size. +%\setfont\keyrm\rmshape{8}{1000}{OT1} +%\font\keysy=cmsy9 +%\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% +% \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% +% \vbox{\hrule\kern-0.4pt +% \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% +% \kern-0.4pt\hrule}% +% \kern-.06em\raise0.4pt\hbox{\angleright}}}} + +% definition of @key with no lozenge. If the current font is already +% monospace, don't change it; that way, we respect @kbdinputstyle. But +% if it isn't monospace, then use \tt. +% +\def\key#1{{\setupmarkupstyle{key}% + \nohyphenation + \ifmonospace\else\tt\fi + #1}\null} + +% ctrl is no longer a Texinfo command. +\def\ctrl #1{{\tt \rawbackslash \hat}#1} + +% @file, @option are the same as @samp. +\let\file=\samp +\let\option=\samp + +% @code is a modification of @t, +% which makes spaces the same size as normal in the surrounding text. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \plainfrenchspacing + #1% + }% + \null +} + +% We *must* turn on hyphenation at `-' and `_' in @code. +% Otherwise, it is too hard to avoid overfull hboxes +% in the Emacs manual, the Library manual, etc. + +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate at a dash. +% -- rms. +{ + \catcode`\-=\active \catcode`\_=\active + \catcode`\'=\active \catcode`\`=\active + \global\let'=\rq \global\let`=\lq % default definitions + % + \global\def\code{\begingroup + \setupmarkupstyle{code}% + % The following should really be moved into \setupmarkupstyle handlers. + \catcode\dashChar=\active \catcode\underChar=\active + \ifallowcodebreaks + \let-\codedash + \let_\codeunder + \else + \let-\realdash + \let_\realunder + \fi + \codex + } +} + +\def\realdash{-} +\def\codedash{-\discretionary{}{}{}} +\def\codeunder{% + % this is all so @math{@code{var_name}+1} can work. In math mode, _ + % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) + % will therefore expand the active definition of _, which is us + % (inside @code that is), therefore an endless loop. + \ifusingtt{\ifmmode + \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. + \else\normalunderscore \fi + \discretionary{}{}{}}% + {\_}% +} +\def\codex #1{\tclose{#1}\endgroup} + +% An additional complication: the above will allow breaks after, e.g., +% each of the four underscores in __typeof__. This is undesirable in +% some manuals, especially if they don't have long identifiers in +% general. @allowcodebreaks provides a way to control this. +% +\newif\ifallowcodebreaks \allowcodebreakstrue + +\def\keywordtrue{true} +\def\keywordfalse{false} + +\parseargdef\allowcodebreaks{% + \def\txiarg{#1}% + \ifx\txiarg\keywordtrue + \allowcodebreakstrue + \else\ifx\txiarg\keywordfalse + \allowcodebreaksfalse + \else + \errhelp = \EMsimple + \errmessage{Unknown @allowcodebreaks option `\txiarg'}% + \fi\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). +\parseargdef\kbdinputstyle{% + \def\txiarg{#1}% + \ifx\txiarg\worddistinct + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% + \else\ifx\txiarg\wordexample + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% + \else\ifx\txiarg\wordcode + \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% + \else + \errhelp = \EMsimple + \errmessage{Unknown @kbdinputstyle option `\txiarg'}% + \fi\fi\fi +} +\def\worddistinct{distinct} +\def\wordexample{example} +\def\wordcode{code} + +% Default is `distinct'. +\kbdinputstyle distinct + +\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} + +% For @indicateurl, @env, @command quotes seem unnecessary, so use \code. +\let\indicateurl=\code +\let\env=\code +\let\command=\code + +% @clicksequence{File @click{} Open ...} +\def\clicksequence#1{\begingroup #1\endgroup} + +% @clickstyle @arrow (by default) +\parseargdef\clickstyle{\def\click{#1}} +\def\click{\arrow} + +% @uref (abbreviation for `urlref') takes an optional (comma-separated) +% second argument specifying the text to display and an optional third +% arg as text to display instead of (rather than in addition to) the url +% itself. First (mandatory) arg is the url. Perhaps eventually put in +% a hypertex \special here. +% +\def\uref#1{\douref #1,,,\finish} +\def\douref#1,#2,#3,#4\finish{\begingroup + \unsepspaces + \pdfurl{#1}% + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt + \unhbox0 % third arg given, show only that + \else + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \ifpdf + \unhbox0 % PDF: 2nd arg given, show only it + \else + \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url + \fi + \else + \code{#1}% only url given, so show it + \fi + \fi + \endlink +\endgroup} + +% @url synonym for @uref, since that's how everyone uses it. +% +\let\url=\uref + +% rms does not like angle brackets --karl, 17may97. +% So now @email is just like @uref, unless we are pdf. +% +%\def\email#1{\angleleft{\tt #1}\angleright} +\ifpdf + \def\email#1{\doemail#1,,\finish} + \def\doemail#1,#2,#3\finish{\begingroup + \unsepspaces + \pdfurl{mailto:#1}% + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi + \endlink + \endgroup} +\else + \let\email=\uref +\fi + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +% @l was never documented to mean ``switch to the Lisp font'', +% and it is not used as such in any manual I can find. We need it for +% Polish suppressed-l. --karl, 22sep96. +%\def\l#1{{\li #1}\null} + +% @acronym for "FBI", "NATO", and the like. +% We print this one point size smaller, since it's intended for +% all-uppercase. +% +\def\acronym#1{\doacronym #1,,\finish} +\def\doacronym#1,#2,#3\finish{% + {\selectfonts\lsize #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi +} + +% @abbr for "Comput. J." and the like. +% No font change, but don't do end-of-sentence spacing. +% +\def\abbr#1{\doabbr #1,,\finish} +\def\doabbr#1,#2,#3\finish{% + {\plainfrenchspacing #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi +} + + +\message{glyphs,} + +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +% +% Since these characters are used in examples, they should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% +\def\point{$\star$} +\def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}} +\def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} +\def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% The @error{} command. +% Adapted from the TeXbook's \boxit. +% +\newbox\errorbox +% +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \reducedsf error\kern-1.5pt} +% +\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{% + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} +% +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @pounds{} is a sterling sign, which Knuth put in the CM italic font. +% +\def\pounds{{\it\$}} + +% @euro{} comes from a separate font, depending on the current style. +% We use the free feym* fonts from the eurosym package by Henrik +% Theiling, which support regular, slanted, bold and bold slanted (and +% "outlined" (blackboard board, sort of) versions, which we don't need). +% It is available from http://www.ctan.org/tex-archive/fonts/eurosym. +% +% Although only regular is the truly official Euro symbol, we ignore +% that. The Euro is designed to be slightly taller than the regular +% font height. +% +% feymr - regular +% feymo - slanted +% feybr - bold +% feybo - bold slanted +% +% There is no good (free) typewriter version, to my knowledge. +% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. +% Hmm. +% +% Also doesn't work in math. Do we need to do math with euro symbols? +% Hope not. +% +% +\def\euro{{\eurofont e}} +\def\eurofont{% + % We set the font at each command, rather than predefining it in + % \textfonts and the other font-switching commands, so that + % installations which never need the symbol don't have to have the + % font installed. + % + % There is only one designed size (nominal 10pt), so we always scale + % that to the current nominal size. + % + % By the way, simply using "at 1em" works for cmr10 and the like, but + % does not work for cmbx10 and other extended/shrunken fonts. + % + \def\eurosize{\csname\curfontsize nominalsize\endcsname}% + % + \ifx\curfontstyle\bfstylename + % bold: + \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize + \else + % regular: + \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize + \fi + \thiseurofont +} + +% Glyphs from the EC fonts. We don't use \let for the aliases, because +% sometimes we redefine the original macro, and the alias should reflect +% the redefinition. +% +% Use LaTeX names for the Icelandic letters. +\def\DH{{\ecfont \char"D0}} % Eth +\def\dh{{\ecfont \char"F0}} % eth +\def\TH{{\ecfont \char"DE}} % Thorn +\def\th{{\ecfont \char"FE}} % thorn +% +\def\guillemetleft{{\ecfont \char"13}} +\def\guillemotleft{\guillemetleft} +\def\guillemetright{{\ecfont \char"14}} +\def\guillemotright{\guillemetright} +\def\guilsinglleft{{\ecfont \char"0E}} +\def\guilsinglright{{\ecfont \char"0F}} +\def\quotedblbase{{\ecfont \char"12}} +\def\quotesinglbase{{\ecfont \char"0D}} +% +% This positioning is not perfect (see the ogonek LaTeX package), but +% we have the precomposed glyphs for the most common cases. We put the +% tests to use those glyphs in the single \ogonek macro so we have fewer +% dummy definitions to worry about for index entries, etc. +% +% ogonek is also used with other letters in Lithuanian (IOU), but using +% the precomposed glyphs for those is not so easy since they aren't in +% the same EC font. +\def\ogonek#1{{% + \def\temp{#1}% + \ifx\temp\macrocharA\Aogonek + \else\ifx\temp\macrochara\aogonek + \else\ifx\temp\macrocharE\Eogonek + \else\ifx\temp\macrochare\eogonek + \else + \ecfont \setbox0=\hbox{#1}% + \ifdim\ht0=1ex\accent"0C #1% + \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}% + \fi + \fi\fi\fi\fi + }% +} +\def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A} +\def\aogonek{{\ecfont \char"A1}}\def\macrochara{a} +\def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E} +\def\eogonek{{\ecfont \char"A6}}\def\macrochare{e} +% +% Use the ec* fonts (cm-super in outline format) for non-CM glyphs. +\def\ecfont{% + % We can't distinguish serif/sans and italic/slanted, but this + % is used for crude hacks anyway (like adding French and German + % quotes to documents typeset with CM, where we lose kerning), so + % hopefully nobody will notice/care. + \edef\ecsize{\csname\curfontsize ecsize\endcsname}% + \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}% + \ifx\curfontstyle\bfstylename + % bold: + \font\thisecfont = ecb\ifusingit{i}{x}\ecsize \space at \nominalsize + \else + % regular: + \font\thisecfont = ec\ifusingit{ti}{rm}\ecsize \space at \nominalsize + \fi + \thisecfont +} + +% @registeredsymbol - R in a circle. The font for the R should really +% be smaller yet, but lllsize is the best we can do for now. +% Adapted from the plain.tex definition of \copyright. +% +\def\registeredsymbol{% + $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}% + \hfil\crcr\Orb}}% + }$% +} + +% @textdegree - the normal degrees sign. +% +\def\textdegree{$^\circ$} + +% Laurent Siebenmann reports \Orb undefined with: +% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 +% so we'll define it if necessary. +% +\ifx\Orb\undefined +\def\Orb{\mathhexbox20D} +\fi + +% Quotes. +\chardef\quotedblleft="5C +\chardef\quotedblright=`\" +\chardef\quoteleft=`\` +\chardef\quoteright=`\' + + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\newif\ifseenauthor +\newif\iffinishedtitlepage + +% Do an implicit @contents or @shortcontents after @end titlepage if the +% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. +% +\newif\ifsetcontentsaftertitlepage + \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue +\newif\ifsetshortcontentsaftertitlepage + \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue + +\parseargdef\shorttitlepage{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\envdef\titlepage{% + % Open one extra group, as we want to close it in the middle of \Etitlepage. + \begingroup + \parindent=0pt \textfonts + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \let\page = \oldpage + \page + \null + }% +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + % + % Need this before the \...aftertitlepage checks so that if they are + % in effect the toc pages will come out with page numbers. + \HEADINGSon + % + % If they want short, they certainly want long too. + \ifsetshortcontentsaftertitlepage + \shortcontents + \contents + \global\let\shortcontents = \relax + \global\let\contents = \relax + \fi + % + \ifsetcontentsaftertitlepage + \contents + \global\let\contents = \relax + \global\let\shortcontents = \relax + \fi +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +%%% Macros to be used within @titlepage: + +\let\subtitlerm=\tenrm +\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} + +\parseargdef\title{% + \checkenv\titlepage + \leftline{\titlefonts\rmisbold #1} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt +} + +\parseargdef\subtitle{% + \checkenv\titlepage + {\subtitlefont \rightline{#1}}% +} + +% @author should come last, but may come many times. +% It can also be used inside @quotation. +% +\parseargdef\author{% + \def\temp{\quotation}% + \ifx\thisenv\temp + \def\quotationauthor{#1}% printed in \Equotation. + \else + \checkenv\titlepage + \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi + {\secfonts\rmisbold \leftline{#1}}% + \fi +} + + +%%% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks\evenheadline % headline on even pages +\newtoks\oddheadline % headline on odd pages +\newtoks\evenfootline % footline on even pages +\newtoks\oddfootline % footline on odd pages + +% Now make TeX use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + + +\def\evenheading{\parsearg\evenheadingxxx} +\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} +\def\evenheadingyyy #1\|#2\|#3\|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddheading{\parsearg\oddheadingxxx} +\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} +\def\oddheadingyyy #1\|#2\|#3\|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} +\def\evenfootingyyy #1\|#2\|#3\|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddfooting{\parsearg\oddfootingxxx} +\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} +\def\oddfootingyyy #1\|#2\|#3\|#4\finish{% + \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% + % + % Leave some space for the footline. Hopefully ok to assume + % @evenfooting will not be used by itself. + \global\advance\pageheight by -12pt + \global\advance\vsize by -12pt +} + +\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} + +% @evenheadingmarks top \thischapter <- chapter at the top of a page +% @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page +% +% The same set of arguments for: +% +% @oddheadingmarks +% @evenfootingmarks +% @oddfootingmarks +% @everyheadingmarks +% @everyfootingmarks + +\def\evenheadingmarks{\headingmarks{even}{heading}} +\def\oddheadingmarks{\headingmarks{odd}{heading}} +\def\evenfootingmarks{\headingmarks{even}{footing}} +\def\oddfootingmarks{\headingmarks{odd}{footing}} +\def\everyheadingmarks#1 {\headingmarks{even}{heading}{#1} + \headingmarks{odd}{heading}{#1} } +\def\everyfootingmarks#1 {\headingmarks{even}{footing}{#1} + \headingmarks{odd}{footing}{#1} } +% #1 = even/odd, #2 = heading/footing, #3 = top/bottom. +\def\headingmarks#1#2#3 {% + \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname + \global\expandafter\let\csname get#1#2marks\endcsname \temp +} + +\everyheadingmarks bottom +\everyfootingmarks bottom + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off at the start of a document, +% and turned `on' after @end titlepage. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\HEADINGSoff{% +\global\evenheadline={\hfil} \global\evenfootline={\hfil} +\global\oddheadline={\hfil} \global\oddfootline={\hfil}} +\HEADINGSoff +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} +\let\contentsalignmacro = \chappager + +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} + +% Subroutines used in generating headings +% This produces Day Month Year style of output. +% Only define if not already defined, in case a txi-??.tex file has set +% up a different format (e.g., txi-cs.tex does this). +\ifx\today\undefined +\def\today{% + \number\day\space + \ifcase\month + \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr + \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug + \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec + \fi + \space\number\year} +\fi + +% @settitle line... specifies the title of the document, for headings. +% It generates no output of its own. +\def\thistitle{\putwordNoTitle} +\def\settitle{\parsearg{\gdef\thistitle}} + + +\message{tables,} +% Tables -- @table, @ftable, @vtable, @item(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @ftable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemindicate{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. However, if + % what follows is an environment such as @example, there will be no + % \parskip glue; then the negative vskip we just inserted would + % cause the example and the item to crash together. So we use this + % bizarre value of 10001 as a signal to \aboveenvbreak to insert + % \parskip glue after all. Section titles are handled this way also. + % + \penalty 10001 + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. + \noindent + % Do this with kerns and \unhbox so that if there is a footnote in + % the item text, it can migrate to the main vertical list and + % eventually be printed. + \nobreak\kern-\tableindent + \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 + \unhbox0 + \nobreak\kern\dimen0 + \endgroup + \itemxneedsnegativevskiptrue + \fi +} + +\def\item{\errmessage{@item while not in a list environment}} +\def\itemx{\errmessage{@itemx while not in a list environment}} + +% @table, @ftable, @vtable. +\envdef\table{% + \let\itemindex\gobble + \tablecheck{table}% +} +\envdef\ftable{% + \def\itemindex ##1{\doind {fn}{\code{##1}}}% + \tablecheck{ftable}% +} +\envdef\vtable{% + \def\itemindex ##1{\doind {vr}{\code{##1}}}% + \tablecheck{vtable}% +} +\def\tablecheck#1{% + \ifnum \the\catcode`\^^M=\active + \endgroup + \errmessage{This command won't work in this context; perhaps the problem is + that we are \inenvironment\thisenv}% + \def\next{\doignore{#1}}% + \else + \let\next\tablex + \fi + \next +} +\def\tablex#1{% + \def\itemindicate{#1}% + \parsearg\tabley +} +\def\tabley#1{% + {% + \makevalueexpandable + \edef\temp{\noexpand\tablez #1\space\space\space}% + \expandafter + }\temp \endtablez +} +\def\tablez #1 #2 #3 #4\endtablez{% + \aboveenvbreak + \ifnum 0#1>0 \advance \leftskip by #1\mil \fi + \ifnum 0#2>0 \tableindent=#2\mil \fi + \ifnum 0#3>0 \advance \rightskip by #3\mil \fi + \itemmax=\tableindent + \advance \itemmax by -\itemmargin + \advance \leftskip by \tableindent + \exdentamount=\tableindent + \parindent = 0pt + \parskip = \smallskipamount + \ifdim \parskip=0pt \parskip=2pt \fi + \let\item = \internalBitem + \let\itemx = \internalBitemx +} +\def\Etable{\endgraf\afterenvbreak} +\let\Eftable\Etable +\let\Evtable\Etable +\let\Eitemize\Etable +\let\Eenumerate\Etable + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\envdef\itemize{\parsearg\doitemize} + +\def\doitemize#1{% + \aboveenvbreak + \itemmax=\itemindent + \advance\itemmax by -\itemmargin + \advance\leftskip by \itemindent + \exdentamount=\itemindent + \parindent=0pt + \parskip=\smallskipamount + \ifdim\parskip=0pt \parskip=2pt \fi + % + % Try typesetting the item mark that if the document erroneously says + % something like @itemize @samp (intending @table), there's an error + % right away at the @itemize. It's not the best error message in the + % world, but it's better than leaving it to the @item. This means if + % the user wants an empty mark, they have to say @w{} not just @w. + \def\itemcontents{#1}% + \setbox0 = \hbox{\itemcontents}% + % + % @itemize with no arg is equivalent to @itemize @bullet. + \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi + % + \let\item=\itemizeitem +} + +% Definition of @item while inside @itemize and @enumerate. +% +\def\itemizeitem{% + \advance\itemno by 1 % for enumerations + {\let\par=\endgraf \smallbreak}% reasonable place to break + {% + % If the document has an @itemize directly after a section title, a + % \nobreak will be last on the list, and \sectionheading will have + % done a \vskip-\parskip. In that case, we don't want to zero + % parskip, or the item text will crash with the heading. On the + % other hand, when there is normal text preceding the item (as there + % usually is), we do want to zero parskip, or there would be too much + % space. In that case, we won't have a \nobreak before. At least + % that's the theory. + \ifnum\lastpenalty<10000 \parskip=0in \fi + \noindent + \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% + % + \vadjust{\penalty 1200}}% not good to break after first line of item. + \flushcr +} + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\envparseargdef\enumerate{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a <number>. + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call \doitemize, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \doitemize{#1.}\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + + +% @multitable macros +% Amy Hendrickson, 8/18/94, 3/6/96 +% +% @multitable ... @end multitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @columnfractions .25 .3 .45 +% @item ... +% +% Numbers following @columnfractions are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab do not need to be on their own lines, but it will not hurt +% if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @end multitable + +% Default dimensions may be reset by user. +% @multitableparskip is vertical space between paragraphs in table. +% @multitableparindent is paragraph indent in table. +% @multitablecolmargin is horizontal space to be left between columns. +% @multitablelinespace is space to leave between table items, baseline +% to baseline. +% 0pt means it depends on current normal line spacing. +% +\newskip\multitableparskip +\newskip\multitableparindent +\newdimen\multitablecolspace +\newskip\multitablelinespace +\multitableparskip=0pt +\multitableparindent=6pt +\multitablecolspace=12pt +\multitablelinespace=0pt + +% Macros used to set up halign preamble: +% +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\columnfractions\relax +\def\xcolumnfractions{\columnfractions} +\newif\ifsetpercent + +% #1 is the @columnfraction, usually a decimal number like .5, but might +% be just 1. We just use it, whatever it is. +% +\def\pickupwholefraction#1 {% + \global\advance\colcount by 1 + \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% + \setuptable +} + +\newcount\colcount +\def\setuptable#1{% + \def\firstarg{#1}% + \ifx\firstarg\xendsetuptable + \let\go = \relax + \else + \ifx\firstarg\xcolumnfractions + \global\setpercenttrue + \else + \ifsetpercent + \let\go\pickupwholefraction + \else + \global\advance\colcount by 1 + \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a + % separator; typically that is always in the input, anyway. + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi + \fi + \ifx\go\pickupwholefraction + % Put the argument back for the \pickupwholefraction call, so + % we'll always have a period there to be parsed. + \def\go{\pickupwholefraction#1}% + \else + \let\go = \setuptable + \fi% + \fi + \go +} + +% multitable-only commands. +% +% @headitem starts a heading row, which we typeset in bold. +% Assignments have to be global since we are inside the implicit group +% of an alignment entry. \everycr resets \everytab so we don't have to +% undo it ourselves. +\def\headitemfont{\b}% for people to use in the template row; not changeable +\def\headitem{% + \checkenv\multitable + \crcr + \global\everytab={\bf}% can't use \headitemfont since the parsing differs + \the\everytab % for the first item +}% +% +% A \tab used to include \hskip1sp. But then the space in a template +% line is not enough. That is bad. So let's go back to just `&' until +% we again encounter the problem the 1sp was intended to solve. +% --karl, nathan@acm.org, 20apr99. +\def\tab{\checkenv\multitable &\the\everytab}% + +% @multitable ... @end multitable definitions: +% +\newtoks\everytab % insert after every tab. +% +\envdef\multitable{% + \vskip\parskip + \startsavinginserts + % + % @item within a multitable starts a normal row. + % We use \def instead of \let so that if one of the multitable entries + % contains an @itemize, we don't choke on the \item (seen as \crcr aka + % \endtemplate) expanding \doitemize. + \def\item{\crcr}% + % + \tolerance=9500 + \hbadness=9500 + \setmultitablespacing + \parskip=\multitableparskip + \parindent=\multitableparindent + \overfullrule=0pt + \global\colcount=0 + % + \everycr = {% + \noalign{% + \global\everytab={}% + \global\colcount=0 % Reset the column counter. + % Check for saved footnotes, etc. + \checkinserts + % Keeps underfull box messages off when table breaks over pages. + %\filbreak + % Maybe so, but it also creates really weird page breaks when the + % table breaks over pages. Wouldn't \vfil be better? Wait until the + % problem manifests itself, so it can be fixed for real --karl. + }% + }% + % + \parsearg\domultitable +} +\def\domultitable#1{% + % To parse everything between @multitable and @item: + \setuptable#1 \endsetuptable + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. + \halign\bgroup &% + \global\advance\colcount by 1 + \multistrut + \vtop{% + % Use the current \colcount to find the correct column width: + \hsize=\expandafter\csname col\the\colcount\endcsname + % + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % + % If the user has set preamble in terms of percent of \hsize we will + % use that dimension as the width of the column, and the \leftskip + % will keep entries from bumping into each other. Table will start at + % left margin and final column will justify at right margin. + % + % Make sure we don't inherit \rightskip from the outer environment. + \rightskip=0pt + \ifnum\colcount=1 + % The first column will be indented with the surrounding text. + \advance\hsize by\leftskip + \else + \ifsetpercent \else + % If user has not set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace. + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: + \leftskip=\multitablecolspace + \fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighting sequences respectively + % marking characters. + \noindent\ignorespaces##\unskip\multistrut + }\cr +} +\def\Emultitable{% + \crcr + \egroup % end the \halign + \global\setpercentfalse +} + +\def\setmultitablespacing{% + \def\multistrut{\strut}% just use the standard line spacing + % + % Compute \multitablelinespace (if not defined by user) for use in + % \multitableparskip calculation. We used define \multistrut based on + % this, but (ironically) that caused the spacing to be off. + % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. +\ifdim\multitablelinespace=0pt +\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip +\global\advance\multitablelinespace by-\ht0 +\fi +%% Test to see if parskip is larger than space between lines of +%% table. If not, do nothing. +%% If so, set to same dimension as multitablelinespace. +\ifdim\multitableparskip>\multitablelinespace +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi% +\ifdim\multitableparskip=0pt +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi} + + +\message{conditionals,} + +% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, +% @ifnotxml always succeed. They currently do nothing; we don't +% attempt to check whether the conditionals are properly nested. But we +% have to remember that they are conditionals, so that @end doesn't +% attempt to close an environment group. +% +\def\makecond#1{% + \expandafter\let\csname #1\endcsname = \relax + \expandafter\let\csname iscond.#1\endcsname = 1 +} +\makecond{iftex} +\makecond{ifnotdocbook} +\makecond{ifnothtml} +\makecond{ifnotinfo} +\makecond{ifnotplaintext} +\makecond{ifnotxml} + +% Ignore @ignore, @ifhtml, @ifinfo, and the like. +% +\def\direntry{\doignore{direntry}} +\def\documentdescription{\doignore{documentdescription}} +\def\docbook{\doignore{docbook}} +\def\html{\doignore{html}} +\def\ifdocbook{\doignore{ifdocbook}} +\def\ifhtml{\doignore{ifhtml}} +\def\ifinfo{\doignore{ifinfo}} +\def\ifnottex{\doignore{ifnottex}} +\def\ifplaintext{\doignore{ifplaintext}} +\def\ifxml{\doignore{ifxml}} +\def\ignore{\doignore{ignore}} +\def\menu{\doignore{menu}} +\def\xml{\doignore{xml}} + +% Ignore text until a line `@end #1', keeping track of nested conditionals. +% +% A count to remember the depth of nesting. +\newcount\doignorecount + +\def\doignore#1{\begingroup + % Scan in ``verbatim'' mode: + \obeylines + \catcode`\@ = \other + \catcode`\{ = \other + \catcode`\} = \other + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \spaceisspace + % + % Count number of #1's that we've seen. + \doignorecount = 0 + % + % Swallow text until we reach the matching `@end #1'. + \dodoignore{#1}% +} + +{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. + \obeylines % + % + \gdef\dodoignore#1{% + % #1 contains the command name as a string, e.g., `ifinfo'. + % + % Define a command to find the next `@end #1'. + \long\def\doignoretext##1^^M@end #1{% + \doignoretextyyy##1^^M@#1\_STOP_}% + % + % And this command to find another #1 command, at the beginning of a + % line. (Otherwise, we would consider a line `@c @ifset', for + % example, to count as an @ifset for nesting.) + \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% + % + % And now expand that command. + \doignoretext ^^M% + }% +} + +\def\doignoreyyy#1{% + \def\temp{#1}% + \ifx\temp\empty % Nothing found. + \let\next\doignoretextzzz + \else % Found a nested condition, ... + \advance\doignorecount by 1 + \let\next\doignoretextyyy % ..., look for another. + % If we're here, #1 ends with ^^M\ifinfo (for example). + \fi + \next #1% the token \_STOP_ is present just after this macro. +} + +% We have to swallow the remaining "\_STOP_". +% +\def\doignoretextzzz#1{% + \ifnum\doignorecount = 0 % We have just found the outermost @end. + \let\next\enddoignore + \else % Still inside a nested condition. + \advance\doignorecount by -1 + \let\next\doignoretext % Look for the next @end. + \fi + \next +} + +% Finish off ignored text. +{ \obeylines% + % Ignore anything after the last `@end #1'; this matters in verbatim + % environments, where otherwise the newline after an ignored conditional + % would result in a blank line in the output. + \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% +} + + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. +% We rely on the fact that \parsearg sets \catcode`\ =10. +% +\parseargdef\set{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + {% + \makevalueexpandable + \def\temp{#2}% + \edef\next{\gdef\makecsname{SET#1}}% + \ifx\temp\empty + \next{}% + \else + \setzzz#2\endsetzzz + \fi + }% +} +% Remove the trailing space \setxxx inserted. +\def\setzzz#1 \endsetzzz{\next{#1}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\parseargdef\clear{% + {% + \makevalueexpandable + \global\expandafter\let\csname SET#1\endcsname=\relax + }% +} + +% @value{foo} gets the text saved in variable foo. +\def\value{\begingroup\makevalueexpandable\valuexxx} +\def\valuexxx#1{\expandablevalue{#1}\endgroup} +{ + \catcode`\- = \active \catcode`\_ = \active + % + \gdef\makevalueexpandable{% + \let\value = \expandablevalue + % We don't want these characters active, ... + \catcode`\-=\other \catcode`\_=\other + % ..., but we might end up with active ones in the argument if + % we're called from @code, as @code{@value{foo-bar_}}, though. + % So \let them to their normal equivalents. + \let-\realdash \let_\normalunderscore + } +} + +% We have this subroutine so that we can handle at least some @value's +% properly in indexes (we call \makevalueexpandable in \indexdummies). +% The command has to be fully expandable (if the variable is set), since +% the result winds up in the index file. This means that if the +% variable's value contains other Texinfo commands, it's almost certain +% it will fail (although perhaps we could fix that with sufficient work +% to do a one-level expansion on the result, instead of complete). +% +\def\expandablevalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + {[No value for ``#1'']}% + \message{Variable `#1', used in @value, is not set.}% + \else + \csname SET#1\endcsname + \fi +} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +% To get special treatment of `@end ifset,' call \makeond and the redefine. +% +\makecond{ifset} +\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} +\def\doifset#1#2{% + {% + \makevalueexpandable + \let\next=\empty + \expandafter\ifx\csname SET#2\endcsname\relax + #1% If not set, redefine \next. + \fi + \expandafter + }\next +} +\def\ifsetfail{\doignore{ifset}} + +% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +% The `\else' inside the `\doifset' parameter is a trick to reuse the +% above code: if the variable is not set, do nothing, if it is set, +% then redefine \next to \ifclearfail. +% +\makecond{ifclear} +\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} +\def\ifclearfail{\doignore{ifclear}} + +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory=\comment + +% @defininfoenclose. +\let\definfoenclose=\comment + + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within macros and \if's. +\edef\newwrite{\makecsname{ptexnewwrite}} + +% \newindex {foo} defines an index named foo. +% It automatically defines \fooindex such that +% \fooindex ...rest of line... puts an entry in the index foo. +% It also defines \fooindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is foo. +% The name of an index should be no more than 2 characters long +% for the sake of vms. +% +\def\newindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 % Open the file + \fi + \expandafter\xdef\csname#1index\endcsname{% % Define @#1index + \noexpand\doindex{#1}} +} + +% @defindex foo == \newindex{foo} +% +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. +% +\def\defcodeindex{\parsearg\newcodeindex} +% +\def\newcodeindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 + \fi + \expandafter\xdef\csname#1index\endcsname{% + \noexpand\docodeindex{#1}}% +} + + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +% +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +% +\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} +\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} + +% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), +% #3 the target index (bar). +\def\dosynindex#1#2#3{% + % Only do \closeout if we haven't already done it, else we'll end up + % closing the target index. + \expandafter \ifx\csname donesynindex#2\endcsname \relax + % The \closeout helps reduce unnecessary open files; the limit on the + % Acorn RISC OS is a mere 16 files. + \expandafter\closeout\csname#2indfile\endcsname + \expandafter\let\csname donesynindex#2\endcsname = 1 + \fi + % redefine \fooindfile: + \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname + \expandafter\let\csname#2indfile\endcsname=\temp + % redefine \fooindex: + \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% +} + +% Define \doindex, the driver for all \fooindex macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is "foo", the name of the index. + +% \doindex just uses \parsearg; it calls \doind for the actual work. +% This is because \doind is more useful to call from other macros. + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} +\def\singleindexer #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} +\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} + +% Take care of Texinfo commands that can appear in an index entry. +% Since there are some commands we want to expand, and others we don't, +% we have to laboriously prevent expansion for those that we don't. +% +\def\indexdummies{% + \escapechar = `\\ % use backslash in output files. + \def\@{@}% change to @@ when we switch to @ as escape char in index files. + \def\ {\realbackslash\space }% + % + % Need these in case \tex is in effect and \{ is a \delimiter again. + % But can't use \lbracecmd and \rbracecmd because texindex assumes + % braces and backslashes are used only as delimiters. + \let\{ = \mylbrace + \let\} = \myrbrace + % + % I don't entirely understand this, but when an index entry is + % generated from a macro call, the \endinput which \scanmacro inserts + % causes processing to be prematurely terminated. This is, + % apparently, because \indexsorttmp is fully expanded, and \endinput + % is an expandable command. The redefinition below makes \endinput + % disappear altogether for that purpose -- although logging shows that + % processing continues to some further point. On the other hand, it + % seems \endinput does not hurt in the printed index arg, since that + % is still getting written without apparent harm. + % + % Sample source (mac-idx3.tex, reported by Graham Percival to + % help-texinfo, 22may06): + % @macro funindex {WORD} + % @findex xyz + % @end macro + % ... + % @funindex commtest + % + % The above is not enough to reproduce the bug, but it gives the flavor. + % + % Sample whatsit resulting: + % .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}} + % + % So: + \let\endinput = \empty + % + % Do the redefinitions. + \commondummies +} + +% For the aux and toc files, @ is the escape character. So we want to +% redefine everything using @ as the escape character (instead of +% \realbackslash, still used for index files). When everything uses @, +% this will be simpler. +% +\def\atdummies{% + \def\@{@@}% + \def\ {@ }% + \let\{ = \lbraceatcmd + \let\} = \rbraceatcmd + % + % Do the redefinitions. + \commondummies + \otherbackslash +} + +% Called from \indexdummies and \atdummies. +% +\def\commondummies{% + % + % \definedummyword defines \#1 as \string\#1\space, thus effectively + % preventing its expansion. This is used only for control% words, + % not control letters, because the \space would be incorrect for + % control characters, but is needed to separate the control word + % from whatever follows. + % + % For control letters, we have \definedummyletter, which omits the + % space. + % + % These can be used both for control words that take an argument and + % those that do not. If it is followed by {arg} in the input, then + % that will dutifully get written to the index (or wherever). + % + \def\definedummyword ##1{\def##1{\string##1\space}}% + \def\definedummyletter##1{\def##1{\string##1}}% + \let\definedummyaccent\definedummyletter + % + \commondummiesnofonts + % + \definedummyletter\_% + % + % Non-English letters. + \definedummyword\AA + \definedummyword\AE + \definedummyword\DH + \definedummyword\L + \definedummyword\O + \definedummyword\OE + \definedummyword\TH + \definedummyword\aa + \definedummyword\ae + \definedummyword\dh + \definedummyword\exclamdown + \definedummyword\l + \definedummyword\o + \definedummyword\oe + \definedummyword\ordf + \definedummyword\ordm + \definedummyword\questiondown + \definedummyword\ss + \definedummyword\th + % + % Although these internal commands shouldn't show up, sometimes they do. + \definedummyword\bf + \definedummyword\gtr + \definedummyword\hat + \definedummyword\less + \definedummyword\sf + \definedummyword\sl + \definedummyword\tclose + \definedummyword\tt + % + \definedummyword\LaTeX + \definedummyword\TeX + % + % Assorted special characters. + \definedummyword\bullet + \definedummyword\comma + \definedummyword\copyright + \definedummyword\registeredsymbol + \definedummyword\dots + \definedummyword\enddots + \definedummyword\equiv + \definedummyword\error + \definedummyword\euro + \definedummyword\guillemetleft + \definedummyword\guillemetright + \definedummyword\guilsinglleft + \definedummyword\guilsinglright + \definedummyword\expansion + \definedummyword\minus + \definedummyword\ogonek + \definedummyword\pounds + \definedummyword\point + \definedummyword\print + \definedummyword\quotedblbase + \definedummyword\quotedblleft + \definedummyword\quotedblright + \definedummyword\quoteleft + \definedummyword\quoteright + \definedummyword\quotesinglbase + \definedummyword\result + \definedummyword\textdegree + % + % We want to disable all macros so that they are not expanded by \write. + \macrolist + % + \normalturnoffactive + % + % Handle some cases of @value -- where it does not contain any + % (non-fully-expandable) commands. + \makevalueexpandable +} + +% \commondummiesnofonts: common to \commondummies and \indexnofonts. +% +\def\commondummiesnofonts{% + % Control letters and accents. + \definedummyletter\!% + \definedummyaccent\"% + \definedummyaccent\'% + \definedummyletter\*% + \definedummyaccent\,% + \definedummyletter\.% + \definedummyletter\/% + \definedummyletter\:% + \definedummyaccent\=% + \definedummyletter\?% + \definedummyaccent\^% + \definedummyaccent\`% + \definedummyaccent\~% + \definedummyword\u + \definedummyword\v + \definedummyword\H + \definedummyword\dotaccent + \definedummyword\ogonek + \definedummyword\ringaccent + \definedummyword\tieaccent + \definedummyword\ubaraccent + \definedummyword\udotaccent + \definedummyword\dotless + % + % Texinfo font commands. + \definedummyword\b + \definedummyword\i + \definedummyword\r + \definedummyword\sc + \definedummyword\t + % + % Commands that take arguments. + \definedummyword\acronym + \definedummyword\cite + \definedummyword\code + \definedummyword\command + \definedummyword\dfn + \definedummyword\email + \definedummyword\emph + \definedummyword\env + \definedummyword\file + \definedummyword\kbd + \definedummyword\key + \definedummyword\math + \definedummyword\option + \definedummyword\pxref + \definedummyword\ref + \definedummyword\samp + \definedummyword\strong + \definedummyword\tie + \definedummyword\uref + \definedummyword\url + \definedummyword\var + \definedummyword\verb + \definedummyword\w + \definedummyword\xref +} + +% \indexnofonts is used when outputting the strings to sort the index +% by, and when constructing control sequence names. It eliminates all +% control sequences and just writes whatever the best ASCII sort string +% would be for a given command (usually its argument). +% +\def\indexnofonts{% + % Accent commands should become @asis. + \def\definedummyaccent##1{\let##1\asis}% + % We can just ignore other control letters. + \def\definedummyletter##1{\let##1\empty}% + % Hopefully, all control words can become @asis. + \let\definedummyword\definedummyaccent + % + \commondummiesnofonts + % + % Don't no-op \tt, since it isn't a user-level command + % and is used in the definitions of the active chars like <, >, |, etc. + % Likewise with the other plain tex font commands. + %\let\tt=\asis + % + \def\ { }% + \def\@{@}% + % how to handle braces? + \def\_{\normalunderscore}% + % + % Non-English letters. + \def\AA{AA}% + \def\AE{AE}% + \def\DH{DZZ}% + \def\L{L}% + \def\OE{OE}% + \def\O{O}% + \def\TH{ZZZ}% + \def\aa{aa}% + \def\ae{ae}% + \def\dh{dzz}% + \def\exclamdown{!}% + \def\l{l}% + \def\oe{oe}% + \def\ordf{a}% + \def\ordm{o}% + \def\o{o}% + \def\questiondown{?}% + \def\ss{ss}% + \def\th{zzz}% + % + \def\LaTeX{LaTeX}% + \def\TeX{TeX}% + % + % Assorted special characters. + % (The following {} will end up in the sort string, but that's ok.) + \def\bullet{bullet}% + \def\comma{,}% + \def\copyright{copyright}% + \def\dots{...}% + \def\enddots{...}% + \def\equiv{==}% + \def\error{error}% + \def\euro{euro}% + \def\expansion{==>}% + \def\guillemetleft{<<}% + \def\guillemetright{>>}% + \def\guilsinglleft{<}% + \def\guilsinglright{>}% + \def\minus{-}% + \def\point{.}% + \def\pounds{pounds}% + \def\print{-|}% + \def\quotedblbase{"}% + \def\quotedblleft{"}% + \def\quotedblright{"}% + \def\quoteleft{`}% + \def\quoteright{'}% + \def\quotesinglbase{,}% + \def\registeredsymbol{R}% + \def\result{=>}% + \def\textdegree{o}% + % + % We need to get rid of all macros, leaving only the arguments (if present). + % Of course this is not nearly correct, but it is the best we can do for now. + % makeinfo does not expand macros in the argument to @deffn, which ends up + % writing an index entry, and texindex isn't prepared for an index sort entry + % that starts with \. + % + % Since macro invocations are followed by braces, we can just redefine them + % to take a single TeX argument. The case of a macro invocation that + % goes to end-of-line is not handled. + % + \macrolist +} + +\let\indexbackslash=0 %overridden during \printindex. +\let\SETmarginindex=\relax % put index entries in margin (undocumented)? + +% Most index entries go through here, but \dosubind is the general case. +% #1 is the index name, #2 is the entry text. +\def\doind#1#2{\dosubind{#1}{#2}{}} + +% Workhorse for all \fooindexes. +% #1 is name of index, #2 is stuff to put there, #3 is subentry -- +% empty if called from \doind, as we usually are (the main exception +% is with most defuns, which call us directly). +% +\def\dosubind#1#2#3{% + \iflinks + {% + % Store the main index entry text (including the third arg). + \toks0 = {#2}% + % If third arg is present, precede it with a space. + \def\thirdarg{#3}% + \ifx\thirdarg\empty \else + \toks0 = \expandafter{\the\toks0 \space #3}% + \fi + % + \edef\writeto{\csname#1indfile\endcsname}% + % + \safewhatsit\dosubindwrite + }% + \fi +} + +% Write the entry in \toks0 to the index file: +% +\def\dosubindwrite{% + % Put the index entry in the margin if desired. + \ifx\SETmarginindex\relax\else + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}% + \fi + % + % Remember, we are within a group. + \indexdummies % Must do this here, since \bf, etc expand at this stage + \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now + % so it will be output as is; and it will print as backslash. + % + % Process the index entry with all font commands turned off, to + % get the string to sort by. + {\indexnofonts + \edef\temp{\the\toks0}% need full expansion + \xdef\indexsorttmp{\temp}% + }% + % + % Set up the complete index entry, with both the sort key and + % the original text, including any font commands. We write + % three arguments to \entry to the .?? file (four in the + % subentry case), texindex reduces to two when writing the .??s + % sorted result. + \edef\temp{% + \write\writeto{% + \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}% + }% + \temp +} + +% Take care of unwanted page breaks/skips around a whatsit: +% +% If a skip is the last thing on the list now, preserve it +% by backing up by \lastskip, doing the \write, then inserting +% the skip again. Otherwise, the whatsit generated by the +% \write or \pdfdest will make \lastskip zero. The result is that +% sequences like this: +% @end defun +% @tindex whatever +% @defun ... +% will have extra space inserted, because the \medbreak in the +% start of the @defun won't see the skip inserted by the @end of +% the previous defun. +% +% But don't do any of this if we're not in vertical mode. We +% don't want to do a \vskip and prematurely end a paragraph. +% +% Avoid page breaks due to these extra skips, too. +% +% But wait, there is a catch there: +% We'll have to check whether \lastskip is zero skip. \ifdim is not +% sufficient for this purpose, as it ignores stretch and shrink parts +% of the skip. The only way seems to be to check the textual +% representation of the skip. +% +% The following is almost like \def\zeroskipmacro{0.0pt} except that +% the ``p'' and ``t'' characters have catcode \other, not 11 (letter). +% +\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} +% +\newskip\whatsitskip +\newcount\whatsitpenalty +% +% ..., ready, GO: +% +\def\safewhatsit#1{% +\ifhmode + #1% +\else + % \lastskip and \lastpenalty cannot both be nonzero simultaneously. + \whatsitskip = \lastskip + \edef\lastskipmacro{\the\lastskip}% + \whatsitpenalty = \lastpenalty + % + % If \lastskip is nonzero, that means the last item was a + % skip. And since a skip is discardable, that means this + % -\whatsitskip glue we're inserting is preceded by a + % non-discardable item, therefore it is not a potential + % breakpoint, therefore no \nobreak needed. + \ifx\lastskipmacro\zeroskipmacro + \else + \vskip-\whatsitskip + \fi + % + #1% + % + \ifx\lastskipmacro\zeroskipmacro + % If \lastskip was zero, perhaps the last item was a penalty, and + % perhaps it was >=10000, e.g., a \nobreak. In that case, we want + % to re-insert the same penalty (values >10000 are used for various + % signals); since we just inserted a non-discardable item, any + % following glue (such as a \parskip) would be a breakpoint. For example: + % + % @deffn deffn-whatever + % @vindex index-whatever + % Description. + % would allow a break between the index-whatever whatsit + % and the "Description." paragraph. + \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi + \else + % On the other hand, if we had a nonzero \lastskip, + % this make-up glue would be preceded by a non-discardable item + % (the whatsit from the \write), so we must insert a \nobreak. + \nobreak\vskip\whatsitskip + \fi +\fi +} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% @printindex causes a particular index (the ??s file) to get printed. +% It does not print any chapter heading (usually an @unnumbered). +% +\parseargdef\printindex{\begingroup + \dobreak \chapheadingskip{10000}% + % + \smallfonts \rm + \tolerance = 9500 + \plainfrenchspacing + \everypar = {}% don't want the \kern\-parindent from indentation suppression. + % + % See if the index file exists and is nonempty. + % Change catcode of @ here so that if the index file contains + % \initial {@} + % as its first line, TeX doesn't complain about mismatched braces + % (because it thinks @} is a control sequence). + \catcode`\@ = 11 + \openin 1 \jobname.#1s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + \putwordIndexNonexistent + \else + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \temp + \ifeof 1 + \putwordIndexIsEmpty + \else + % Index files are almost Texinfo source, but we use \ as the escape + % character. It would be better to use @, but that's too big a change + % to make right now. + \def\indexbackslash{\backslashcurfont}% + \catcode`\\ = 0 + \escapechar = `\\ + \begindoublecolumns + \input \jobname.#1s + \enddoublecolumns + \fi + \fi + \closein 1 +\endgroup} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +\def\initial#1{{% + % Some minor font changes for the special characters. + \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt + % + % Remove any glue we may have, we'll be inserting our own. + \removelastskip + % + % We like breaks before the index initials, so insert a bonus. + \nobreak + \vskip 0pt plus 3\baselineskip + \penalty 0 + \vskip 0pt plus -3\baselineskip + % + % Typeset the initial. Making this add up to a whole number of + % baselineskips increases the chance of the dots lining up from column + % to column. It still won't often be perfect, because of the stretch + % we need before each entry, but it's better. + % + % No shrink because it confuses \balancecolumns. + \vskip 1.67\baselineskip plus .5\baselineskip + \leftline{\secbf #1}% + % Do our best not to break after the initial. + \nobreak + \vskip .33\baselineskip plus .1\baselineskip +}} + +% \entry typesets a paragraph consisting of the text (#1), dot leaders, and +% then page number (#2) flushed to the right margin. It is used for index +% and table of contents entries. The paragraph is indented by \leftskip. +% +% A straightforward implementation would start like this: +% \def\entry#1#2{... +% But this freezes the catcodes in the argument, and can cause problems to +% @code, which sets - active. This problem was fixed by a kludge--- +% ``-'' was active throughout whole index, but this isn't really right. +% +% The right solution is to prevent \entry from swallowing the whole text. +% --kasal, 21nov03 +\def\entry{% + \begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent = 2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % A bit of stretch before each entry for the benefit of balancing + % columns. + \vskip 0pt plus1pt + % + % Swallow the left brace of the text (first parameter): + \afterassignment\doentry + \let\temp = +} +\def\doentry{% + \bgroup % Instead of the swallowed brace. + \noindent + \aftergroup\finishentry + % And now comes the text of the entry. +} +\def\finishentry#1{% + % #1 is the page number. + % + % The following is kludged to not output a line of dots in the index if + % there are no page numbers. The next person who breaks this will be + % cursed by a Unix daemon. + \setbox\boxA = \hbox{#1}% + \ifdim\wd\boxA = 0pt + \ % + \else + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ifpdf + \pdfgettoks#1.% + \ \the\toksA + \else + \ #1% + \fi + \fi + \par + \endgroup +} + +% Like plain.tex's \dotfill, except uses up at least 1 em. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill} + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm +\def\secondary#1#2{{% + \parfillskip=0in + \parskip=0in + \hangindent=1in + \hangafter=1 + \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill + \ifpdf + \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. + \else + #2 + \fi + \par +}} + +% Define two-column mode, which we use to typeset indexes. +% Adapted from the TeXbook, page 416, which is to say, +% the manmac.tex format used to print the TeXbook itself. +\catcode`\@=11 + +\newbox\partialpage +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns + % Grab any single-column material above us. + \output = {% + % + % Here is a possibility not foreseen in manmac: if we accumulate a + % whole lot of material, we might end up calling this \output + % routine twice in a row (see the doublecol-lose test, which is + % essentially a couple of indexes with @setchapternewpage off). In + % that case we just ship out what is in \partialpage with the normal + % output routine. Generally, \partialpage will be empty when this + % runs and this will be a no-op. See the indexspread.tex test case. + \ifvoid\partialpage \else + \onepageout{\pagecontents\partialpage}% + \fi + % + \global\setbox\partialpage = \vbox{% + % Unvbox the main output page. + \unvbox\PAGE + \kern-\topskip \kern\baselineskip + }% + }% + \eject % run that output routine to set \partialpage + % + % Use the double-column output routine for subsequent pages. + \output = {\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it in one place. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +-<1pt) + % as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \vsize = 2\vsize +} + +% The double-column output routine for all double-column pages except +% the last. +% +\def\doublecolumnout{% + \splittopskip=\topskip \splitmaxdepth=\maxdepth + % Get the available space for the double columns -- the normal + % (undoubled) page height minus any material left over from the + % previous page. + \dimen@ = \vsize + \divide\dimen@ by 2 + \advance\dimen@ by -\ht\partialpage + % + % box0 will be the left-hand column, box2 the right. + \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ + \onepageout\pagesofar + \unvbox255 + \penalty\outputpenalty +} +% +% Re-output the contents of the output page -- any previous material, +% followed by the two boxes we just split, in box0 and box2. +\def\pagesofar{% + \unvbox\partialpage + % + \hsize = \doublecolumnhsize + \wd0=\hsize \wd2=\hsize + \hbox to\pagewidth{\box0\hfil\box2}% +} +% +% All done with double columns. +\def\enddoublecolumns{% + % The following penalty ensures that the page builder is exercised + % _before_ we change the output routine. This is necessary in the + % following situation: + % + % The last section of the index consists only of a single entry. + % Before this section, \pagetotal is less than \pagegoal, so no + % break occurs before the last section starts. However, the last + % section, consisting of \initial and the single \entry, does not + % fit on the page and has to be broken off. Without the following + % penalty the page builder will not be exercised until \eject + % below, and by that time we'll already have changed the output + % routine to the \balancecolumns version, so the next-to-last + % double-column page will be processed with \balancecolumns, which + % is wrong: The two columns will go to the main vertical list, with + % the broken-off section in the recent contributions. As soon as + % the output routine finishes, TeX starts reconsidering the page + % break. The two columns and the broken-off section both fit on the + % page, because the two columns now take up only half of the page + % goal. When TeX sees \eject from below which follows the final + % section, it invokes the new output routine that we've set after + % \balancecolumns below; \onepageout will try to fit the two columns + % and the final section into the vbox of \pageheight (see + % \pagebody), causing an overfull box. + % + % Note that glue won't work here, because glue does not exercise the + % page builder, unlike penalties (see The TeXbook, pp. 280-281). + \penalty0 + % + \output = {% + % Split the last of the double-column material. Leave it on the + % current page, no automatic page break. + \balancecolumns + % + % If we end up splitting too much material for the current page, + % though, there will be another page break right after this \output + % invocation ends. Having called \balancecolumns once, we do not + % want to call it again. Therefore, reset \output to its normal + % definition right away. (We hope \balancecolumns will never be + % called on to balance too much material, but if it is, this makes + % the output somewhat more palatable.) + \global\output = {\onepageout{\pagecontents\PAGE}}% + }% + \eject + \endgroup % started in \begindoublecolumns + % + % \pagegoal was set to the doubled \vsize above, since we restarted + % the current page. We're now back to normal single-column + % typesetting, so reset \pagegoal to the normal \vsize (after the + % \endgroup where \vsize got restored). + \pagegoal = \vsize +} +% +% Called at the end of the double column material. +\def\balancecolumns{% + \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. + \dimen@ = \ht0 + \advance\dimen@ by \topskip + \advance\dimen@ by-\baselineskip + \divide\dimen@ by 2 % target to split to + %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}% + \splittopskip = \topskip + % Loop until we get a decent breakpoint. + {% + \vbadness = 10000 + \loop + \global\setbox3 = \copy0 + \global\setbox1 = \vsplit3 to \dimen@ + \ifdim\ht3>\dimen@ + \global\advance\dimen@ by 1pt + \repeat + }% + %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}% + \setbox0=\vbox to\dimen@{\unvbox1}% + \setbox2=\vbox to\dimen@{\unvbox3}% + % + \pagesofar +} +\catcode`\@ = \other + + +\message{sectioning,} +% Chapters, sections, etc. + +% \unnumberedno is an oxymoron, of course. But we count the unnumbered +% sections so that we can refer to them unambiguously in the pdf +% outlines by their "section number". We avoid collisions with chapter +% numbers by starting them at 10000. (If a document ever has 10000 +% chapters, we're in trouble anyway, I'm sure.) +\newcount\unnumberedno \unnumberedno = 10000 +\newcount\chapno +\newcount\secno \secno=0 +\newcount\subsecno \subsecno=0 +\newcount\subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount\appendixno \appendixno = `\@ +% +% \def\appendixletter{\char\the\appendixno} +% We do the following ugly conditional instead of the above simple +% construct for the sake of pdftex, which needs the actual +% letter in the expansion, not just typeset. +% +\def\appendixletter{% + \ifnum\appendixno=`A A% + \else\ifnum\appendixno=`B B% + \else\ifnum\appendixno=`C C% + \else\ifnum\appendixno=`D D% + \else\ifnum\appendixno=`E E% + \else\ifnum\appendixno=`F F% + \else\ifnum\appendixno=`G G% + \else\ifnum\appendixno=`H H% + \else\ifnum\appendixno=`I I% + \else\ifnum\appendixno=`J J% + \else\ifnum\appendixno=`K K% + \else\ifnum\appendixno=`L L% + \else\ifnum\appendixno=`M M% + \else\ifnum\appendixno=`N N% + \else\ifnum\appendixno=`O O% + \else\ifnum\appendixno=`P P% + \else\ifnum\appendixno=`Q Q% + \else\ifnum\appendixno=`R R% + \else\ifnum\appendixno=`S S% + \else\ifnum\appendixno=`T T% + \else\ifnum\appendixno=`U U% + \else\ifnum\appendixno=`V V% + \else\ifnum\appendixno=`W W% + \else\ifnum\appendixno=`X X% + \else\ifnum\appendixno=`Y Y% + \else\ifnum\appendixno=`Z Z% + % The \the is necessary, despite appearances, because \appendixletter is + % expanded while writing the .toc file. \char\appendixno is not + % expandable, thus it is written literally, thus all appendixes come out + % with the same letter (or @) in the toc without it. + \else\char\the\appendixno + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} + +% Each @chapter defines these (using marks) as the number+name, number +% and name of the chapter. Page headings and footings can use +% these. @section does likewise. +\def\thischapter{} +\def\thischapternum{} +\def\thischaptername{} +\def\thissection{} +\def\thissectionnum{} +\def\thissectionname{} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% we only have subsub. +\chardef\maxseclevel = 3 +% +% A numbered section within an unnumbered changes to unnumbered too. +% To achive this, remember the "biggest" unnum. sec. we are currently in: +\chardef\unmlevel = \maxseclevel +% +% Trace whether the current chapter is an appendix or not: +% \chapheadtype is "N" or "A", unnumbered chapters are ignored. +\def\chapheadtype{N} + +% Choose a heading macro +% #1 is heading type +% #2 is heading level +% #3 is text for heading +\def\genhead#1#2#3{% + % Compute the abs. sec. level: + \absseclevel=#2 + \advance\absseclevel by \secbase + % Make sure \absseclevel doesn't fall outside the range: + \ifnum \absseclevel < 0 + \absseclevel = 0 + \else + \ifnum \absseclevel > 3 + \absseclevel = 3 + \fi + \fi + % The heading type: + \def\headtype{#1}% + \if \headtype U% + \ifnum \absseclevel < \unmlevel + \chardef\unmlevel = \absseclevel + \fi + \else + % Check for appendix sections: + \ifnum \absseclevel = 0 + \edef\chapheadtype{\headtype}% + \else + \if \headtype A\if \chapheadtype N% + \errmessage{@appendix... within a non-appendix chapter}% + \fi\fi + \fi + % Check for numbered within unnumbered: + \ifnum \absseclevel > \unmlevel + \def\headtype{U}% + \else + \chardef\unmlevel = 3 + \fi + \fi + % Now print the heading: + \if \headtype U% + \ifcase\absseclevel + \unnumberedzzz{#3}% + \or \unnumberedseczzz{#3}% + \or \unnumberedsubseczzz{#3}% + \or \unnumberedsubsubseczzz{#3}% + \fi + \else + \if \headtype A% + \ifcase\absseclevel + \appendixzzz{#3}% + \or \appendixsectionzzz{#3}% + \or \appendixsubseczzz{#3}% + \or \appendixsubsubseczzz{#3}% + \fi + \else + \ifcase\absseclevel + \chapterzzz{#3}% + \or \seczzz{#3}% + \or \numberedsubseczzz{#3}% + \or \numberedsubsubseczzz{#3}% + \fi + \fi + \fi + \suppressfirstparagraphindent +} + +% an interface: +\def\numhead{\genhead N} +\def\apphead{\genhead A} +\def\unnmhead{\genhead U} + +% @chapter, @appendix, @unnumbered. Increment top-level counter, reset +% all lower-level sectioning counters to zero. +% +% Also set \chaplevelprefix, which we prepend to @float sequence numbers +% (e.g., figures), q.v. By default (before any chapter), that is empty. +\let\chaplevelprefix = \empty +% +\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz#1{% + % section resetting is \global in case the chapter is in a group, such + % as an @include file. + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\chapno by 1 + % + % Used for \float. + \gdef\chaplevelprefix{\the\chapno.}% + \resetallfloatnos + % + % \putwordChapter can contain complex things in translations. + \toks0=\expandafter{\putwordChapter}% + \message{\the\toks0 \space \the\chapno}% + % + % Write the actual heading. + \chapmacro{#1}{Ynumbered}{\the\chapno}% + % + % So @section and the like are numbered underneath this chapter. + \global\let\section = \numberedsec + \global\let\subsection = \numberedsubsec + \global\let\subsubsection = \numberedsubsubsec +} + +\outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz +% +\def\appendixzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\appendixno by 1 + \gdef\chaplevelprefix{\appendixletter.}% + \resetallfloatnos + % + % \putwordAppendix can contain complex things in translations. + \toks0=\expandafter{\putwordAppendix}% + \message{\the\toks0 \space \appendixletter}% + % + \chapmacro{#1}{Yappendix}{\appendixletter}% + % + \global\let\section = \appendixsec + \global\let\subsection = \appendixsubsec + \global\let\subsubsection = \appendixsubsubsec +} + +\outer\parseargdef\unnumbered{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz +\def\unnumberedzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\unnumberedno by 1 + % + % Since an unnumbered has no number, no prefix for figures. + \global\let\chaplevelprefix = \empty + \resetallfloatnos + % + % This used to be simply \message{#1}, but TeX fully expands the + % argument to \message. Therefore, if #1 contained @-commands, TeX + % expanded them. For example, in `@unnumbered The @cite{Book}', TeX + % expanded @cite (which turns out to cause errors because \cite is meant + % to be executed, not expanded). + % + % Anyway, we don't want the fully-expanded definition of @cite to appear + % as a result of the \message, we just want `@cite' itself. We use + % \the<toks register> to achieve this: TeX expands \the<toks> only once, + % simply yielding the contents of <toks register>. (We also do this for + % the toc entries.) + \toks0 = {#1}% + \message{(\the\toks0)}% + % + \chapmacro{#1}{Ynothing}{\the\unnumberedno}% + % + \global\let\section = \unnumberedsec + \global\let\subsection = \unnumberedsubsec + \global\let\subsubsection = \unnumberedsubsubsec +} + +% @centerchap is like @unnumbered, but the heading is centered. +\outer\parseargdef\centerchap{% + % Well, we could do the following in a group, but that would break + % an assumption that \chapmacro is called at the outermost level. + % Thus we are safer this way: --kasal, 24feb04 + \let\centerparametersmaybe = \centerparameters + \unnmhead0{#1}% + \let\centerparametersmaybe = \relax +} + +% @top is like @unnumbered. +\let\top\unnumbered + +% Sections. +\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz +\def\seczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% +} + +\outer\parseargdef\appendixsection{\apphead1{#1}} % normally calls appendixsectionzzz +\def\appendixsectionzzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% +} +\let\appendixsec\appendixsection + +\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} % normally calls unnumberedseczzz +\def\unnumberedseczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% +} + +% Subsections. +\outer\parseargdef\numberedsubsec{\numhead2{#1}} % normally calls numberedsubseczzz +\def\numberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% +} + +\outer\parseargdef\appendixsubsec{\apphead2{#1}} % normally calls appendixsubseczzz +\def\appendixsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno}% +} + +\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} %normally calls unnumberedsubseczzz +\def\unnumberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno}% +} + +% Subsubsections. +\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} % normally numberedsubsubseczzz +\def\numberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynumbered}% + {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} % normally appendixsubsubseczzz +\def\appendixsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} %normally unnumberedsubsubseczzz +\def\unnumberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\let\section = \numberedsec +\let\subsection = \numberedsubsec +\let\subsubsection = \numberedsubsubsec + +% 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 +} + +\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 + \suppressfirstparagraphindent +} + +% @heading, @subheading, @subsubheading. +\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +%%% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +%%% Define plain chapter starts, and page on/off switching for it +% Parameter controlling skip before chapter headings (if needed) + +\newskip\chapheadingskip + +\def\chapbreak{\dobreak \chapheadingskip {-4000}} +\def\chappager{\par\vfill\supereject} +% Because \domark is called before \chapoddpage, the filler page will +% get the headings for the next chapter, which is wrong. But we don't +% care -- we just disable all headings on the filler page. +\def\chapoddpage{% + \chappager + \ifodd\pageno \else + \begingroup + \evenheadline={\hfil}\evenfootline={\hfil}% + \oddheadline={\hfil}\oddfootline={\hfil}% + \hbox to 0pt{}% + \chappager + \endgroup + \fi +} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{% +\global\let\contentsalignmacro = \chapoddpage +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +% Chapter opening. +% +% #1 is the text, #2 is the section type (Ynumbered, Ynothing, +% Yappendix, Yomitfromtoc), #3 the chapter number. +% +% To test against our argument. +\def\Ynothingkeyword{Ynothing} +\def\Yomitfromtockeyword{Yomitfromtoc} +\def\Yappendixkeyword{Yappendix} +% +\def\chapmacro#1#2#3{% + % Insert the first mark before the heading break (see notes for \domark). + \let\prevchapterdefs=\lastchapterdefs + \let\prevsectiondefs=\lastsectiondefs + \gdef\lastsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}% + \gdef\thissection{}}% + % + \def\temptype{#2}% + \ifx\temptype\Ynothingkeyword + \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% + \gdef\thischapter{\thischaptername}}% + \else\ifx\temptype\Yomitfromtockeyword + \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% + \gdef\thischapter{}}% + \else\ifx\temptype\Yappendixkeyword + \toks0={#1}% + \xdef\lastchapterdefs{% + \gdef\noexpand\thischaptername{\the\toks0}% + \gdef\noexpand\thischapternum{\appendixletter}% + % \noexpand\putwordAppendix avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thischapter{\noexpand\putwordAppendix{} + \noexpand\thischapternum: + \noexpand\thischaptername}% + }% + \else + \toks0={#1}% + \xdef\lastchapterdefs{% + \gdef\noexpand\thischaptername{\the\toks0}% + \gdef\noexpand\thischapternum{\the\chapno}% + % \noexpand\putwordChapter avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thischapter{\noexpand\putwordChapter{} + \noexpand\thischapternum: + \noexpand\thischaptername}% + }% + \fi\fi\fi + % + % Output the mark. Pass it through \safewhatsit, to take care of + % the preceding space. + \safewhatsit\domark + % + % Insert the chapter heading break. + \pchapsepmacro + % + % Now the second mark, after the heading break. No break points + % between here and the heading. + \let\prevchapterdefs=\lastchapterdefs + \let\prevsectiondefs=\lastsectiondefs + \domark + % + {% + \chapfonts \rmisbold + % + % Have to define \lastsection before calling \donoderef, because the + % xref code eventually uses it. On the other hand, it has to be called + % after \pchapsepmacro, or the headline will change too soon. + \gdef\lastsection{#1}% + % + % Only insert the separating space if we have a chapter/appendix + % number, and don't print the unnumbered ``number''. + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unnchap}% + \else\ifx\temptype\Yomitfromtockeyword + \setbox0 = \hbox{}% contents like unnumbered, but no toc entry + \def\toctype{omit}% + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% + \def\toctype{app}% + \else + \setbox0 = \hbox{#3\enspace}% + \def\toctype{numchap}% + \fi\fi\fi + % + % Write the toc entry for this chapter. Must come before the + % \donoderef, because we include the current node name in the toc + % entry, and \donoderef resets it to empty. + \writetocentry{\toctype}{#1}{#3}% + % + % For pdftex, we have to write out the node definition (aka, make + % the pdfdest) after any page break, but before the actual text has + % been typeset. If the destination for the pdf outline is after the + % text, then jumping from the outline may wind up with the text not + % being visible, for instance under high magnification. + \donoderef{#2}% + % + % Typeset the actual heading. + \nobreak % Avoid page breaks at the interline glue. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright + \hangindent=\wd0 \centerparametersmaybe + \unhbox0 #1\par}% + }% + \nobreak\bigskip % no page break after a chapter title + \nobreak +} + +% @centerchap -- centered and unnumbered. +\let\centerparametersmaybe = \relax +\def\centerparameters{% + \advance\rightskip by 3\rightskip + \leftskip = \rightskip + \parfillskip = 0pt +} + + +% I don't think this chapter style is supported any more, so I'm not +% updating it with the new noderef stuff. We'll see. --karl, 11aug03. +% +\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 +} +\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 +} +\def\CHAPFopen{% + \global\let\chapmacro=\chfopen + \global\let\centerchapmacro=\centerchfopen} + + +% Section titles. These macros combine the section number parts and +% call the generic \sectionheading to do the printing. +% +\newskip\secheadingskip +\def\secheadingbreak{\dobreak \secheadingskip{-1000}} + +% Subsection titles. +\newskip\subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} + +% Subsubsection titles. +\def\subsubsecheadingskip{\subsecheadingskip} +\def\subsubsecheadingbreak{\subsecheadingbreak} + + +% Print any size, any type, section title. +% +% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is +% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the +% section number. +% +\def\seckeyword{sec} +% +\def\sectionheading#1#2#3#4{% + {% + % Switch to the right set of fonts. + \csname #2fonts\endcsname \rmisbold + % + \def\sectionlevel{#2}% + \def\temptype{#3}% + % + % Insert first mark before the heading break (see notes for \domark). + \let\prevsectiondefs=\lastsectiondefs + \ifx\temptype\Ynothingkeyword + \ifx\sectionlevel\seckeyword + \gdef\lastsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}% + \gdef\thissection{\thissectionname}}% + \fi + \else\ifx\temptype\Yomitfromtockeyword + % Don't redefine \thissection. + \else\ifx\temptype\Yappendixkeyword + \ifx\sectionlevel\seckeyword + \toks0={#1}% + \xdef\lastsectiondefs{% + \gdef\noexpand\thissectionname{\the\toks0}% + \gdef\noexpand\thissectionnum{#4}% + % \noexpand\putwordSection avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thissection{\noexpand\putwordSection{} + \noexpand\thissectionnum: + \noexpand\thissectionname}% + }% + \fi + \else + \ifx\sectionlevel\seckeyword + \toks0={#1}% + \xdef\lastsectiondefs{% + \gdef\noexpand\thissectionname{\the\toks0}% + \gdef\noexpand\thissectionnum{#4}% + % \noexpand\putwordSection avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thissection{\noexpand\putwordSection{} + \noexpand\thissectionnum: + \noexpand\thissectionname}% + }% + \fi + \fi\fi\fi + % + % Go into vertical mode. Usually we'll already be there, but we + % don't want the following whatsit to end up in a preceding paragraph + % if the document didn't happen to have a blank line. + \par + % + % Output the mark. Pass it through \safewhatsit, to take care of + % the preceding space. + \safewhatsit\domark + % + % Insert space above the heading. + \csname #2headingbreak\endcsname + % + % Now the second mark, after the heading break. No break points + % between here and the heading. + \let\prevsectiondefs=\lastsectiondefs + \domark + % + % Only insert the space after the number if we have a section number. + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unn}% + \gdef\lastsection{#1}% + \else\ifx\temptype\Yomitfromtockeyword + % for @headings -- no section number, don't include in toc, + % and don't redefine \lastsection. + \setbox0 = \hbox{}% + \def\toctype{omit}% + \let\sectionlevel=\empty + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{#4\enspace}% + \def\toctype{app}% + \gdef\lastsection{#1}% + \else + \setbox0 = \hbox{#4\enspace}% + \def\toctype{num}% + \gdef\lastsection{#1}% + \fi\fi\fi + % + % Write the toc entry (before \donoderef). See comments in \chapmacro. + \writetocentry{\toctype\sectionlevel}{#1}{#4}% + % + % Write the node reference (= pdf destination for pdftex). + % Again, see comments in \chapmacro. + \donoderef{#3}% + % + % Interline glue will be inserted when the vbox is completed. + % That glue will be a valid breakpoint for the page, since it'll be + % preceded by a whatsit (usually from the \donoderef, or from the + % \writetocentry if there was no node). We don't want to allow that + % break, since then the whatsits could end up on page n while the + % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. + \nobreak + % + % Output the actual section heading. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright + \hangindent=\wd0 % zero if no section number + \unhbox0 #1}% + }% + % Add extra space after the heading -- half of whatever came above it. + % Don't allow stretch, though. + \kern .5 \csname #2headingskip\endcsname + % + % Do not let the kern be a potential breakpoint, as it would be if it + % was followed by glue. + \nobreak + % + % We'll almost certainly start a paragraph next, so don't let that + % glue accumulate. (Not a breakpoint because it's preceded by a + % discardable item.) + \vskip-\parskip + % + % This is purely so the last item on the list is a known \penalty > + % 10000. This is so \startdefun can avoid allowing breakpoints after + % section headings. Otherwise, it would insert a valid breakpoint between: + % + % @section sec-whatever + % @deffn def-whatever + \penalty 10001 +} + + +\message{toc,} +% Table of contents. +\newwrite\tocfile + +% Write an entry to the toc file, opening it if necessary. +% Called from @chapter, etc. +% +% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} +% We append the current node name (if any) and page number as additional +% arguments for the \{chap,sec,...}entry macros which will eventually +% read this. The node name is used in the pdf outlines as the +% destination to jump to. +% +% We open the .toc file for writing here instead of at @setfilename (or +% any other fixed time) so that @contents can be anywhere in the document. +% But if #1 is `omit', then we don't do anything. This is used for the +% table of contents chapter openings themselves. +% +\newif\iftocfileopened +\def\omitkeyword{omit}% +% +\def\writetocentry#1#2#3{% + \edef\writetoctype{#1}% + \ifx\writetoctype\omitkeyword \else + \iftocfileopened\else + \immediate\openout\tocfile = \jobname.toc + \global\tocfileopenedtrue + \fi + % + \iflinks + {\atdummies + \edef\temp{% + \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% + \temp + }% + \fi + \fi + % + % Tell \shipout to create a pdf destination on each page, if we're + % writing pdf. These are used in the table of contents. We can't + % just write one on every page because the title pages are numbered + % 1 and 2 (the page numbers aren't printed), and so are the first + % two pages of the document. Thus, we'd have two destinations named + % `1', and two named `2'. + \ifpdf \global\pdfmakepagedesttrue \fi +} + + +% These characters do not print properly in the Computer Modern roman +% fonts, so we must take special care. This is more or less redundant +% with the Texinfo input format setup at the end of this file. +% +\def\activecatcodes{% + \catcode`\"=\active + \catcode`\$=\active + \catcode`\<=\active + \catcode`\>=\active + \catcode`\\=\active + \catcode`\^=\active + \catcode`\_=\active + \catcode`\|=\active + \catcode`\~=\active +} + + +% Read the toc file, which is essentially Texinfo input. +\def\readtocfile{% + \setupdatafile + \activecatcodes + \input \tocreadfilename +} + +\newskip\contentsrightmargin \contentsrightmargin=1in +\newcount\savepageno +\newcount\lastnegativepageno \lastnegativepageno = -1 + +% Prepare to read what we've written to \tocfile. +% +\def\startcontents#1{% + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. Thus, we maintain + % \contentsalignmacro in parallel with \pagealignmacro. + % From: Torbjorn Granlund <tege@matematik.su.se> + \contentsalignmacro + \immediate\closeout\tocfile + % + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \chapmacro{#1}{Yomitfromtoc}{}% + % + \savepageno = \pageno + \begingroup % Set up to handle contents files properly. + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. + % + % Roman numerals for page numbers. + \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi +} + +% redefined for the two-volume lispref. We always output on +% \jobname.toc even if this is redefined. +% +\def\tocreadfilename{\jobname.toc} + +% Normal (long) toc. +% +\def\contents{% + \startcontents{\putwordTOC}% + \openin 1 \tocreadfilename\space + \ifeof 1 \else + \readtocfile + \fi + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \ifeof 1 \else + \pdfmakeoutlines + \fi + \closein 1 + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} + +% And just the chapters. +\def\summarycontents{% + \startcontents{\putwordShortTOC}% + % + \let\numchapentry = \shortchapentry + \let\appentry = \shortchapentry + \let\unnchapentry = \shortunnchapentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf + \let\sl=\shortcontsl \let\tt=\shortconttt + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\numsecentry##1##2##3##4{} + \let\appsecentry = \numsecentry + \let\unnsecentry = \numsecentry + \let\numsubsecentry = \numsecentry + \let\appsubsecentry = \numsecentry + \let\unnsubsecentry = \numsecentry + \let\numsubsubsecentry = \numsecentry + \let\appsubsubsecentry = \numsecentry + \let\unnsubsubsecentry = \numsecentry + \openin 1 \tocreadfilename\space + \ifeof 1 \else + \readtocfile + \fi + \closein 1 + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} +\let\shortcontents = \summarycontents + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g., `A' for an appendix, or `3' for a chapter. +% +\def\shortchaplabel#1{% + % This space should be enough, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % But use \hss just in case. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + % + % We'd like to right-justify chapter numbers, but that looks strange + % with appendix letters. And right-justifying numbers and + % left-justifying letters looks strange when there is less than 10 + % chapters. Have to read the whole toc once to know how many chapters + % there are before deciding ... + \hbox to 1em{#1\hss}% +} + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Chapters, in the main contents. +\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} +% +% Chapters, in the short toc. +% See comments in \dochapentry re vbox and related settings. +\def\shortchapentry#1#2#3#4{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% +} + +% Appendices, in the main contents. +% Need the word Appendix, and a fixed-size box. +% +\def\appendixbox#1{% + % We use M since it's probably the widest letter. + \setbox0 = \hbox{\putwordAppendix{} M}% + \hbox to \wd0{\putwordAppendix{} #1\hss}} +% +\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}} + +% Unnumbered chapters. +\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} +\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} + +% Sections. +\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} +\let\appsecentry=\numsecentry +\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} + +% Subsections. +\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} +\let\appsubsecentry=\numsubsecentry +\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} + +% And subsubsections. +\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} +\let\appsubsubsecentry=\numsubsubsecentry +\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} + +% This parameter controls the indentation of the various levels. +% Same as \defaultparindent. +\newdimen\tocindent \tocindent = 15pt + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we want it to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip + \begingroup + \chapentryfonts + \tocentry{#1}{\dopageno\bgroup#2\egroup}% + \endgroup + \nobreak\vskip .25\baselineskip plus.1\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +% We use the same \entry macro as for the index entries. +\let\tocentry = \entry + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\def\subsecentryfonts{\textfonts} +\def\subsubsecentryfonts{\textfonts} + + +\message{environments,} +% @foo ... @end foo. + +% @tex ... @end tex escapes into raw Tex temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain tex @ character. + +\envdef\tex{% + \setupmarkupstyle{tex}% + \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 + \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 + \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie + \catcode `\%=14 + \catcode `\+=\other + \catcode `\"=\other + \catcode `\|=\other + \catcode `\<=\other + \catcode `\>=\other + \catcode`\`=\other + \catcode`\'=\other + \escapechar=`\\ + % + \let\b=\ptexb + \let\bullet=\ptexbullet + \let\c=\ptexc + \let\,=\ptexcomma + \let\.=\ptexdot + \let\dots=\ptexdots + \let\equiv=\ptexequiv + \let\!=\ptexexclam + \let\i=\ptexi + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \let\{=\ptexlbrace + \let\+=\tabalign + \let\}=\ptexrbrace + \let\/=\ptexslash + \let\*=\ptexstar + \let\t=\ptext + \expandafter \let\csname top\endcsname=\ptextop % outer + \let\frenchspacing=\plainfrenchspacing + % + \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% + \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% + \def\@{@}% +} +% There is no need to define \Etex. + +% Define @lisp ... @end lisp. +% @lisp environment forms a group so it can rebind things, +% including the definition of @end lisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip. +% +\def\aboveenvbreak{{% + % =10000 instead of <10000 because of a special case in \itemzzz and + % \sectionheading, q.v. + \ifnum \lastpenalty=10000 \else + \advance\envskipamount by \parskip + \endgraf + \ifdim\lastskip<\envskipamount + \removelastskip + % it's not a good place to break if the last penalty was \nobreak + % or better ... + \ifnum\lastpenalty<10000 \penalty-50 \fi + \vskip\envskipamount + \fi + \fi +}} + +\let\afterenvbreak = \aboveenvbreak + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will +% also clear it, so that its embedded environments do the narrowing again. +\let\nonarrowing=\relax + +% @cartouche ... @end cartouche: draw rectangle w/rounded corners around +% environment contents. +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\envdef\cartouche{% + \ifhmode\par\fi % can't be in the midst of a paragraph. + \startsavinginserts + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt % we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18.4pt % allow for 3pt kerns on either + % side, and for 6pt waste from + % each corner char, and rule thickness + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing = t% + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \kern3pt + \hsize=\cartinner + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip + \comment % For explanation, see the end of \def\group. +} +\def\Ecartouche{% + \ifhmode\par\fi + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup + \checkinserts +} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\newdimen\nonfillparindent +\def\nonfillstart{% + \aboveenvbreak + \hfuzz = 12pt % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + % Turn off paragraph indentation but redefine \indent to emulate + % the normal \indent. + \nonfillparindent=\parindent + \parindent = 0pt + \let\indent\nonfillindent + % + \emergencystretch = 0pt % don't try to avoid overfull boxes + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \let\exdent=\nofillexdent +} + +\begingroup +\obeyspaces +% We want to swallow spaces (but not other tokens) after the fake +% @indent in our nonfill-environments, where spaces are normally +% active and set to @tie, resulting in them not being ignored after +% @indent. +\gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}% +\gdef\nonfillindentcheck{% +\ifx\temp % +\expandafter\nonfillindentgobble% +\else% +\leavevmode\nonfillindentbox% +\fi% +}% +\endgroup +\def\nonfillindentgobble#1{\nonfillindent} +\def\nonfillindentbox{\hbox to \nonfillparindent{\hss}} + +% If you want all examples etc. small: @set dispenvsize small. +% If you want even small examples the full size: @set dispenvsize nosmall. +% This affects the following displayed environments: +% @example, @display, @format, @lisp +% +\def\smallword{small} +\def\nosmallword{nosmall} +\let\SETdispenvsize\relax +\def\setnormaldispenv{% + \ifx\SETdispenvsize\smallword + % end paragraph for sake of leading, in case document has no blank + % line. This is redundant with what happens in \aboveenvbreak, but + % we need to do it before changing the fonts, and it's inconvenient + % to change the fonts afterward. + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} +\def\setsmalldispenv{% + \ifx\SETdispenvsize\nosmallword + \else + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} + +% We often define two environments, @foo and @smallfoo. +% Let's do it by one command: +\def\makedispenv #1#2{ + \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2} + \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2} + \expandafter\let\csname E#1\endcsname \afterenvbreak + \expandafter\let\csname Esmall#1\endcsname \afterenvbreak +} + +% Define two synonyms: +\def\maketwodispenvs #1#2#3{ + \makedispenv{#1}{#3} + \makedispenv{#2}{#3} +} + +% @lisp: indented, narrowed, typewriter font; @example: same as @lisp. +% +% @smallexample and @smalllisp: use smaller fonts. +% Originally contributed by Pavel@xerox. +% +\maketwodispenvs {lisp}{example}{% + \nonfillstart + \tt\setupmarkupstyle{example}% + \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. + \gobble % eat return +} +% @display/@smalldisplay: same as @lisp except keep current font. +% +\makedispenv {display}{% + \nonfillstart + \gobble +} + +% @format/@smallformat: same as @display except don't narrow margins. +% +\makedispenv{format}{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} + +% @flushleft: same as @format, but doesn't obey \SETdispenvsize. +\envdef\flushleft{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} +\let\Eflushleft = \afterenvbreak + +% @flushright. +% +\envdef\flushright{% + \let\nonarrowing = t% + \nonfillstart + \advance\leftskip by 0pt plus 1fill + \gobble +} +\let\Eflushright = \afterenvbreak + + +% @raggedright does more-or-less normal line breaking but no right +% justification. From plain.tex. +\envdef\raggedright{% + \rightskip0pt plus2em \spaceskip.3333em \xspaceskip.5em\relax +} +\let\Eraggedright\par + +\envdef\raggedleft{% + \parindent=0pt \leftskip0pt plus2em + \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt + \hbadness=10000 % Last line will usually be underfull, so turn off + % badness reporting. +} +\let\Eraggedleft\par + +\envdef\raggedcenter{% + \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em + \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt + \hbadness=10000 % Last line will usually be underfull, so turn off + % badness reporting. +} +\let\Eraggedcenter\par + + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. We keep \parskip nonzero in general, since +% we're doing normal filling. So, when using \aboveenvbreak and +% \afterenvbreak, temporarily make \parskip 0. +% +\def\quotationstart{% + {\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 + \advance\rightskip by \lispnarrowing + \exdentamount = \lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \parsearg\quotationlabel +} + +\envdef\quotation{% + \setnormaldispenv + \quotationstart +} + +\envdef\smallquotation{% + \setsmalldispenv + \quotationstart +} +\let\Esmallquotation = \Equotation + +% We have retained a nonzero parskip for the environment, since we're +% doing normal filling. +% +\def\Equotation{% + \par + \ifx\quotationauthor\undefined\else + % indent a bit. + \leftline{\kern 2\leftskip \sl ---\quotationauthor}% + \fi + {\parskip=0pt \afterenvbreak}% +} + +% If we're given an argument, typeset it in bold with a colon after. +\def\quotationlabel#1{% + \def\temp{#1}% + \ifx\temp\empty \else + {\bf #1: }% + \fi +} + + +% LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>} +% If we want to allow any <char> as delimiter, +% we need the curly braces so that makeinfo sees the @verb command, eg: +% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org +% +% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. +% +% [Knuth] p.344; only we need to do the other characters Texinfo sets +% active too. Otherwise, they get lost as the first character on a +% verbatim line. +\def\dospecials{% + \do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% + \do\<\do\>\do\|\do\@\do+\do\"% + % Don't do the quotes -- if we do, @set txicodequoteundirected and + % @set txicodequotebacktick will not have effect on @verb and + % @verbatim, and ?` and !` ligatures won't get disabled. + %\do\`\do\'% +} +% +% [Knuth] p. 380 +\def\uncatcodespecials{% + \def\do##1{\catcode`##1=\other}\dospecials} +% +% Setup for the @verb command. +% +% Eight spaces for a tab +\begingroup + \catcode`\^^I=\active + \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} +\endgroup +% +\def\setupverb{% + \tt % easiest (and conventionally used) font for verbatim + \def\par{\leavevmode\endgraf}% + \setupmarkupstyle{verb}% + \tabeightspaces + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces +} + +% Setup for the @verbatim environment +% +% Real tab expansion +\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount +% +\def\starttabbox{\setbox0=\hbox\bgroup} +% +\begingroup + \catcode`\^^I=\active + \gdef\tabexpand{% + \catcode`\^^I=\active + \def^^I{\leavevmode\egroup + \dimen0=\wd0 % the width so far, or since the previous tab + \divide\dimen0 by\tabw + \multiply\dimen0 by\tabw % compute previous multiple of \tabw + \advance\dimen0 by\tabw % advance to next multiple of \tabw + \wd0=\dimen0 \box0 \starttabbox + }% + } +\endgroup + +% start the verbatim environment. +\def\setupverbatim{% + \let\nonarrowing = t% + \nonfillstart + % Easiest (and conventionally used) font for verbatim + \tt + \def\par{\leavevmode\egroup\box0\endgraf}% + \tabexpand + \setupmarkupstyle{verbatim}% + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces + \everypar{\starttabbox}% +} + +% Do the @verb magic: verbatim text is quoted by unique +% delimiter characters. Before first delimiter expect a +% right brace, after last delimiter expect closing brace: +% +% \def\doverb'{'<char>#1<char>'}'{#1} +% +% [Knuth] p. 382; only eat outer {} +\begingroup + \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other + \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] +\endgroup +% +\def\verb{\begingroup\setupverb\doverb} +% +% +% Do the @verbatim magic: define the macro \doverbatim so that +% the (first) argument ends when '@end verbatim' is reached, ie: +% +% \def\doverbatim#1@end verbatim{#1} +% +% For Texinfo it's a lot easier than for LaTeX, +% because texinfo's \verbatim doesn't stop at '\end{verbatim}': +% we need not redefine '\', '{' and '}'. +% +% Inspired by LaTeX's verbatim command set [latex.ltx] +% +\begingroup + \catcode`\ =\active + \obeylines % + % ignore everything up to the first ^^M, that's the newline at the end + % of the @verbatim input line itself. Otherwise we get an extra blank + % line in the output. + \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% + % We really want {...\end verbatim} in the body of the macro, but + % without the active space; thus we have to use \xdef and \gobble. +\endgroup +% +\envdef\verbatim{% + \setupverbatim\doverbatim +} +\let\Everbatim = \afterenvbreak + + +% @verbatiminclude FILE - insert text of file in verbatim environment. +% +\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} +% +\def\doverbatiminclude#1{% + {% + \makevalueexpandable + \setupverbatim + \indexnofonts % Allow `@@' and other weird things in file names. + \input #1 + \afterenvbreak + }% +} + +% @copying ... @end copying. +% Save the text away for @insertcopying later. +% +% We save the uninterpreted tokens, rather than creating a box. +% Saving the text in a box would be much easier, but then all the +% typesetting commands (@smallbook, font changes, etc.) have to be done +% beforehand -- and a) we want @copying to be done first in the source +% file; b) letting users define the frontmatter in as flexible order as +% possible is very desirable. +% +\def\copying{\checkenv{}\begingroup\scanargctxt\docopying} +\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} +% +\def\insertcopying{% + \begingroup + \parindent = 0pt % paragraph indentation looks wrong on title page + \scanexp\copyingtext + \endgroup +} + + +\message{defuns,} +% @defun etc. + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deflastargmargin \deflastargmargin=18pt +\newcount\defunpenalty + +% Start the processing of @deffn: +\def\startdefun{% + \ifnum\lastpenalty<10000 + \medbreak + \defunpenalty=10003 % Will keep this @deffn together with the + % following @def command, see below. + \else + % If there are two @def commands in a row, we'll have a \nobreak, + % which is there to keep the function description together with its + % header. But if there's nothing but headers, we need to allow a + % break somewhere. Check specifically for penalty 10002, inserted + % by \printdefunline, instead of 10000, since the sectioning + % commands also insert a nobreak penalty, and we don't want to allow + % a break between a section heading and a defun. + % + % As a minor refinement, we avoid "club" headers by signalling + % with penalty of 10003 after the very first @deffn in the + % sequence (see above), and penalty of 10002 after any following + % @def command. + \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi + % + % Similarly, after a section heading, do not allow a break. + % But do insert the glue. + \medskip % preceded by discardable penalty, so not a breakpoint + \fi + % + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent +} + +\def\dodefunx#1{% + % First, check whether we are in the right environment: + \checkenv#1% + % + % As above, allow line break if we have multiple x headers in a row. + % It's not a great place, though. + \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi + % + % And now, it's time to reuse the body of the original defun: + \expandafter\gobbledefun#1% +} +\def\gobbledefun#1\startdefun{} + +% \printdefunline \deffnheader{text} +% +\def\printdefunline#1#2{% + \begingroup + % call \deffnheader: + #1#2 \endheader + % common ending: + \interlinepenalty = 10000 + \advance\rightskip by 0pt plus 1fil + \endgraf + \nobreak\vskip -\parskip + \penalty\defunpenalty % signal to \startdefun and \dodefunx + % Some of the @defun-type tags do not enable magic parentheses, + % rendering the following check redundant. But we don't optimize. + \checkparencounts + \endgroup +} + +\def\Edefun{\endgraf\medbreak} + +% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; +% the only thing remaining is to define \deffnheader. +% +\def\makedefun#1{% + \expandafter\let\csname E#1\endcsname = \Edefun + \edef\temp{\noexpand\domakedefun + \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% + \temp +} + +% \domakedefun \deffn \deffnx \deffnheader +% +% Define \deffn and \deffnx, without parameters. +% \deffnheader has to be defined explicitly. +% +\def\domakedefun#1#2#3{% + \envdef#1{% + \startdefun + \parseargusing\activeparens{\printdefunline#3}% + }% + \def#2{\dodefunx#1}% + \def#3% +} + +%%% Untyped functions: + +% @deffn category name args +\makedefun{deffn}{\deffngeneral{}} + +% @deffn category class name args +\makedefun{defop}#1 {\defopon{#1\ \putwordon}} + +% \defopon {category on}class name args +\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deffngeneral {subind}category name args +% +\def\deffngeneral#1#2 #3 #4\endheader{% + % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}. + \dosubind{fn}{\code{#3}}{#1}% + \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% +} + +%%% Typed functions: + +% @deftypefn category type name args +\makedefun{deftypefn}{\deftypefngeneral{}} + +% @deftypeop category class type name args +\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} + +% \deftypeopon {category on}class type name args +\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deftypefngeneral {subind}category type name args +% +\def\deftypefngeneral#1#2 #3 #4 #5\endheader{% + \dosubind{fn}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +%%% Typed variables: + +% @deftypevr category type var args +\makedefun{deftypevr}{\deftypecvgeneral{}} + +% @deftypecv category class type var args +\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} + +% \deftypecvof {category of}class type var args +\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } + +% \deftypecvgeneral {subind}category type var args +% +\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% + \dosubind{vr}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +%%% Untyped variables: + +% @defvr category var args +\makedefun{defvr}#1 {\deftypevrheader{#1} {} } + +% @defcv category class var args +\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} + +% \defcvof {category of}class var args +\def\defcvof#1#2 {\deftypecvof{#1}#2 {} } + +%%% Type: +% @deftp category name args +\makedefun{deftp}#1 #2 #3\endheader{% + \doind{tp}{\code{#2}}% + \defname{#1}{}{#2}\defunargs{#3\unskip}% +} + +% Remaining @defun-like shortcuts: +\makedefun{defun}{\deffnheader{\putwordDeffunc} } +\makedefun{defmac}{\deffnheader{\putwordDefmac} } +\makedefun{defspec}{\deffnheader{\putwordDefspec} } +\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } +\makedefun{defvar}{\defvrheader{\putwordDefvar} } +\makedefun{defopt}{\defvrheader{\putwordDefopt} } +\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } +\makedefun{defmethod}{\defopon\putwordMethodon} +\makedefun{deftypemethod}{\deftypeopon\putwordMethodon} +\makedefun{defivar}{\defcvof\putwordInstanceVariableof} +\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} + +% \defname, which formats the name of the @def (not the args). +% #1 is the category, such as "Function". +% #2 is the return type, if any. +% #3 is the function name. +% +% We are followed by (but not passed) the arguments, if any. +% +\def\defname#1#2#3{% + % Get the values of \leftskip and \rightskip as they were outside the @def... + \advance\leftskip by -\defbodyindent + % + % How we'll format the type name. Putting it in brackets helps + % distinguish it from the body text that may end up on the next line + % just below it. + \def\temp{#1}% + \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} + % + % Figure out line sizes for the paragraph shape. + % The first line needs space for \box0; but if \rightskip is nonzero, + % we need only space for the part of \box0 which exceeds it: + \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip + % The continuations: + \dimen2=\hsize \advance\dimen2 by -\defargsindent + % (plain.tex says that \dimen1 should be used only as global.) + \parshape 2 0in \dimen0 \defargsindent \dimen2 + % + % Put the type name to the right margin. + \noindent + \hbox to 0pt{% + \hfil\box0 \kern-\hsize + % \hsize has to be shortened this way: + \kern\leftskip + % Intentionally do not respect \rightskip, since we need the space. + }% + % + % Allow all lines to be underfull without complaint: + \tolerance=10000 \hbadness=10000 + \exdentamount=\defbodyindent + {% + % defun fonts. We use typewriter by default (used to be bold) because: + % . we're printing identifiers, they should be in tt in principle. + % . in languages with many accents, such as Czech or French, it's + % common to leave accents off identifiers. The result looks ok in + % tt, but exceedingly strange in rm. + % . we don't want -- and --- to be treated as ligatures. + % . this still does not fix the ?` and !` ligatures, but so far no + % one has made identifiers using them :). + \df \tt + \def\temp{#2}% return value type + \ifx\temp\empty\else \tclose{\temp} \fi + #3% output function name + }% + {\rm\enskip}% hskip 0.5 em of \tenrm + % + \boldbrax + % arguments will be output next, if any. +} + +% Print arguments in slanted roman (not ttsl), inconsistently with using +% tt for the name. This is because literal text is sometimes needed in +% the argument list (groff manual), and ttsl and tt are not very +% distinguishable. Prevent hyphenation at `-' chars. +% +\def\defunargs#1{% + % use sl by default (not ttsl), + % tt for the names. + \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. + \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}% + #1% + \sl\hyphenchar\font=45 +} + +% We want ()&[] to print specially on the defun line. +% +\def\activeparens{% + \catcode`\(=\active \catcode`\)=\active + \catcode`\[=\active \catcode`\]=\active + \catcode`\&=\active +} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +{ + \activeparens + \global\let(=\lparen \global\let)=\rparen + \global\let[=\lbrack \global\let]=\rbrack + \global\let& = \& + + \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} + \gdef\magicamp{\let&=\amprm} +} + +\newcount\parencount + +% If we encounter &foo, then turn on ()-hacking afterwards +\newif\ifampseen +\def\amprm#1 {\ampseentrue{\bf\ }} + +\def\parenfont{% + \ifampseen + % At the first level, print parens in roman, + % otherwise use the default font. + \ifnum \parencount=1 \rm \fi + \else + % The \sf parens (in \boldbrax) actually are a little bolder than + % the contained text. This is especially needed for [ and ] . + \sf + \fi +} +\def\infirstlevel#1{% + \ifampseen + \ifnum\parencount=1 + #1% + \fi + \fi +} +\def\bfafterword#1 {#1 \bf} + +\def\opnr{% + \global\advance\parencount by 1 + {\parenfont(}% + \infirstlevel \bfafterword +} +\def\clnr{% + {\parenfont)}% + \infirstlevel \sl + \global\advance\parencount by -1 +} + +\newcount\brackcount +\def\lbrb{% + \global\advance\brackcount by 1 + {\bf[}% +} +\def\rbrb{% + {\bf]}% + \global\advance\brackcount by -1 +} + +\def\checkparencounts{% + \ifnum\parencount=0 \else \badparencount \fi + \ifnum\brackcount=0 \else \badbrackcount \fi +} +% these should not use \errmessage; the glibc manual, at least, actually +% has such constructs (when documenting function pointers). +\def\badparencount{% + \message{Warning: unbalanced parentheses in @def...}% + \global\parencount=0 +} +\def\badbrackcount{% + \message{Warning: unbalanced square brackets in @def...}% + \global\brackcount=0 +} + + +\message{macros,} +% @macro. + +% To do this right we need a feature of e-TeX, \scantokens, +% which we arrange to emulate with a temporary file in ordinary TeX. +\ifx\eTeXversion\undefined + \newwrite\macscribble + \def\scantokens#1{% + \toks0={#1}% + \immediate\openout\macscribble=\jobname.tmp + \immediate\write\macscribble{\the\toks0}% + \immediate\closeout\macscribble + \input \jobname.tmp + } +\fi + +\def\scanmacro#1{% + \begingroup + \newlinechar`\^^M + \let\xeatspaces\eatspaces + % Undo catcode changes of \startcontents and \doprintindex + % When called from @insertcopying or (short)caption, we need active + % backslash to get it printed correctly. Previously, we had + % \catcode`\\=\other instead. We'll see whether a problem appears + % with macro expansion. --kasal, 19aug04 + \catcode`\@=0 \catcode`\\=\active \escapechar=`\@ + % ... and \example + \spaceisspace + % + % Append \endinput to make sure that TeX does not see the ending newline. + % I've verified that it is necessary both for e-TeX and for ordinary TeX + % --kasal, 29nov03 + \scantokens{#1\endinput}% + \endgroup +} + +\def\scanexp#1{% + \edef\temp{\noexpand\scanmacro{#1}}% + \temp +} + +\newcount\paramno % Count of parameters +\newtoks\macname % Macro name +\newif\ifrecursive % Is it recursive? + +% List of all defined macros in the form +% \definedummyword\macro1\definedummyword\macro2... +% Currently is also contains all @aliases; the list can be split +% if there is a need. +\def\macrolist{} + +% Add the macro to \macrolist +\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} +\def\addtomacrolistxxx#1{% + \toks0 = \expandafter{\macrolist\definedummyword#1}% + \xdef\macrolist{\the\toks0}% +} + +% Utility routines. +% This does \let #1 = #2, with \csnames; that is, +% \let \csname#1\endcsname = \csname#2\endcsname +% (except of course we have to play expansion games). +% +\def\cslet#1#2{% + \expandafter\let + \csname#1\expandafter\endcsname + \csname#2\endcsname +} + +% Trim leading and trailing spaces off a string. +% Concepts from aro-bend problem 15 (see CTAN). +{\catcode`\@=11 +\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} +\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} +\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} +\def\unbrace#1{#1} +\unbrace{\gdef\trim@@@ #1 } #2@{#1} +} + +% Trim a single trailing ^^M off a string. +{\catcode`\^^M=\other \catcode`\Q=3% +\gdef\eatcr #1{\eatcra #1Q^^MQ}% +\gdef\eatcra#1^^MQ{\eatcrb#1Q}% +\gdef\eatcrb#1Q#2Q{#1}% +} + +% Macro bodies are absorbed as an argument in a context where +% all characters are catcode 10, 11 or 12, except \ which is active +% (as in normal texinfo). It is necessary to change the definition of \. + +% Non-ASCII encodings make 8-bit characters active, so un-activate +% them to avoid their expansion. Must do this non-globally, to +% confine the change to the current group. + +% It's necessary to have hard CRs when the macro is executed. This is +% done by making ^^M (\endlinechar) catcode 12 when reading the macro +% body, and then making it the \newlinechar in \scanmacro. + +\def\scanctxt{% + \catcode`\"=\other + \catcode`\+=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\@=\other + \catcode`\^=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\~=\other + \ifx\declaredencoding\ascii \else \setnonasciicharscatcodenonglobal\other \fi +} + +\def\scanargctxt{% + \scanctxt + \catcode`\\=\other + \catcode`\^^M=\other +} + +\def\macrobodyctxt{% + \scanctxt + \catcode`\{=\other + \catcode`\}=\other + \catcode`\^^M=\other + \usembodybackslash +} + +\def\macroargctxt{% + \scanctxt + \catcode`\\=\other +} + +% \mbodybackslash is the definition of \ in @macro bodies. +% It maps \foo\ => \csname macarg.foo\endcsname => #N +% where N is the macro parameter number. +% We define \csname macarg.\endcsname to be \realbackslash, so +% \\ in macro replacement text gets you a backslash. + +{\catcode`@=0 @catcode`@\=@active + @gdef@usembodybackslash{@let\=@mbodybackslash} + @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} +} +\expandafter\def\csname macarg.\endcsname{\realbackslash} + +\def\macro{\recursivefalse\parsearg\macroxxx} +\def\rmacro{\recursivetrue\parsearg\macroxxx} + +\def\macroxxx#1{% + \getargs{#1}% now \macname is the macname and \argl the arglist + \ifx\argl\empty % no arguments + \paramno=0% + \else + \expandafter\parsemargdef \argl;% + \fi + \if1\csname ismacro.\the\macname\endcsname + \message{Warning: redefining \the\macname}% + \else + \expandafter\ifx\csname \the\macname\endcsname \relax + \else \errmessage{Macro name \the\macname\space already defined}\fi + \global\cslet{macsave.\the\macname}{\the\macname}% + \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% + \addtomacrolist{\the\macname}% + \fi + \begingroup \macrobodyctxt + \ifrecursive \expandafter\parsermacbody + \else \expandafter\parsemacbody + \fi} + +\parseargdef\unmacro{% + \if1\csname ismacro.#1\endcsname + \global\cslet{#1}{macsave.#1}% + \global\expandafter\let \csname ismacro.#1\endcsname=0% + % Remove the macro name from \macrolist: + \begingroup + \expandafter\let\csname#1\endcsname \relax + \let\definedummyword\unmacrodo + \xdef\macrolist{\macrolist}% + \endgroup + \else + \errmessage{Macro #1 not defined}% + \fi +} + +% Called by \do from \dounmacro on each macro. The idea is to omit any +% macro definitions that have been changed to \relax. +% +\def\unmacrodo#1{% + \ifx #1\relax + % remove this + \else + \noexpand\definedummyword \noexpand#1% + \fi +} + +% This makes use of the obscure feature that if the last token of a +% <parameter list> is #, then the preceding argument is delimited by +% an opening brace, and that opening brace is not consumed. +\def\getargs#1{\getargsxxx#1{}} +\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} +\def\getmacname #1 #2\relax{\macname={#1}} +\def\getmacargs#1{\def\argl{#1}} + +% Parse the optional {params} list. Set up \paramno and \paramlist +% so \defmacro knows what to do. Define \macarg.blah for each blah +% in the params list, to be ##N where N is the position in that list. +% That gets used by \mbodybackslash (above). + +% We need to get `macro parameter char #' into several definitions. +% The technique used is stolen from LaTeX: let \hash be something +% unexpandable, insert that wherever you need a #, and then redefine +% it to # just before using the token list produced. +% +% The same technique is used to protect \eatspaces till just before +% the macro is used. + +\def\parsemargdef#1;{\paramno=0\def\paramlist{}% + \let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,} +\def\parsemargdefxxx#1,{% + \if#1;\let\next=\relax + \else \let\next=\parsemargdefxxx + \advance\paramno by 1% + \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname + {\xeatspaces{\hash\the\paramno}}% + \edef\paramlist{\paramlist\hash\the\paramno,}% + \fi\next} + +% These two commands read recursive and nonrecursive macro bodies. +% (They're different since rec and nonrec macros end differently.) + +\long\def\parsemacbody#1@end macro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% +\long\def\parsermacbody#1@end rmacro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% + +% This defines the macro itself. There are six cases: recursive and +% nonrecursive macros of zero, one, and many arguments. +% Much magic with \expandafter here. +% \xdef is used so that macro definitions will survive the file +% they're defined in; @include reads the file inside a group. +\def\defmacro{% + \let\hash=##% convert placeholders to macro parameter chars + \ifrecursive + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\scanmacro{\temp}}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup\noexpand\scanmacro{\temp}}% + \else % many + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{\egroup\noexpand\scanmacro{\temp}}% + \fi + \else + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \else % many + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \expandafter\noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \fi + \fi} + +\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} + +% \braceorline decides whether the next nonwhitespace character is a +% {. If so it reads up to the closing }, if not, it reads the whole +% line. Whatever was read is then fed to the next control sequence +% as an argument (by \parsebrace or \parsearg) +\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} +\def\braceorlinexxx{% + \ifx\nchar\bgroup\else + \expandafter\parsearg + \fi \macnamexxx} + + +% @alias. +% We need some trickery to remove the optional spaces around the equal +% sign. Just make them active and then expand them all to nothing. +\def\alias{\parseargusing\obeyspaces\aliasxxx} +\def\aliasxxx #1{\aliasyyy#1\relax} +\def\aliasyyy #1=#2\relax{% + {% + \expandafter\let\obeyedspace=\empty + \addtomacrolist{#1}% + \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% + }% + \next +} + + +\message{cross references,} + +\newwrite\auxfile +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% @inforef is relatively simple. +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +% @node's only job in TeX is to define \lastnode, which is used in +% cross-references. The @node line might or might not have commas, and +% might or might not have spaces before the first comma, like: +% @node foo , bar , ... +% We don't want such trailing spaces in the node name. +% +\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} +% +% also remove a trailing comma, in case of something like this: +% @node Help-Cross, , , Cross-refs +\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} +\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}} + +\let\nwnode=\node +\let\lastnode=\empty + +% Write a cross-reference definition for the current node. #1 is the +% type (Ynumbered, Yappendix, Ynothing). +% +\def\donoderef#1{% + \ifx\lastnode\empty\else + \setref{\lastnode}{#1}% + \global\let\lastnode=\empty + \fi +} + +% @anchor{NAME} -- define xref target at arbitrary point. +% +\newcount\savesfregister +% +\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} +\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} +\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} + +% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an +% anchor), which consists of three parts: +% 1) NAME-title - the current sectioning name taken from \lastsection, +% or the anchor name. +% 2) NAME-snt - section number and type, passed as the SNT arg, or +% empty for anchors. +% 3) NAME-pg - the page number. +% +% This is called from \donoderef, \anchor, and \dofloat. In the case of +% floats, there is an additional part, which is not written here: +% 4) NAME-lof - the text as it should appear in a @listoffloats. +% +\def\setref#1#2{% + \pdfmkdest{#1}% + \iflinks + {% + \atdummies % preserve commands, but don't expand them + \edef\writexrdef##1##2{% + \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef + ##1}{##2}}% these are parameters of \writexrdef + }% + \toks0 = \expandafter{\lastsection}% + \immediate \writexrdef{title}{\the\toks0 }% + \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. + \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, during \shipout + }% + \fi +} + +% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is +% the node name, #2 the name of the Info cross-reference, #3 the printed +% node name, #4 the name of the Info file, #5 the name of the printed +% manual. All but the node name can be omitted. +% +\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} +\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} +\def\ref#1{\xrefX[#1,,,,,,,]} +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \unsepspaces + \def\printedmanual{\ignorespaces #5}% + \def\printedrefname{\ignorespaces #3}% + \setbox1=\hbox{\printedmanual\unskip}% + \setbox0=\hbox{\printedrefname\unskip}% + \ifdim \wd0 = 0pt + % No printed node name was explicitly given. + \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax + % Use the node name inside the square brackets. + \def\printedrefname{\ignorespaces #1}% + \else + % Use the actual chapter/section title appear inside + % the square brackets. Use the real section title if we have it. + \ifdim \wd1 > 0pt + % It is in another manual, so we don't have it. + \def\printedrefname{\ignorespaces #1}% + \else + \ifhavexrefs + % We know the real title if we have the xref values. + \def\printedrefname{\refx{#1-title}{}}% + \else + % Otherwise just copy the Info node name. + \def\printedrefname{\ignorespaces #1}% + \fi% + \fi + \fi + \fi + % + % Make link in pdf output. + \ifpdf + {\indexnofonts + \turnoffactive + % This expands tokens, so do it after making catcode changes, so _ + % etc. don't get their TeX definitions. + \getfilename{#4}% + % + % See comments at \activebackslashdouble. + {\activebackslashdouble \xdef\pdfxrefdest{#1}% + \backslashparens\pdfxrefdest}% + % + \leavevmode + \startlink attr{/Border [0 0 0]}% + \ifnum\filenamelength>0 + goto file{\the\filename.pdf} name{\pdfxrefdest}% + \else + goto name{\pdfmkpgn{\pdfxrefdest}}% + \fi + }% + \setcolor{\linkcolor}% + \fi + % + % Float references are printed completely differently: "Figure 1.2" + % instead of "[somenode], p.3". We distinguish them by the + % LABEL-title being set to a magic string. + {% + % Have to otherify everything special to allow the \csname to + % include an _ in the xref name, etc. + \indexnofonts + \turnoffactive + \expandafter\global\expandafter\let\expandafter\Xthisreftitle + \csname XR#1-title\endcsname + }% + \iffloat\Xthisreftitle + % If the user specified the print name (third arg) to the ref, + % print it instead of our usual "Figure 1.2". + \ifdim\wd0 = 0pt + \refx{#1-snt}{}% + \else + \printedrefname + \fi + % + % if the user also gave the printed manual name (fifth arg), append + % "in MANUALNAME". + \ifdim \wd1 > 0pt + \space \putwordin{} \cite{\printedmanual}% + \fi + \else + % node/anchor (non-float) references. + % + % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not + % insert empty discretionaries after hyphens, which means that it will + % not find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, this + % is a loss. Therefore, we give the text of the node name again, so it + % is as if TeX is seeing it for the first time. + \ifdim \wd1 > 0pt + \putwordSection{} ``\printedrefname'' \putwordin{} \cite{\printedmanual}% + \else + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\turnoffactive + % Only output a following space if the -snt ref is nonempty; for + % @unnumbered and @anchor, it won't be. + \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% + \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi + }% + % output the `[mynode]' via a macro so it can be overridden. + \xrefprintnodename\printedrefname + % + % But we always want a comma and a space: + ,\space + % + % output the `page 3'. + \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + \fi + \fi + \endlink +\endgroup} + +% This macro is called from \xrefX for the `[nodename]' part of xref +% output. It's a separate macro only so it can be changed more easily, +% since square brackets don't work well in some documents. Particularly +% one that Bob is working on :). +% +\def\xrefprintnodename#1{[#1]} + +% Things referred to by \setref. +% +\def\Ynothing{} +\def\Yomitfromtoc{} +\def\Ynumbered{% + \ifnum\secno=0 + \putwordChapter@tie \the\chapno + \else \ifnum\subsecno=0 + \putwordSection@tie \the\chapno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno + \else + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} +\def\Yappendix{% + \ifnum\secno=0 + \putwordAppendix@tie @char\the\appendixno{}% + \else \ifnum\subsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno + \else + \putwordSection@tie + @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. +% +\def\refx#1#2{% + {% + \indexnofonts + \otherbackslash + \expandafter\global\expandafter\let\expandafter\thisrefX + \csname XR#1\endcsname + }% + \ifx\thisrefX\relax + % If not defined, say something at least. + \angleleft un\-de\-fined\angleright + \iflinks + \ifhavexrefs + \message{\linenumber Undefined cross reference `#1'.}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \fi + \else + % It's defined, so just use it. + \thisrefX + \fi + #2% Output the suffix in any case. +} + +% This is the macro invoked by entries in the aux file. Usually it's +% just a \def (we prepend XR to the control sequence name to avoid +% collisions). But if this is a float type, we have more work to do. +% +\def\xrdef#1#2{% + {% The node name might contain 8-bit characters, which in our current + % implementation are changed to commands like @'e. Don't let these + % mess up the control sequence name. + \indexnofonts + \turnoffactive + \xdef\safexrefname{#1}% + }% + % + \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref + % + % Was that xref control sequence that we just defined for a float? + \expandafter\iffloat\csname XR\safexrefname\endcsname + % it was a float, and we have the (safe) float type in \iffloattype. + \expandafter\let\expandafter\floatlist + \csname floatlist\iffloattype\endcsname + % + % Is this the first time we've seen this float type? + \expandafter\ifx\floatlist\relax + \toks0 = {\do}% yes, so just \do + \else + % had it before, so preserve previous elements in list. + \toks0 = \expandafter{\floatlist\do}% + \fi + % + % Remember this xref in the control sequence \floatlistFLOATTYPE, + % for later use in \listoffloats. + \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0 + {\safexrefname}}% + \fi +} + +% Read the last existing aux file, if any. No error if none exists. +% +\def\tryauxfile{% + \openin 1 \jobname.aux + \ifeof 1 \else + \readdatafile{aux}% + \global\havexrefstrue + \fi + \closein 1 +} + +\def\setupdatafile{% + \catcode`\^^@=\other + \catcode`\^^A=\other + \catcode`\^^B=\other + \catcode`\^^C=\other + \catcode`\^^D=\other + \catcode`\^^E=\other + \catcode`\^^F=\other + \catcode`\^^G=\other + \catcode`\^^H=\other + \catcode`\^^K=\other + \catcode`\^^L=\other + \catcode`\^^N=\other + \catcode`\^^P=\other + \catcode`\^^Q=\other + \catcode`\^^R=\other + \catcode`\^^S=\other + \catcode`\^^T=\other + \catcode`\^^U=\other + \catcode`\^^V=\other + \catcode`\^^W=\other + \catcode`\^^X=\other + \catcode`\^^Z=\other + \catcode`\^^[=\other + \catcode`\^^\=\other + \catcode`\^^]=\other + \catcode`\^^^=\other + \catcode`\^^_=\other + % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc. + % in xref tags, i.e., node names. But since ^^e4 notation isn't + % supported in the main text, it doesn't seem desirable. Furthermore, + % that is not enough: for node names that actually contain a ^ + % character, we would end up writing a line like this: 'xrdef {'hat + % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first + % argument, and \hat is not an expandable control sequence. It could + % all be worked out, but why? Either we support ^^ or we don't. + % + % The other change necessary for this was to define \auxhat: + % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter + % and then to call \auxhat in \setq. + % + \catcode`\^=\other + % + % Special characters. Should be turned off anyway, but... + \catcode`\~=\other + \catcode`\[=\other + \catcode`\]=\other + \catcode`\"=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\$=\other + \catcode`\#=\other + \catcode`\&=\other + \catcode`\%=\other + \catcode`+=\other % avoid \+ for paranoia even though we've turned it off + % + % This is to support \ in node names and titles, since the \ + % characters end up in a \csname. It's easier than + % leaving it active and making its active definition an actual \ + % character. What I don't understand is why it works in the *value* + % of the xrdef. Seems like it should be a catcode12 \, and that + % should not typeset properly. But it works, so I'm moving on for + % now. --karl, 15jan04. + \catcode`\\=\other + % + % Make the characters 128-255 be printing characters. + {% + \count1=128 + \def\loop{% + \catcode\count1=\other + \advance\count1 by 1 + \ifnum \count1<256 \loop \fi + }% + }% + % + % @ is our escape character in .aux files, and we need braces. + \catcode`\{=1 + \catcode`\}=2 + \catcode`\@=0 +} + +\def\readdatafile#1{% +\begingroup + \setupdatafile + \input\jobname.#1 +\endgroup} + + +\message{insertions,} +% including footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. (Generally, numeric constants should always be followed by a +% space to prevent strange expansion errors.) +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for info output only. +\let\footnotestyle=\comment + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \dofootnote +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +% Oh yes, they do; otherwise, @ifset (and anything else that uses +% \parseargline) fails inside footnotes because the tokens are fixed when +% the footnote is read. --karl, 16nov96. +% +\gdef\dofootnote{% + \insert\footins\bgroup + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \hsize=\pagewidth + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + \smallfonts \rm + % + % Because we use hanging indentation in footnotes, a @noindent appears + % to exdent this text, so make it be a no-op. makeinfo does not use + % hanging indentation so @noindent can still be needed within footnote + % text after an @example or the like (not that this is good style). + \let\noindent = \relax + % + % Hang the footnote text off the number. Use \everypar in case the + % footnote extends for more than one paragraph. + \everypar = {\hang}% + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + \futurelet\next\fo@t +} +}%end \catcode `\@=11 + +% In case a @footnote appears in a vbox, save the footnote text and create +% the real \insert just after the vbox finished. Otherwise, the insertion +% would be lost. +% Similarly, if a @footnote appears inside an alignment, save the footnote +% text to a box and make the \insert when a row of the table is finished. +% And the same can be done for other insert classes. --kasal, 16nov03. + +% Replace the \insert primitive by a cheating macro. +% Deeper inside, just make sure that the saved insertions are not spilled +% out prematurely. +% +\def\startsavinginserts{% + \ifx \insert\ptexinsert + \let\insert\saveinsert + \else + \let\checkinserts\relax + \fi +} + +% This \insert replacement works for both \insert\footins{foo} and +% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. +% +\def\saveinsert#1{% + \edef\next{\noexpand\savetobox \makeSAVEname#1}% + \afterassignment\next + % swallow the left brace + \let\temp = +} +\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} +\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} + +\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} + +\def\placesaveins#1{% + \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname + {\box#1}% +} + +% eat @SAVE -- beware, all of them have catcode \other: +{ + \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) + \gdef\gobblesave @SAVE{} +} + +% initialization: +\def\newsaveins #1{% + \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% + \next +} +\def\newsaveinsX #1{% + \csname newbox\endcsname #1% + \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts + \checksaveins #1}% +} + +% initialize: +\let\checkinserts\empty +\newsaveins\footins +\newsaveins\margin + + +% @image. We use the macros from epsf.tex to support this. +% If epsf.tex is not installed and @image is used, we complain. +% +% Check for and read epsf.tex up front. If we read it only at @image +% time, we might be inside a group, and then its definitions would get +% undone and the next image would fail. +\openin 1 = epsf.tex +\ifeof 1 \else + % Do not bother showing banner with epsf.tex v2.7k (available in + % doc/epsf.tex and on ctan). + \def\epsfannounce{\toks0 = }% + \input epsf.tex +\fi +\closein 1 +% +% We will only complain once about lack of epsf.tex. +\newif\ifwarnednoepsf +\newhelp\noepsfhelp{epsf.tex must be installed for images to + work. It is also included in the Texinfo distribution, or you can get + it from ftp://tug.org/tex/epsf.tex.} +% +\def\image#1{% + \ifx\epsfbox\undefined + \ifwarnednoepsf \else + \errhelp = \noepsfhelp + \errmessage{epsf.tex not found, images will be ignored}% + \global\warnednoepsftrue + \fi + \else + \imagexxx #1,,,,,\finish + \fi +} +% +% Arguments to @image: +% #1 is (mandatory) image filename; we tack on .eps extension. +% #2 is (optional) width, #3 is (optional) height. +% #4 is (ignored optional) html alt text. +% #5 is (ignored optional) extension. +% #6 is just the usual extra ignored arg for parsing this stuff. +\newif\ifimagevmode +\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup + \catcode`\^^M = 5 % in case we're inside an example + \normalturnoffactive % allow _ et al. in names + % If the image is by itself, center it. + \ifvmode + \imagevmodetrue + \nobreak\medskip + % Usually we'll have text after the image which will insert + % \parskip glue, so insert it here too to equalize the space + % above and below. + \nobreak\vskip\parskip + \nobreak + \fi + % + % Leave vertical mode so that indentation from an enclosing + % environment such as @quotation is respected. On the other hand, if + % it's at the top level, we don't want the normal paragraph indentation. + \noindent + % + % Output the image. + \ifpdf + \dopdfimage{#1}{#2}{#3}% + \else + % \epsfbox itself resets \epsf?size at each figure. + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi + \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi + \epsfbox{#1.eps}% + \fi + % + \ifimagevmode \medskip \fi % space after the standalone image +\endgroup} + + +% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, +% etc. We don't actually implement floating yet, we always include the +% float "here". But it seemed the best name for the future. +% +\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} + +% There may be a space before second and/or third parameter; delete it. +\def\eatcommaspace#1, {#1,} + +% #1 is the optional FLOATTYPE, the text label for this float, typically +% "Figure", "Table", "Example", etc. Can't contain commas. If omitted, +% this float will not be numbered and cannot be referred to. +% +% #2 is the optional xref label. Also must be present for the float to +% be referable. +% +% #3 is the optional positioning argument; for now, it is ignored. It +% will somehow specify the positions allowed to float to (here, top, bottom). +% +% We keep a separate counter for each FLOATTYPE, which we reset at each +% chapter-level command. +\let\resetallfloatnos=\empty +% +\def\dofloat#1,#2,#3,#4\finish{% + \let\thiscaption=\empty + \let\thisshortcaption=\empty + % + % don't lose footnotes inside @float. + % + % BEWARE: when the floats start float, we have to issue warning whenever an + % insert appears inside a float which could possibly float. --kasal, 26may04 + % + \startsavinginserts + % + % We can't be used inside a paragraph. + \par + % + \vtop\bgroup + \def\floattype{#1}% + \def\floatlabel{#2}% + \def\floatloc{#3}% we do nothing with this yet. + % + \ifx\floattype\empty + \let\safefloattype=\empty + \else + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + \fi + % + % If label is given but no type, we handle that as the empty type. + \ifx\floatlabel\empty \else + % We want each FLOATTYPE to be numbered separately (Figure 1, + % Table 1, Figure 2, ...). (And if no label, no number.) + % + \expandafter\getfloatno\csname\safefloattype floatno\endcsname + \global\advance\floatno by 1 + % + {% + % This magic value for \lastsection is output by \setref as the + % XREFLABEL-title value. \xrefX uses it to distinguish float + % labels (which have a completely different output format) from + % node and anchor labels. And \xrdef uses it to construct the + % lists of floats. + % + \edef\lastsection{\floatmagic=\safefloattype}% + \setref{\floatlabel}{Yfloat}% + }% + \fi + % + % start with \parskip glue, I guess. + \vskip\parskip + % + % Don't suppress indentation if a float happens to start a section. + \restorefirstparagraphindent +} + +% we have these possibilities: +% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap +% @float Foo,lbl & no caption: Foo 1.1 +% @float Foo & @caption{Cap}: Foo: Cap +% @float Foo & no caption: Foo +% @float ,lbl & Caption{Cap}: 1.1: Cap +% @float ,lbl & no caption: 1.1 +% @float & @caption{Cap}: Cap +% @float & no caption: +% +\def\Efloat{% + \let\floatident = \empty + % + % In all cases, if we have a float type, it comes first. + \ifx\floattype\empty \else \def\floatident{\floattype}\fi + % + % If we have an xref label, the number comes next. + \ifx\floatlabel\empty \else + \ifx\floattype\empty \else % if also had float type, need tie first. + \appendtomacro\floatident{\tie}% + \fi + % the number. + \appendtomacro\floatident{\chaplevelprefix\the\floatno}% + \fi + % + % Start the printed caption with what we've constructed in + % \floatident, but keep it separate; we need \floatident again. + \let\captionline = \floatident + % + \ifx\thiscaption\empty \else + \ifx\floatident\empty \else + \appendtomacro\captionline{: }% had ident, so need a colon between + \fi + % + % caption text. + \appendtomacro\captionline{\scanexp\thiscaption}% + \fi + % + % If we have anything to print, print it, with space before. + % Eventually this needs to become an \insert. + \ifx\captionline\empty \else + \vskip.5\parskip + \captionline + % + % Space below caption. + \vskip\parskip + \fi + % + % If have an xref label, write the list of floats info. Do this + % after the caption, to avoid chance of it being a breakpoint. + \ifx\floatlabel\empty \else + % Write the text that goes in the lof to the aux file as + % \floatlabel-lof. Besides \floatident, we include the short + % caption if specified, else the full caption if specified, else nothing. + {% + \atdummies + % + % since we read the caption text in the macro world, where ^^M + % is turned into a normal character, we have to scan it back, so + % we don't write the literal three characters "^^M" into the aux file. + \scanexp{% + \xdef\noexpand\gtemp{% + \ifx\thisshortcaption\empty + \thiscaption + \else + \thisshortcaption + \fi + }% + }% + \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident + \ifx\gtemp\empty \else : \gtemp \fi}}% + }% + \fi + \egroup % end of \vtop + % + % place the captured inserts + % + % BEWARE: when the floats start floating, we have to issue warning + % whenever an insert appears inside a float which could possibly + % float. --kasal, 26may04 + % + \checkinserts +} + +% Append the tokens #2 to the definition of macro #1, not expanding either. +% +\def\appendtomacro#1#2{% + \expandafter\def\expandafter#1\expandafter{#1#2}% +} + +% @caption, @shortcaption +% +\def\caption{\docaption\thiscaption} +\def\shortcaption{\docaption\thisshortcaption} +\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} +\def\defcaption#1#2{\egroup \def#1{#2}} + +% The parameter is the control sequence identifying the counter we are +% going to use. Create it if it doesn't exist and assign it to \floatno. +\def\getfloatno#1{% + \ifx#1\relax + % Haven't seen this figure type before. + \csname newcount\endcsname #1% + % + % Remember to reset this floatno at the next chap. + \expandafter\gdef\expandafter\resetallfloatnos + \expandafter{\resetallfloatnos #1=0 }% + \fi + \let\floatno#1% +} + +% \setref calls this to get the XREFLABEL-snt value. We want an @xref +% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we +% first read the @float command. +% +\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% + +% Magic string used for the XREFLABEL-title value, so \xrefX can +% distinguish floats from other xref types. +\def\floatmagic{!!float!!} + +% #1 is the control sequence we are passed; we expand into a conditional +% which is true if #1 represents a float ref. That is, the magic +% \lastsection value which we \setref above. +% +\def\iffloat#1{\expandafter\doiffloat#1==\finish} +% +% #1 is (maybe) the \floatmagic string. If so, #2 will be the +% (safe) float type for this float. We set \iffloattype to #2. +% +\def\doiffloat#1=#2=#3\finish{% + \def\temp{#1}% + \def\iffloattype{#2}% + \ifx\temp\floatmagic +} + +% @listoffloats FLOATTYPE - print a list of floats like a table of contents. +% +\parseargdef\listoffloats{% + \def\floattype{#1}% floattype + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + % + % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. + \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax + \ifhavexrefs + % if the user said @listoffloats foo but never @float foo. + \message{\linenumber No `\safefloattype' floats to list.}% + \fi + \else + \begingroup + \leftskip=\tocindent % indent these entries like a toc + \let\do=\listoffloatsdo + \csname floatlist\safefloattype\endcsname + \endgroup + \fi +} + +% This is called on each entry in a list of floats. We're passed the +% xref label, in the form LABEL-title, which is how we save it in the +% aux file. We strip off the -title and look up \XRLABEL-lof, which +% has the text we're supposed to typeset here. +% +% Figures without xref labels will not be included in the list (since +% they won't appear in the aux file). +% +\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} +\def\listoffloatsdoentry#1-title\finish{{% + % Can't fully expand XR#1-lof because it can contain anything. Just + % pass the control sequence. On the other hand, XR#1-pg is just the + % page number, and we want to fully expand that so we can get a link + % in pdf output. + \toksA = \expandafter{\csname XR#1-lof\endcsname}% + % + % use the same \entry macro we use to generate the TOC and index. + \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% + \writeentry +}} + + +\message{localization,} + +% For single-language documents, @documentlanguage is usually given very +% early, just after @documentencoding. Single argument is the language +% (de) or locale (de_DE) abbreviation. +% +{ + \catcode`\_ = \active + \globaldefs=1 +\parseargdef\documentlanguage{\begingroup + \let_=\normalunderscore % normal _ character for filenames + \tex % read txi-??.tex file in plain TeX. + % Read the file by the name they passed if it exists. + \openin 1 txi-#1.tex + \ifeof 1 + \documentlanguagetrywithoutunderscore{#1_\finish}% + \else + \globaldefs = 1 % everything in the txi-LL files needs to persist + \input txi-#1.tex + \fi + \closein 1 + \endgroup % end raw TeX +\endgroup} +% +% If they passed de_DE, and txi-de_DE.tex doesn't exist, +% try txi-de.tex. +% +\gdef\documentlanguagetrywithoutunderscore#1_#2\finish{% + \openin 1 txi-#1.tex + \ifeof 1 + \errhelp = \nolanghelp + \errmessage{Cannot read language file txi-#1.tex}% + \else + \globaldefs = 1 % everything in the txi-LL files needs to persist + \input txi-#1.tex + \fi + \closein 1 +} +}% end of special _ catcode +% +\newhelp\nolanghelp{The given language definition file cannot be found or +is empty. Maybe you need to install it? Putting it in the current +directory should work if nowhere else does.} + +% This macro is called from txi-??.tex files; the first argument is the +% \language name to set (without the "\lang@" prefix), the second and +% third args are \{left,right}hyphenmin. +% +% The language names to pass are determined when the format is built. +% See the etex.log file created at that time, e.g., +% /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log. +% +% With TeX Live 2008, etex now includes hyphenation patterns for all +% available languages. This means we can support hyphenation in +% Texinfo, at least to some extent. (This still doesn't solve the +% accented characters problem.) +% +\catcode`@=11 +\def\txisetlanguage#1#2#3{% + % do not set the language if the name is undefined in the current TeX. + \expandafter\ifx\csname lang@#1\endcsname \relax + \message{no patterns for #1}% + \else + \global\language = \csname lang@#1\endcsname + \fi + % but there is no harm in adjusting the hyphenmin values regardless. + \global\lefthyphenmin = #2\relax + \global\righthyphenmin = #3\relax +} + +% Helpers for encodings. +% Set the catcode of characters 128 through 255 to the specified number. +% +\def\setnonasciicharscatcode#1{% + \count255=128 + \loop\ifnum\count255<256 + \global\catcode\count255=#1\relax + \advance\count255 by 1 + \repeat +} + +\def\setnonasciicharscatcodenonglobal#1{% + \count255=128 + \loop\ifnum\count255<256 + \catcode\count255=#1\relax + \advance\count255 by 1 + \repeat +} + +% @documentencoding sets the definition of non-ASCII characters +% according to the specified encoding. +% +\parseargdef\documentencoding{% + % Encoding being declared for the document. + \def\declaredencoding{\csname #1.enc\endcsname}% + % + % Supported encodings: names converted to tokens in order to be able + % to compare them with \ifx. + \def\ascii{\csname US-ASCII.enc\endcsname}% + \def\latnine{\csname ISO-8859-15.enc\endcsname}% + \def\latone{\csname ISO-8859-1.enc\endcsname}% + \def\lattwo{\csname ISO-8859-2.enc\endcsname}% + \def\utfeight{\csname UTF-8.enc\endcsname}% + % + \ifx \declaredencoding \ascii + \asciichardefs + % + \else \ifx \declaredencoding \lattwo + \setnonasciicharscatcode\active + \lattwochardefs + % + \else \ifx \declaredencoding \latone + \setnonasciicharscatcode\active + \latonechardefs + % + \else \ifx \declaredencoding \latnine + \setnonasciicharscatcode\active + \latninechardefs + % + \else \ifx \declaredencoding \utfeight + \setnonasciicharscatcode\active + \utfeightchardefs + % + \else + \message{Unknown document encoding #1, ignoring.}% + % + \fi % utfeight + \fi % latnine + \fi % latone + \fi % lattwo + \fi % ascii +} + +% A message to be logged when using a character that isn't available +% the default font encoding (OT1). +% +\def\missingcharmsg#1{\message{Character missing in OT1 encoding: #1.}} + +% Take account of \c (plain) vs. \, (Texinfo) difference. +\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi} + +% First, make active non-ASCII characters in order for them to be +% correctly categorized when TeX reads the replacement text of +% macros containing the character definitions. +\setnonasciicharscatcode\active +% +% Latin1 (ISO-8859-1) character definitions. +\def\latonechardefs{% + \gdef^^a0{~} + \gdef^^a1{\exclamdown} + \gdef^^a2{\missingcharmsg{CENT SIGN}} + \gdef^^a3{{\pounds}} + \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} + \gdef^^a5{\missingcharmsg{YEN SIGN}} + \gdef^^a6{\missingcharmsg{BROKEN BAR}} + \gdef^^a7{\S} + \gdef^^a8{\"{}} + \gdef^^a9{\copyright} + \gdef^^aa{\ordf} + \gdef^^ab{\guillemetleft} + \gdef^^ac{$\lnot$} + \gdef^^ad{\-} + \gdef^^ae{\registeredsymbol} + \gdef^^af{\={}} + % + \gdef^^b0{\textdegree} + \gdef^^b1{$\pm$} + \gdef^^b2{$^2$} + \gdef^^b3{$^3$} + \gdef^^b4{\'{}} + \gdef^^b5{$\mu$} + \gdef^^b6{\P} + % + \gdef^^b7{$^.$} + \gdef^^b8{\cedilla\ } + \gdef^^b9{$^1$} + \gdef^^ba{\ordm} + % + \gdef^^bb{\guilletright} + \gdef^^bc{$1\over4$} + \gdef^^bd{$1\over2$} + \gdef^^be{$3\over4$} + \gdef^^bf{\questiondown} + % + \gdef^^c0{\`A} + \gdef^^c1{\'A} + \gdef^^c2{\^A} + \gdef^^c3{\~A} + \gdef^^c4{\"A} + \gdef^^c5{\ringaccent A} + \gdef^^c6{\AE} + \gdef^^c7{\cedilla C} + \gdef^^c8{\`E} + \gdef^^c9{\'E} + \gdef^^ca{\^E} + \gdef^^cb{\"E} + \gdef^^cc{\`I} + \gdef^^cd{\'I} + \gdef^^ce{\^I} + \gdef^^cf{\"I} + % + \gdef^^d0{\DH} + \gdef^^d1{\~N} + \gdef^^d2{\`O} + \gdef^^d3{\'O} + \gdef^^d4{\^O} + \gdef^^d5{\~O} + \gdef^^d6{\"O} + \gdef^^d7{$\times$} + \gdef^^d8{\O} + \gdef^^d9{\`U} + \gdef^^da{\'U} + \gdef^^db{\^U} + \gdef^^dc{\"U} + \gdef^^dd{\'Y} + \gdef^^de{\TH} + \gdef^^df{\ss} + % + \gdef^^e0{\`a} + \gdef^^e1{\'a} + \gdef^^e2{\^a} + \gdef^^e3{\~a} + \gdef^^e4{\"a} + \gdef^^e5{\ringaccent a} + \gdef^^e6{\ae} + \gdef^^e7{\cedilla c} + \gdef^^e8{\`e} + \gdef^^e9{\'e} + \gdef^^ea{\^e} + \gdef^^eb{\"e} + \gdef^^ec{\`{\dotless i}} + \gdef^^ed{\'{\dotless i}} + \gdef^^ee{\^{\dotless i}} + \gdef^^ef{\"{\dotless i}} + % + \gdef^^f0{\dh} + \gdef^^f1{\~n} + \gdef^^f2{\`o} + \gdef^^f3{\'o} + \gdef^^f4{\^o} + \gdef^^f5{\~o} + \gdef^^f6{\"o} + \gdef^^f7{$\div$} + \gdef^^f8{\o} + \gdef^^f9{\`u} + \gdef^^fa{\'u} + \gdef^^fb{\^u} + \gdef^^fc{\"u} + \gdef^^fd{\'y} + \gdef^^fe{\th} + \gdef^^ff{\"y} +} + +% Latin9 (ISO-8859-15) encoding character definitions. +\def\latninechardefs{% + % Encoding is almost identical to Latin1. + \latonechardefs + % + \gdef^^a4{\euro} + \gdef^^a6{\v S} + \gdef^^a8{\v s} + \gdef^^b4{\v Z} + \gdef^^b8{\v z} + \gdef^^bc{\OE} + \gdef^^bd{\oe} + \gdef^^be{\"Y} +} + +% Latin2 (ISO-8859-2) character definitions. +\def\lattwochardefs{% + \gdef^^a0{~} + \gdef^^a1{\ogonek{A}} + \gdef^^a2{\u{}} + \gdef^^a3{\L} + \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} + \gdef^^a5{\v L} + \gdef^^a6{\'S} + \gdef^^a7{\S} + \gdef^^a8{\"{}} + \gdef^^a9{\v S} + \gdef^^aa{\cedilla S} + \gdef^^ab{\v T} + \gdef^^ac{\'Z} + \gdef^^ad{\-} + \gdef^^ae{\v Z} + \gdef^^af{\dotaccent Z} + % + \gdef^^b0{\textdegree} + \gdef^^b1{\ogonek{a}} + \gdef^^b2{\ogonek{ }} + \gdef^^b3{\l} + \gdef^^b4{\'{}} + \gdef^^b5{\v l} + \gdef^^b6{\'s} + \gdef^^b7{\v{}} + \gdef^^b8{\cedilla\ } + \gdef^^b9{\v s} + \gdef^^ba{\cedilla s} + \gdef^^bb{\v t} + \gdef^^bc{\'z} + \gdef^^bd{\H{}} + \gdef^^be{\v z} + \gdef^^bf{\dotaccent z} + % + \gdef^^c0{\'R} + \gdef^^c1{\'A} + \gdef^^c2{\^A} + \gdef^^c3{\u A} + \gdef^^c4{\"A} + \gdef^^c5{\'L} + \gdef^^c6{\'C} + \gdef^^c7{\cedilla C} + \gdef^^c8{\v C} + \gdef^^c9{\'E} + \gdef^^ca{\ogonek{E}} + \gdef^^cb{\"E} + \gdef^^cc{\v E} + \gdef^^cd{\'I} + \gdef^^ce{\^I} + \gdef^^cf{\v D} + % + \gdef^^d0{\DH} + \gdef^^d1{\'N} + \gdef^^d2{\v N} + \gdef^^d3{\'O} + \gdef^^d4{\^O} + \gdef^^d5{\H O} + \gdef^^d6{\"O} + \gdef^^d7{$\times$} + \gdef^^d8{\v R} + \gdef^^d9{\ringaccent U} + \gdef^^da{\'U} + \gdef^^db{\H U} + \gdef^^dc{\"U} + \gdef^^dd{\'Y} + \gdef^^de{\cedilla T} + \gdef^^df{\ss} + % + \gdef^^e0{\'r} + \gdef^^e1{\'a} + \gdef^^e2{\^a} + \gdef^^e3{\u a} + \gdef^^e4{\"a} + \gdef^^e5{\'l} + \gdef^^e6{\'c} + \gdef^^e7{\cedilla c} + \gdef^^e8{\v c} + \gdef^^e9{\'e} + \gdef^^ea{\ogonek{e}} + \gdef^^eb{\"e} + \gdef^^ec{\v e} + \gdef^^ed{\'\i} + \gdef^^ee{\^\i} + \gdef^^ef{\v d} + % + \gdef^^f0{\dh} + \gdef^^f1{\'n} + \gdef^^f2{\v n} + \gdef^^f3{\'o} + \gdef^^f4{\^o} + \gdef^^f5{\H o} + \gdef^^f6{\"o} + \gdef^^f7{$\div$} + \gdef^^f8{\v r} + \gdef^^f9{\ringaccent u} + \gdef^^fa{\'u} + \gdef^^fb{\H u} + \gdef^^fc{\"u} + \gdef^^fd{\'y} + \gdef^^fe{\cedilla t} + \gdef^^ff{\dotaccent{}} +} + +% UTF-8 character definitions. +% +% This code to support UTF-8 is based on LaTeX's utf8.def, with some +% changes for Texinfo conventions. It is included here under the GPL by +% permission from Frank Mittelbach and the LaTeX team. +% +\newcount\countUTFx +\newcount\countUTFy +\newcount\countUTFz + +\gdef\UTFviiiTwoOctets#1#2{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\endcsname} +% +\gdef\UTFviiiThreeOctets#1#2#3{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname} +% +\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname} + +\gdef\UTFviiiDefined#1{% + \ifx #1\relax + \message{\linenumber Unicode char \string #1 not defined for Texinfo}% + \else + \expandafter #1% + \fi +} + +\begingroup + \catcode`\~13 + \catcode`\"12 + + \def\UTFviiiLoop{% + \global\catcode\countUTFx\active + \uccode`\~\countUTFx + \uppercase\expandafter{\UTFviiiTmp}% + \advance\countUTFx by 1 + \ifnum\countUTFx < \countUTFy + \expandafter\UTFviiiLoop + \fi} + + \countUTFx = "C2 + \countUTFy = "E0 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiTwoOctets\string~}} + \UTFviiiLoop + + \countUTFx = "E0 + \countUTFy = "F0 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiThreeOctets\string~}} + \UTFviiiLoop + + \countUTFx = "F0 + \countUTFy = "F4 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiFourOctets\string~}} + \UTFviiiLoop +\endgroup + +\begingroup + \catcode`\"=12 + \catcode`\<=12 + \catcode`\.=12 + \catcode`\,=12 + \catcode`\;=12 + \catcode`\!=12 + \catcode`\~=13 + + \gdef\DeclareUnicodeCharacter#1#2{% + \countUTFz = "#1\relax + \wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}% + \begingroup + \parseXMLCharref + \def\UTFviiiTwoOctets##1##2{% + \csname u8:##1\string ##2\endcsname}% + \def\UTFviiiThreeOctets##1##2##3{% + \csname u8:##1\string ##2\string ##3\endcsname}% + \def\UTFviiiFourOctets##1##2##3##4{% + \csname u8:##1\string ##2\string ##3\string ##4\endcsname}% + \expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter + \gdef\UTFviiiTmp{#2}% + \endgroup} + + \gdef\parseXMLCharref{% + \ifnum\countUTFz < "A0\relax + \errhelp = \EMsimple + \errmessage{Cannot define Unicode char value < 00A0}% + \else\ifnum\countUTFz < "800\relax + \parseUTFviiiA,% + \parseUTFviiiB C\UTFviiiTwoOctets.,% + \else\ifnum\countUTFz < "10000\relax + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiB E\UTFviiiThreeOctets.{,;}% + \else + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiA!% + \parseUTFviiiB F\UTFviiiFourOctets.{!,;}% + \fi\fi\fi + } + + \gdef\parseUTFviiiA#1{% + \countUTFx = \countUTFz + \divide\countUTFz by 64 + \countUTFy = \countUTFz + \multiply\countUTFz by 64 + \advance\countUTFx by -\countUTFz + \advance\countUTFx by 128 + \uccode `#1\countUTFx + \countUTFz = \countUTFy} + + \gdef\parseUTFviiiB#1#2#3#4{% + \advance\countUTFz by "#10\relax + \uccode `#3\countUTFz + \uppercase{\gdef\UTFviiiTmp{#2#3#4}}} +\endgroup + +\def\utfeightchardefs{% + \DeclareUnicodeCharacter{00A0}{\tie} + \DeclareUnicodeCharacter{00A1}{\exclamdown} + \DeclareUnicodeCharacter{00A3}{\pounds} + \DeclareUnicodeCharacter{00A8}{\"{ }} + \DeclareUnicodeCharacter{00A9}{\copyright} + \DeclareUnicodeCharacter{00AA}{\ordf} + \DeclareUnicodeCharacter{00AB}{\guillemetleft} + \DeclareUnicodeCharacter{00AD}{\-} + \DeclareUnicodeCharacter{00AE}{\registeredsymbol} + \DeclareUnicodeCharacter{00AF}{\={ }} + + \DeclareUnicodeCharacter{00B0}{\ringaccent{ }} + \DeclareUnicodeCharacter{00B4}{\'{ }} + \DeclareUnicodeCharacter{00B8}{\cedilla{ }} + \DeclareUnicodeCharacter{00BA}{\ordm} + \DeclareUnicodeCharacter{00BB}{\guillemetright} + \DeclareUnicodeCharacter{00BF}{\questiondown} + + \DeclareUnicodeCharacter{00C0}{\`A} + \DeclareUnicodeCharacter{00C1}{\'A} + \DeclareUnicodeCharacter{00C2}{\^A} + \DeclareUnicodeCharacter{00C3}{\~A} + \DeclareUnicodeCharacter{00C4}{\"A} + \DeclareUnicodeCharacter{00C5}{\AA} + \DeclareUnicodeCharacter{00C6}{\AE} + \DeclareUnicodeCharacter{00C7}{\cedilla{C}} + \DeclareUnicodeCharacter{00C8}{\`E} + \DeclareUnicodeCharacter{00C9}{\'E} + \DeclareUnicodeCharacter{00CA}{\^E} + \DeclareUnicodeCharacter{00CB}{\"E} + \DeclareUnicodeCharacter{00CC}{\`I} + \DeclareUnicodeCharacter{00CD}{\'I} + \DeclareUnicodeCharacter{00CE}{\^I} + \DeclareUnicodeCharacter{00CF}{\"I} + + \DeclareUnicodeCharacter{00D0}{\DH} + \DeclareUnicodeCharacter{00D1}{\~N} + \DeclareUnicodeCharacter{00D2}{\`O} + \DeclareUnicodeCharacter{00D3}{\'O} + \DeclareUnicodeCharacter{00D4}{\^O} + \DeclareUnicodeCharacter{00D5}{\~O} + \DeclareUnicodeCharacter{00D6}{\"O} + \DeclareUnicodeCharacter{00D8}{\O} + \DeclareUnicodeCharacter{00D9}{\`U} + \DeclareUnicodeCharacter{00DA}{\'U} + \DeclareUnicodeCharacter{00DB}{\^U} + \DeclareUnicodeCharacter{00DC}{\"U} + \DeclareUnicodeCharacter{00DD}{\'Y} + \DeclareUnicodeCharacter{00DE}{\TH} + \DeclareUnicodeCharacter{00DF}{\ss} + + \DeclareUnicodeCharacter{00E0}{\`a} + \DeclareUnicodeCharacter{00E1}{\'a} + \DeclareUnicodeCharacter{00E2}{\^a} + \DeclareUnicodeCharacter{00E3}{\~a} + \DeclareUnicodeCharacter{00E4}{\"a} + \DeclareUnicodeCharacter{00E5}{\aa} + \DeclareUnicodeCharacter{00E6}{\ae} + \DeclareUnicodeCharacter{00E7}{\cedilla{c}} + \DeclareUnicodeCharacter{00E8}{\`e} + \DeclareUnicodeCharacter{00E9}{\'e} + \DeclareUnicodeCharacter{00EA}{\^e} + \DeclareUnicodeCharacter{00EB}{\"e} + \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}} + \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}} + \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}} + \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}} + + \DeclareUnicodeCharacter{00F0}{\dh} + \DeclareUnicodeCharacter{00F1}{\~n} + \DeclareUnicodeCharacter{00F2}{\`o} + \DeclareUnicodeCharacter{00F3}{\'o} + \DeclareUnicodeCharacter{00F4}{\^o} + \DeclareUnicodeCharacter{00F5}{\~o} + \DeclareUnicodeCharacter{00F6}{\"o} + \DeclareUnicodeCharacter{00F8}{\o} + \DeclareUnicodeCharacter{00F9}{\`u} + \DeclareUnicodeCharacter{00FA}{\'u} + \DeclareUnicodeCharacter{00FB}{\^u} + \DeclareUnicodeCharacter{00FC}{\"u} + \DeclareUnicodeCharacter{00FD}{\'y} + \DeclareUnicodeCharacter{00FE}{\th} + \DeclareUnicodeCharacter{00FF}{\"y} + + \DeclareUnicodeCharacter{0100}{\=A} + \DeclareUnicodeCharacter{0101}{\=a} + \DeclareUnicodeCharacter{0102}{\u{A}} + \DeclareUnicodeCharacter{0103}{\u{a}} + \DeclareUnicodeCharacter{0104}{\ogonek{A}} + \DeclareUnicodeCharacter{0105}{\ogonek{a}} + \DeclareUnicodeCharacter{0106}{\'C} + \DeclareUnicodeCharacter{0107}{\'c} + \DeclareUnicodeCharacter{0108}{\^C} + \DeclareUnicodeCharacter{0109}{\^c} + \DeclareUnicodeCharacter{0118}{\ogonek{E}} + \DeclareUnicodeCharacter{0119}{\ogonek{e}} + \DeclareUnicodeCharacter{010A}{\dotaccent{C}} + \DeclareUnicodeCharacter{010B}{\dotaccent{c}} + \DeclareUnicodeCharacter{010C}{\v{C}} + \DeclareUnicodeCharacter{010D}{\v{c}} + \DeclareUnicodeCharacter{010E}{\v{D}} + + \DeclareUnicodeCharacter{0112}{\=E} + \DeclareUnicodeCharacter{0113}{\=e} + \DeclareUnicodeCharacter{0114}{\u{E}} + \DeclareUnicodeCharacter{0115}{\u{e}} + \DeclareUnicodeCharacter{0116}{\dotaccent{E}} + \DeclareUnicodeCharacter{0117}{\dotaccent{e}} + \DeclareUnicodeCharacter{011A}{\v{E}} + \DeclareUnicodeCharacter{011B}{\v{e}} + \DeclareUnicodeCharacter{011C}{\^G} + \DeclareUnicodeCharacter{011D}{\^g} + \DeclareUnicodeCharacter{011E}{\u{G}} + \DeclareUnicodeCharacter{011F}{\u{g}} + + \DeclareUnicodeCharacter{0120}{\dotaccent{G}} + \DeclareUnicodeCharacter{0121}{\dotaccent{g}} + \DeclareUnicodeCharacter{0124}{\^H} + \DeclareUnicodeCharacter{0125}{\^h} + \DeclareUnicodeCharacter{0128}{\~I} + \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}} + \DeclareUnicodeCharacter{012A}{\=I} + \DeclareUnicodeCharacter{012B}{\={\dotless{i}}} + \DeclareUnicodeCharacter{012C}{\u{I}} + \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}} + + \DeclareUnicodeCharacter{0130}{\dotaccent{I}} + \DeclareUnicodeCharacter{0131}{\dotless{i}} + \DeclareUnicodeCharacter{0132}{IJ} + \DeclareUnicodeCharacter{0133}{ij} + \DeclareUnicodeCharacter{0134}{\^J} + \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}} + \DeclareUnicodeCharacter{0139}{\'L} + \DeclareUnicodeCharacter{013A}{\'l} + + \DeclareUnicodeCharacter{0141}{\L} + \DeclareUnicodeCharacter{0142}{\l} + \DeclareUnicodeCharacter{0143}{\'N} + \DeclareUnicodeCharacter{0144}{\'n} + \DeclareUnicodeCharacter{0147}{\v{N}} + \DeclareUnicodeCharacter{0148}{\v{n}} + \DeclareUnicodeCharacter{014C}{\=O} + \DeclareUnicodeCharacter{014D}{\=o} + \DeclareUnicodeCharacter{014E}{\u{O}} + \DeclareUnicodeCharacter{014F}{\u{o}} + + \DeclareUnicodeCharacter{0150}{\H{O}} + \DeclareUnicodeCharacter{0151}{\H{o}} + \DeclareUnicodeCharacter{0152}{\OE} + \DeclareUnicodeCharacter{0153}{\oe} + \DeclareUnicodeCharacter{0154}{\'R} + \DeclareUnicodeCharacter{0155}{\'r} + \DeclareUnicodeCharacter{0158}{\v{R}} + \DeclareUnicodeCharacter{0159}{\v{r}} + \DeclareUnicodeCharacter{015A}{\'S} + \DeclareUnicodeCharacter{015B}{\'s} + \DeclareUnicodeCharacter{015C}{\^S} + \DeclareUnicodeCharacter{015D}{\^s} + \DeclareUnicodeCharacter{015E}{\cedilla{S}} + \DeclareUnicodeCharacter{015F}{\cedilla{s}} + + \DeclareUnicodeCharacter{0160}{\v{S}} + \DeclareUnicodeCharacter{0161}{\v{s}} + \DeclareUnicodeCharacter{0162}{\cedilla{t}} + \DeclareUnicodeCharacter{0163}{\cedilla{T}} + \DeclareUnicodeCharacter{0164}{\v{T}} + + \DeclareUnicodeCharacter{0168}{\~U} + \DeclareUnicodeCharacter{0169}{\~u} + \DeclareUnicodeCharacter{016A}{\=U} + \DeclareUnicodeCharacter{016B}{\=u} + \DeclareUnicodeCharacter{016C}{\u{U}} + \DeclareUnicodeCharacter{016D}{\u{u}} + \DeclareUnicodeCharacter{016E}{\ringaccent{U}} + \DeclareUnicodeCharacter{016F}{\ringaccent{u}} + + \DeclareUnicodeCharacter{0170}{\H{U}} + \DeclareUnicodeCharacter{0171}{\H{u}} + \DeclareUnicodeCharacter{0174}{\^W} + \DeclareUnicodeCharacter{0175}{\^w} + \DeclareUnicodeCharacter{0176}{\^Y} + \DeclareUnicodeCharacter{0177}{\^y} + \DeclareUnicodeCharacter{0178}{\"Y} + \DeclareUnicodeCharacter{0179}{\'Z} + \DeclareUnicodeCharacter{017A}{\'z} + \DeclareUnicodeCharacter{017B}{\dotaccent{Z}} + \DeclareUnicodeCharacter{017C}{\dotaccent{z}} + \DeclareUnicodeCharacter{017D}{\v{Z}} + \DeclareUnicodeCharacter{017E}{\v{z}} + + \DeclareUnicodeCharacter{01C4}{D\v{Z}} + \DeclareUnicodeCharacter{01C5}{D\v{z}} + \DeclareUnicodeCharacter{01C6}{d\v{z}} + \DeclareUnicodeCharacter{01C7}{LJ} + \DeclareUnicodeCharacter{01C8}{Lj} + \DeclareUnicodeCharacter{01C9}{lj} + \DeclareUnicodeCharacter{01CA}{NJ} + \DeclareUnicodeCharacter{01CB}{Nj} + \DeclareUnicodeCharacter{01CC}{nj} + \DeclareUnicodeCharacter{01CD}{\v{A}} + \DeclareUnicodeCharacter{01CE}{\v{a}} + \DeclareUnicodeCharacter{01CF}{\v{I}} + + \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}} + \DeclareUnicodeCharacter{01D1}{\v{O}} + \DeclareUnicodeCharacter{01D2}{\v{o}} + \DeclareUnicodeCharacter{01D3}{\v{U}} + \DeclareUnicodeCharacter{01D4}{\v{u}} + + \DeclareUnicodeCharacter{01E2}{\={\AE}} + \DeclareUnicodeCharacter{01E3}{\={\ae}} + \DeclareUnicodeCharacter{01E6}{\v{G}} + \DeclareUnicodeCharacter{01E7}{\v{g}} + \DeclareUnicodeCharacter{01E8}{\v{K}} + \DeclareUnicodeCharacter{01E9}{\v{k}} + + \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}} + \DeclareUnicodeCharacter{01F1}{DZ} + \DeclareUnicodeCharacter{01F2}{Dz} + \DeclareUnicodeCharacter{01F3}{dz} + \DeclareUnicodeCharacter{01F4}{\'G} + \DeclareUnicodeCharacter{01F5}{\'g} + \DeclareUnicodeCharacter{01F8}{\`N} + \DeclareUnicodeCharacter{01F9}{\`n} + \DeclareUnicodeCharacter{01FC}{\'{\AE}} + \DeclareUnicodeCharacter{01FD}{\'{\ae}} + \DeclareUnicodeCharacter{01FE}{\'{\O}} + \DeclareUnicodeCharacter{01FF}{\'{\o}} + + \DeclareUnicodeCharacter{021E}{\v{H}} + \DeclareUnicodeCharacter{021F}{\v{h}} + + \DeclareUnicodeCharacter{0226}{\dotaccent{A}} + \DeclareUnicodeCharacter{0227}{\dotaccent{a}} + \DeclareUnicodeCharacter{0228}{\cedilla{E}} + \DeclareUnicodeCharacter{0229}{\cedilla{e}} + \DeclareUnicodeCharacter{022E}{\dotaccent{O}} + \DeclareUnicodeCharacter{022F}{\dotaccent{o}} + + \DeclareUnicodeCharacter{0232}{\=Y} + \DeclareUnicodeCharacter{0233}{\=y} + \DeclareUnicodeCharacter{0237}{\dotless{j}} + + \DeclareUnicodeCharacter{02DB}{\ogonek{ }} + + \DeclareUnicodeCharacter{1E02}{\dotaccent{B}} + \DeclareUnicodeCharacter{1E03}{\dotaccent{b}} + \DeclareUnicodeCharacter{1E04}{\udotaccent{B}} + \DeclareUnicodeCharacter{1E05}{\udotaccent{b}} + \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}} + \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}} + \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}} + \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}} + \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}} + \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}} + \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}} + \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}} + + \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}} + \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}} + + \DeclareUnicodeCharacter{1E20}{\=G} + \DeclareUnicodeCharacter{1E21}{\=g} + \DeclareUnicodeCharacter{1E22}{\dotaccent{H}} + \DeclareUnicodeCharacter{1E23}{\dotaccent{h}} + \DeclareUnicodeCharacter{1E24}{\udotaccent{H}} + \DeclareUnicodeCharacter{1E25}{\udotaccent{h}} + \DeclareUnicodeCharacter{1E26}{\"H} + \DeclareUnicodeCharacter{1E27}{\"h} + + \DeclareUnicodeCharacter{1E30}{\'K} + \DeclareUnicodeCharacter{1E31}{\'k} + \DeclareUnicodeCharacter{1E32}{\udotaccent{K}} + \DeclareUnicodeCharacter{1E33}{\udotaccent{k}} + \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}} + \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}} + \DeclareUnicodeCharacter{1E36}{\udotaccent{L}} + \DeclareUnicodeCharacter{1E37}{\udotaccent{l}} + \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}} + \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}} + \DeclareUnicodeCharacter{1E3E}{\'M} + \DeclareUnicodeCharacter{1E3F}{\'m} + + \DeclareUnicodeCharacter{1E40}{\dotaccent{M}} + \DeclareUnicodeCharacter{1E41}{\dotaccent{m}} + \DeclareUnicodeCharacter{1E42}{\udotaccent{M}} + \DeclareUnicodeCharacter{1E43}{\udotaccent{m}} + \DeclareUnicodeCharacter{1E44}{\dotaccent{N}} + \DeclareUnicodeCharacter{1E45}{\dotaccent{n}} + \DeclareUnicodeCharacter{1E46}{\udotaccent{N}} + \DeclareUnicodeCharacter{1E47}{\udotaccent{n}} + \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}} + \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}} + + \DeclareUnicodeCharacter{1E54}{\'P} + \DeclareUnicodeCharacter{1E55}{\'p} + \DeclareUnicodeCharacter{1E56}{\dotaccent{P}} + \DeclareUnicodeCharacter{1E57}{\dotaccent{p}} + \DeclareUnicodeCharacter{1E58}{\dotaccent{R}} + \DeclareUnicodeCharacter{1E59}{\dotaccent{r}} + \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}} + \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}} + \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}} + \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}} + + \DeclareUnicodeCharacter{1E60}{\dotaccent{S}} + \DeclareUnicodeCharacter{1E61}{\dotaccent{s}} + \DeclareUnicodeCharacter{1E62}{\udotaccent{S}} + \DeclareUnicodeCharacter{1E63}{\udotaccent{s}} + \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}} + \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}} + \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}} + \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}} + \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}} + \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}} + + \DeclareUnicodeCharacter{1E7C}{\~V} + \DeclareUnicodeCharacter{1E7D}{\~v} + \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}} + \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}} + + \DeclareUnicodeCharacter{1E80}{\`W} + \DeclareUnicodeCharacter{1E81}{\`w} + \DeclareUnicodeCharacter{1E82}{\'W} + \DeclareUnicodeCharacter{1E83}{\'w} + \DeclareUnicodeCharacter{1E84}{\"W} + \DeclareUnicodeCharacter{1E85}{\"w} + \DeclareUnicodeCharacter{1E86}{\dotaccent{W}} + \DeclareUnicodeCharacter{1E87}{\dotaccent{w}} + \DeclareUnicodeCharacter{1E88}{\udotaccent{W}} + \DeclareUnicodeCharacter{1E89}{\udotaccent{w}} + \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}} + \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}} + \DeclareUnicodeCharacter{1E8C}{\"X} + \DeclareUnicodeCharacter{1E8D}{\"x} + \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}} + \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}} + + \DeclareUnicodeCharacter{1E90}{\^Z} + \DeclareUnicodeCharacter{1E91}{\^z} + \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}} + \DeclareUnicodeCharacter{1E93}{\udotaccent{z}} + \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}} + \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}} + \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}} + \DeclareUnicodeCharacter{1E97}{\"t} + \DeclareUnicodeCharacter{1E98}{\ringaccent{w}} + \DeclareUnicodeCharacter{1E99}{\ringaccent{y}} + + \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}} + \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}} + + \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}} + \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}} + \DeclareUnicodeCharacter{1EBC}{\~E} + \DeclareUnicodeCharacter{1EBD}{\~e} + + \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}} + \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}} + \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}} + \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}} + + \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}} + \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}} + + \DeclareUnicodeCharacter{1EF2}{\`Y} + \DeclareUnicodeCharacter{1EF3}{\`y} + \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}} + + \DeclareUnicodeCharacter{1EF8}{\~Y} + \DeclareUnicodeCharacter{1EF9}{\~y} + + \DeclareUnicodeCharacter{2013}{--} + \DeclareUnicodeCharacter{2014}{---} + \DeclareUnicodeCharacter{2018}{\quoteleft} + \DeclareUnicodeCharacter{2019}{\quoteright} + \DeclareUnicodeCharacter{201A}{\quotesinglbase} + \DeclareUnicodeCharacter{201C}{\quotedblleft} + \DeclareUnicodeCharacter{201D}{\quotedblright} + \DeclareUnicodeCharacter{201E}{\quotedblbase} + \DeclareUnicodeCharacter{2022}{\bullet} + \DeclareUnicodeCharacter{2026}{\dots} + \DeclareUnicodeCharacter{2039}{\guilsinglleft} + \DeclareUnicodeCharacter{203A}{\guilsinglright} + \DeclareUnicodeCharacter{20AC}{\euro} + + \DeclareUnicodeCharacter{2192}{\expansion} + \DeclareUnicodeCharacter{21D2}{\result} + + \DeclareUnicodeCharacter{2212}{\minus} + \DeclareUnicodeCharacter{2217}{\point} + \DeclareUnicodeCharacter{2261}{\equiv} +}% end of \utfeightchardefs + + +% US-ASCII character definitions. +\def\asciichardefs{% nothing need be done + \relax +} + +% Make non-ASCII characters printable again for compatibility with +% existing Texinfo documents that may use them, even without declaring a +% document encoding. +% +\setnonasciicharscatcode \other + + +\message{formatting,} + +\newdimen\defaultparindent \defaultparindent = 15pt + +\chapheadingskip = 15pt plus 4pt minus 2pt +\secheadingskip = 12pt plus 3pt minus 2pt +\subsecheadingskip = 9pt plus 2pt minus 2pt + +% Prevent underfull vbox error messages. +\vbadness = 10000 + +% Don't be so finicky about underfull hboxes, either. +\hbadness = 2000 + +% Following George Bush, get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. We call this whenever the paper size is set. +% +\def\setemergencystretch{% + \ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% + \else + \emergencystretch = .15\hsize + \fi +} + +% Parameters in order: 1) textheight; 2) textwidth; +% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; +% 7) physical page height; 8) physical page width. +% +% We also call \setleading{\textleading}, so the caller should define +% \textleading. The caller should also set \parskip. +% +\def\internalpagesizes#1#2#3#4#5#6#7#8{% + \voffset = #3\relax + \topskip = #6\relax + \splittopskip = \topskip + % + \vsize = #1\relax + \advance\vsize by \topskip + \outervsize = \vsize + \advance\outervsize by 2\topandbottommargin + \pageheight = \vsize + % + \hsize = #2\relax + \outerhsize = \hsize + \advance\outerhsize by 0.5in + \pagewidth = \hsize + % + \normaloffset = #4\relax + \bindingoffset = #5\relax + % + \ifpdf + \pdfpageheight #7\relax + \pdfpagewidth #8\relax + % if we don't reset these, they will remain at "1 true in" of + % whatever layout pdftex was dumped with. + \pdfhorigin = 1 true in + \pdfvorigin = 1 true in + \fi + % + \setleading{\textleading} + % + \parindent = \defaultparindent + \setemergencystretch +} + +% @letterpaper (the default). +\def\letterpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % If page is nothing but text, make it come out even. + \internalpagesizes{607.2pt}{6in}% that's 46 lines + {\voffset}{.25in}% + {\bindingoffset}{36pt}% + {11in}{8.5in}% +}} + +% Use @smallbook to reset parameters for 7x9.25 trim size. +\def\smallbook{{\globaldefs = 1 + \parskip = 2pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.5in}{5in}% + {-.2in}{0in}% + {\bindingoffset}{16pt}% + {9.25in}{7in}% + % + \lispnarrowing = 0.3in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .5cm +}} + +% Use @smallerbook to reset parameters for 6x9 trim size. +% (Just testing, parameters still in flux.) +\def\smallerbook{{\globaldefs = 1 + \parskip = 1.5pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.4in}{4.8in}% + {-.2in}{-.4in}% + {0pt}{14pt}% + {9in}{6in}% + % + \lispnarrowing = 0.25in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .4cm +}} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % Double-side printing via postscript on Laserjet 4050 + % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. + % To change the settings for a different printer or situation, adjust + % \normaloffset until the front-side and back-side texts align. Then + % do the same for \bindingoffset. You can set these for testing in + % your texinfo source file like this: + % @tex + % \global\normaloffset = -6mm + % \global\bindingoffset = 10mm + % @end tex + \internalpagesizes{673.2pt}{160mm}% that's 51 lines + {\voffset}{\hoffset}% + {\bindingoffset}{44pt}% + {297mm}{210mm}% + % + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = 5mm +}} + +% Use @afivepaper to print on European A5 paper. +% From romildo@urano.iceb.ufop.br, 2 July 2000. +% He also recommends making @example and @lisp be small. +\def\afivepaper{{\globaldefs = 1 + \parskip = 2pt plus 1pt minus 0.1pt + \textleading = 12.5pt + % + \internalpagesizes{160mm}{120mm}% + {\voffset}{\hoffset}% + {\bindingoffset}{8pt}% + {210mm}{148mm}% + % + \lispnarrowing = 0.2in + \tolerance = 800 + \hfuzz = 1.2pt + \contentsrightmargin = 0pt + \defbodyindent = 2mm + \tableindent = 12mm +}} + +% A specific text layout, 24x15cm overall, intended for A4 paper. +\def\afourlatex{{\globaldefs = 1 + \afourpaper + \internalpagesizes{237mm}{150mm}% + {\voffset}{4.6mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + % + % Must explicitly reset to 0 because we call \afourpaper. + \globaldefs = 0 +}} + +% Use @afourwide to print on A4 paper in landscape format. +\def\afourwide{{\globaldefs = 1 + \afourpaper + \internalpagesizes{241mm}{165mm}% + {\voffset}{-2.95mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + \globaldefs = 0 +}} + +% @pagesizes TEXTHEIGHT[,TEXTWIDTH] +% Perhaps we should allow setting the margins, \topskip, \parskip, +% and/or leading, also. Or perhaps we should compute them somehow. +% +\parseargdef\pagesizes{\pagesizesyyy #1,,\finish} +\def\pagesizesyyy#1,#2,#3\finish{{% + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi + \globaldefs = 1 + % + \parskip = 3pt plus 2pt minus 1pt + \setleading{\textleading}% + % + \dimen0 = #1\relax + \advance\dimen0 by \voffset + % + \dimen2 = \hsize + \advance\dimen2 by \normaloffset + % + \internalpagesizes{#1}{\hsize}% + {\voffset}{\normaloffset}% + {\bindingoffset}{44pt}% + {\dimen0}{\dimen2}% +}} + +% Set default to letter. +% +\letterpaper + + +\message{and turning on texinfo input format.} + +% DEL is a comment character, in case @c does not suffice. +\catcode`\^^? = 14 + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other +\catcode`\~=\other +\catcode`\^=\other +\catcode`\_=\other +\catcode`\|=\other +\catcode`\<=\other +\catcode`\>=\other +\catcode`\+=\other +\catcode`\$=\other +\def\normaldoublequote{"} +\def\normaltilde{~} +\def\normalcaret{^} +\def\normalunderscore{_} +\def\normalverticalbar{|} +\def\normalless{<} +\def\normalgreater{>} +\def\normalplus{+} +\def\normaldollar{$}%$ font-lock fix + +% This macro is used to make a character print one way in \tt +% (where it can probably be output as-is), and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} + +% Same as above, but check for italic font. Actually this also catches +% non-italic slanted fonts since it is impossible to distinguish them from +% italic fonts. But since this is only used by $ and it uses \sl anyway +% this is not a problem. +\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} + +% Turn off all special characters except @ +% (and those which the user can use as if they were ordinary). +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. + +\catcode`\"=\active +\def\activedoublequote{{\tt\char34}} +\let"=\activedoublequote +\catcode`\~=\active +\def~{{\tt\char126}} +\chardef\hat=`\^ +\catcode`\^=\active +\def^{{\tt \hat}} + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +\let\realunder=_ +% Subroutine for the previous macro. +\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } + +\catcode`\|=\active +\def|{{\tt\char124}} +\chardef \less=`\< +\catcode`\<=\active +\def<{{\tt \less}} +\chardef \gtr=`\> +\catcode`\>=\active +\def>{{\tt \gtr}} +\catcode`\+=\active +\def+{{\tt \char 43}} +\catcode`\$=\active +\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix + +% If a .fmt file is being used, characters that might appear in a file +% name cannot be active until we have parsed the command line. +% So turn them off again, and have \everyjob (or @setfilename) turn them on. +% \otherifyactive is called near the end of this file. +\def\otherifyactive{\catcode`+=\other \catcode`\_=\other} + +% Used sometimes to turn off (effectively) the active characters even after +% parsing them. +\def\turnoffactive{% + \normalturnoffactive + \otherbackslash +} + +\catcode`\@=0 + +% \backslashcurfont outputs one backslash character in current font, +% as in \char`\\. +\global\chardef\backslashcurfont=`\\ +\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work + +% \realbackslash is an actual character `\' with catcode other, and +% \doublebackslash is two of them (for the pdf outlines). +{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}} + +% In texinfo, backslash is an active character; it prints the backslash +% in fixed width font. +\catcode`\\=\active +@def@normalbackslash{{@tt@backslashcurfont}} +% On startup, @fixbackslash assigns: +% @let \ = @normalbackslash + +% \rawbackslash defines an active \ to do \backslashcurfont. +% \otherbackslash defines an active \ to be a literal `\' character with +% catcode other. +@gdef@rawbackslash{@let\=@backslashcurfont} +@gdef@otherbackslash{@let\=@realbackslash} + +% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of +% the literal character `\'. +% +@def@normalturnoffactive{% + @let\=@normalbackslash + @let"=@normaldoublequote + @let~=@normaltilde + @let^=@normalcaret + @let_=@normalunderscore + @let|=@normalverticalbar + @let<=@normalless + @let>=@normalgreater + @let+=@normalplus + @let$=@normaldollar %$ font-lock fix + @markupsetuplqdefault + @markupsetuprqdefault + @unsepspaces +} + +% Make _ and + \other characters, temporarily. +% This is canceled by @fixbackslash. +@otherifyactive + +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% +@gdef@eatinput input texinfo{@fixbackslash} +@global@let\ = @eatinput + +% On the other hand, perhaps the file did not have a `\input texinfo'. Then +% the first `\' in the file would cause an error. This macro tries to fix +% that, assuming it is called before the first `\' could plausibly occur. +% Also turn back on active characters that might appear in the input +% file name, in case not using a pre-dumped format. +% +@gdef@fixbackslash{% + @ifx\@eatinput @let\ = @normalbackslash @fi + @catcode`+=@active + @catcode`@_=@active +} + +% Say @foo, not \foo, in error messages. +@escapechar = `@@ + +% These look ok in all fonts, so just make them not special. +@catcode`@& = @other +@catcode`@# = @other +@catcode`@% = @other + +@c Finally, make ` and ' active, so that txicodequoteundirected and +@c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we +@c don't make ` and ' active, @code will not get them as active chars. +@c Do this last of all since we use ` in the previous @catcode assignments. +@catcode`@'=@active +@catcode`@`=@active +@markupsetuplqdefault +@markupsetuprqdefault + +@c Local variables: +@c eval: (add-hook 'write-file-hooks 'time-stamp) +@c page-delimiter: "^\\\\message" +@c time-stamp-start: "def\\\\texinfoversion{" +@c time-stamp-format: "%:y-%02m-%02d.%02H" +@c time-stamp-end: "}" +@c End: + +@c vim:sw=2: + +@ignore + arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115 +@end ignore diff --git a/doc/troubleshooting.texi b/doc/troubleshooting.texi new file mode 100755 index 0000000..18fee7a --- /dev/null +++ b/doc/troubleshooting.texi @@ -0,0 +1,131 @@ +@node Troubleshooting, Statement Index, Running Knot DNS, Top +@chapter Troubleshooting + +@menu +* Submitting a bugreport:: +* Generating backtrace:: +* Debug messages:: +@end menu + +First of all, check the logs (@pxref{log}). +By default, Knot DNS logs all error messages to syslog. Enabling at least +the @code{warning} message severity may help you identify some problems. + +@node Submitting a bugreport +@section Submitting a bugreport + +If you are unable to solve the problem by yourselves, you can submit a +bugreport to the Knot DNS team. For security issues (e.g. crash) do not +use the public mailinglist. Instead, write to +@url{mailto:knot-dns@@labs.nic.cz, knot-dns@@labs.nic.cz}. All other bugs +and questions may be directed to the Knot DNS users mailinglist +(@url{mailto:knot-dns-users@@lists.nic.cz, knot-dns-users@@lists.nic.cz}). + +The bugreport should contain at least: +@itemize +@item Knot DNS version and type of installation (source, package, etc.), +@item type and version of your operating system, +@item basic hardware information, +@item description of the bug, +@item log output of all messages (category @code{any}) with severity Info +and higher (severities @code{info, notice, warning, error}, or @code{any} if debug messages are not turned on (see below)), +@item steps to reproduce the bug (if known), +@item backtrace (if the bug caused a crash; see next section). +@end itemize + +If it is possible, the actual configuration file and/or zone file(s) will +be very useful as well. + +@node Generating backtrace +@section Generating backtrace + +There are several ways to achieve that, the most common way is to leave core +dumps and then extract a backtrace from it. +This doesn't affect any server operation, you just need to make sure +the OS is configured to generate them. + +@example +$ ulimit -c unlimited # enable unlimited core dump size +... +$ gdb $(which knotd) core.<KNOT_PID> # start gdb on a core dump +(gdb) thread apply all bt # extract backtrace from all threads +(gdb) q +@end example + +If the error is repeatable, you can run the binary in a @code{gdb} debugger +or attach the debugger to the running process. The backtrace from a running +process is generally useful when debugging problems like stuck process and similar. +@example +$ knotc -c knot.conf start +$ sudo gdb --pid <KNOT_PID> +(gdb) continue +... +(gdb) thread apply all bt +(gdb) q +@end example + + + +@node Debug messages +@section Debug messages + +@menu +* Enabling debug messages in server:: +@end menu + +In some cases the aforementioned information may not be enough to find +and fix the bug. In these cases it may be useful to turn on debug messages. + +Two steps are required in order to log debug messages. First you need to +allow the debug messages in the server. Then the logging must be configured +to log debug messages (@pxref{log}). It is recommended to log these +messages to a file. Firstly, the debug output may be rather large and +secondly, it is easier to use the data for debugging. + +@node Enabling debug messages in server +@subsection Enabling debug messages in server + +@menu +* Debug messages Example:: +@end menu + +Allowing debug messages in the server is possible only when configuring the +sources. Two @command{configure} options are required to do this: + +@itemize +@item +The @code{--enable-debug} option specifies the server modules for which you +want to enable debug messages. One or more of the following modules may be +listed, separated by commas: + +@itemize +@item @code{server} - Messages related to networking, threads and low-level + journal handling. +@item @code{zones} - All operations with zones - loading, updating, saving, + timers, high-level journal management. +@item @code{xfr} - AXFR, IXFR and NOTIFY handling. +@item @code{packet} - Packet parsing and response creation. +@item @code{dname} - Parsing, comparing and other operations on domain names. +@item @code{rr} - Details of processed resource records. +@item @code{ns} - Query processing, high-level handling of all requests + (transfers, NOTIFY, normal queries). +@item @code{hash} - Details of hash table (the main data structure) operation. +@item @code{compiler} - Zone file compilation. +@end itemize + +@item +The @code{--enable-debuglevel} option is used to specify the verbosity of the +debug output. Be careful with this, as the @code{details} verbosity may produce +really large logs (in order of GBs). There are three levels of verbosity: +@code{brief}, @code{verbose} and @code{details}. + +@end itemize + +@node Debug messages Example +@subsubsection Example + +@example +$ ./configure --enable-debug=server,zones --enable-debuglevel=verbose +@end example + + diff --git a/knot.sample.conf.in b/knot.sample.conf.in index db5c587..db5c587 100644..100755 --- a/knot.sample.conf.in +++ b/knot.sample.conf.in diff --git a/m4/ax_check_compiler_flags.m4 b/m4/ax_check_compiler_flags.m4 index 05e5c3b..05e5c3b 100644..100755 --- a/m4/ax_check_compiler_flags.m4 +++ b/m4/ax_check_compiler_flags.m4 diff --git a/m4/ax_ext.m4 b/m4/ax_ext.m4 index 898f2bf..898f2bf 100644..100755 --- a/m4/ax_ext.m4 +++ b/m4/ax_ext.m4 diff --git a/m4/ax_gcc_x86_cpuid.m4 b/m4/ax_gcc_x86_cpuid.m4 index e9231b8..e9231b8 100644..100755 --- a/m4/ax_gcc_x86_cpuid.m4 +++ b/m4/ax_gcc_x86_cpuid.m4 diff --git a/m4/libtool.m4 b/m4/libtool.m4 index 8ff3c76..8ff3c76 100644..100755 --- a/m4/libtool.m4 +++ b/m4/libtool.m4 diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4 index 17cfd51..17cfd51 100644..100755 --- a/m4/ltoptions.m4 +++ b/m4/ltoptions.m4 diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4 index 9000a05..9000a05 100644..100755 --- a/m4/ltsugar.m4 +++ b/m4/ltsugar.m4 diff --git a/m4/ltversion.m4 b/m4/ltversion.m4 index 9c7b5d4..9c7b5d4 100644..100755 --- a/m4/ltversion.m4 +++ b/m4/ltversion.m4 diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4 index c573da9..c573da9 100644..100755 --- a/m4/lt~obsolete.m4 +++ b/m4/lt~obsolete.m4 diff --git a/samples/Makefile.am b/samples/Makefile.am index f9aa616..f9aa616 100644..100755 --- a/samples/Makefile.am +++ b/samples/Makefile.am diff --git a/samples/Makefile.in b/samples/Makefile.in index 6abb962..6abb962 100644..100755 --- a/samples/Makefile.in +++ b/samples/Makefile.in diff --git a/samples/bogus25.com.zone b/samples/bogus25.com.zone index 5260270..5260270 100644..100755 --- a/samples/bogus25.com.zone +++ b/samples/bogus25.com.zone diff --git a/samples/example.com.zone b/samples/example.com.zone index c6a0aef..c6a0aef 100644..100755 --- a/samples/example.com.zone +++ b/samples/example.com.zone diff --git a/samples/example.com.zone.signed b/samples/example.com.zone.signed index d57b39b..d57b39b 100644..100755 --- a/samples/example.com.zone.signed +++ b/samples/example.com.zone.signed diff --git a/samples/example.com.zone.signed.nsec3 b/samples/example.com.zone.signed.nsec3 index 88a28fc..88a28fc 100644..100755 --- a/samples/example.com.zone.signed.nsec3 +++ b/samples/example.com.zone.signed.nsec3 diff --git a/samples/knot.full.conf b/samples/knot.full.conf index d9d9de7..9aac27b 100644..100755 --- a/samples/knot.full.conf +++ b/samples/knot.full.conf @@ -18,7 +18,7 @@ system { identity "I have no mouth and must scream"; # Version of the server (see RFC 4892). Not used yet. - version "0.1"; + version "1.1.0"; # Server identifier # Use string format "text" @@ -39,7 +39,7 @@ system { workers 1; # User for running server - # May also specify user.group (f.e. knot.users) + # May also specify user.group (e.g. knot.users) user root; } @@ -119,6 +119,11 @@ zones { # Shared options for all listed zones # + # Build differences from zone file changes + # Possible values: on|off + # Default value: off + ixfr-from-differences off; + # Enable semantic checks for all zones (if 'on') # Possible values: on|off # Default value: off @@ -161,6 +166,11 @@ zones { # it is considered relative to the current directory from which the server # was started. file "samples/example.com.zone"; + + # Build differences from zone file changes + # Possible values: on|off + # Default value: off + ixfr-from-differences off; # Disable ANY type queries for authoritative answers (if 'on') # Possible values: on|off @@ -186,7 +196,7 @@ zones { # Possible values: <1..INT_MAX> (seconds) # Default value: inherited from zones.zonefile-sync # It is also possible to suffix with unit size [s/m/h/d] - # f.e. 1s = 1 day, 1m = 1 minute, 1h = 1 hour, 1d = 1 day + # f.e. 1s = 1 second, 1m = 1 minute, 1h = 1 hour, 1d = 1 day zonefile-sync 1h; # XFR master server diff --git a/samples/knot.min.conf b/samples/knot.min.conf index f9bc29c..6b7c1af 100644..100755 --- a/samples/knot.min.conf +++ b/samples/knot.min.conf @@ -4,7 +4,7 @@ # This is a sample of a minimal configuration file for Knot DNS. # # For exhaustive list of all options see samples/knot.full.conf -# in the source directory. +# in the source directory or refer to user manual. # system { diff --git a/samples/knot.sample.conf.in b/samples/knot.sample.conf.in index 7084595..09da2a8 100644..100755 --- a/samples/knot.sample.conf.in +++ b/samples/knot.sample.conf.in @@ -14,5 +14,5 @@ zones { } log { - syslog { any warning, error; } + syslog { any info, notice, warning, error; } } diff --git a/scripts/urcu-tls-compat.patch b/scripts/urcu-tls-compat.patch index cd5ff25..cd5ff25 100644..100755 --- a/scripts/urcu-tls-compat.patch +++ b/scripts/urcu-tls-compat.patch diff --git a/src/Makefile.am b/src/Makefile.am index 60c51c6..eece959 100644..100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,8 +1,8 @@ ACLOCAL_AMFLAGS = -I ../m4 libexec_PROGRAMS = knot-zcompile unittests unittests-zcompile unittests-libknot-realdata unittests-libknot unittests-xfr sbin_PROGRAMS = knotc knotd -MANPAGES = knotc.8 knotd.8 -man8_MANS = knotc.8 knotd.8 +MANPAGES = knotc.8 knotd.8 knot.conf.5 +man8_MANS = knotc.8 knotd.8 knot.conf.5 EXTRA_DIST = $(man8_MANS) # $(YACC) will generate header file @@ -58,8 +58,6 @@ knot_zcompile_SOURCES = \ unittests_SOURCES = \ tests/common/acl_tests.c \ tests/common/acl_tests.h \ - tests/common/da_tests.c \ - tests/common/da_tests.h \ tests/common/events_tests.c \ tests/common/events_tests.h \ tests/common/skiplist_tests.c \ @@ -159,8 +157,6 @@ libknot_la_SOURCES = \ libknot/util/debug.c \ libknot/util/debug.h \ libknot/util/utils.h \ - libknot/util/conv.h \ - libknot/util/conv.c \ libknot/util/descriptor.c \ libknot/util/tolower.h \ libknot/util/tolower.c \ @@ -184,6 +180,8 @@ libknot_la_SOURCES = \ libknot/zone/node.c \ libknot/zone/dname-table.h \ libknot/zone/dname-table.c \ + libknot/zone/zone-diff.h \ + libknot/zone/zone-diff.c \ libknot/hash/hash-functions.c \ libknot/hash/cuckoo-hash-table.c \ libknot/hash/universal-system.c \ @@ -225,17 +223,19 @@ libknots_la_SOURCES = \ common/mempattern.c \ common/lists.c \ common/base32.c \ + common/base64.c \ + common/base64.h \ common/lists.h \ + common/heap.h \ + common/heap.c \ common/base32.h \ common/print.c \ common/print.h \ - common/dynamic-array.c \ common/skip-list.c \ common/base32hex.c \ common/skip-list.h \ common/general-tree.h \ common/general-tree.c \ - common/dynamic-array.h \ common/tree.h \ common/base32hex.h \ common/evqueue.h \ diff --git a/src/Makefile.in b/src/Makefile.in index c00450d..d20810f 100644..100755 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -60,13 +60,13 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libknot_la_LIBADD = -am_libknot_la_OBJECTS = libknot_error.lo utils.lo debug.lo conv.lo \ +am_libknot_la_OBJECTS = libknot_error.lo utils.lo debug.lo \ descriptor.lo tolower.lo query.lo response.lo packet.lo \ zone.lo zone-contents.lo zone-tree.lo zonedb.lo node.lo \ - dname-table.lo hash-functions.lo cuckoo-hash-table.lo \ - universal-system.lo name-server.lo changesets.lo xfr-in.lo \ - ddns.lo edns.lo rrset.lo dname.lo rdata.lo nsec3.lo tsig.lo \ - tsig-op.lo + dname-table.lo zone-diff.lo hash-functions.lo \ + cuckoo-hash-table.lo universal-system.lo name-server.lo \ + changesets.lo xfr-in.lo ddns.lo edns.lo rrset.lo dname.lo \ + rdata.lo nsec3.lo tsig.lo tsig-op.lo libknot_la_OBJECTS = $(am_libknot_la_OBJECTS) libknotd_la_DEPENDENCIES = libknot.la libknots.la @LIBOBJS@ am_libknotd_la_OBJECTS = gatherer.lo stat.lo error.lo \ @@ -78,7 +78,7 @@ am_libknotd_la_OBJECTS = gatherer.lo stat.lo error.lo \ libknotd_la_OBJECTS = $(am_libknotd_la_OBJECTS) libknots_la_DEPENDENCIES = @LIBOBJS@ am_libknots_la_OBJECTS = slab.lo tap.lo mempattern.lo lists.lo \ - base32.lo print.lo dynamic-array.lo skip-list.lo base32hex.lo \ + base32.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 dSFMT.lo prng.lo fdset.lo fdset_poll.lo \ fdset_kqueue.lo fdset_epoll.lo log.lo @@ -99,12 +99,11 @@ knotc_DEPENDENCIES = libknotd.la libknot.la libknots.la @LIBOBJS@ am_knotd_OBJECTS = main.$(OBJEXT) knotd_OBJECTS = $(am_knotd_OBJECTS) knotd_DEPENDENCIES = libknotd.la libknot.la libknots.la @LIBOBJS@ -am_unittests_OBJECTS = acl_tests.$(OBJEXT) da_tests.$(OBJEXT) \ - events_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) +am_unittests_OBJECTS = acl_tests.$(OBJEXT) events_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) nodist_unittests_OBJECTS = unittests_OBJECTS = $(am_unittests_OBJECTS) \ $(nodist_unittests_OBJECTS) @@ -326,8 +325,8 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I ../m4 -MANPAGES = knotc.8 knotd.8 -man8_MANS = knotc.8 knotd.8 +MANPAGES = knotc.8 knotd.8 knot.conf.5 +man8_MANS = knotc.8 knotd.8 knot.conf.5 EXTRA_DIST = $(man8_MANS) # $(YACC) will generate header file @@ -382,8 +381,6 @@ knot_zcompile_SOURCES = \ unittests_SOURCES = \ tests/common/acl_tests.c \ tests/common/acl_tests.h \ - tests/common/da_tests.c \ - tests/common/da_tests.h \ tests/common/events_tests.c \ tests/common/events_tests.h \ tests/common/skiplist_tests.c \ @@ -482,8 +479,6 @@ libknot_la_SOURCES = \ libknot/util/debug.c \ libknot/util/debug.h \ libknot/util/utils.h \ - libknot/util/conv.h \ - libknot/util/conv.c \ libknot/util/descriptor.c \ libknot/util/tolower.h \ libknot/util/tolower.c \ @@ -507,6 +502,8 @@ libknot_la_SOURCES = \ libknot/zone/node.c \ libknot/zone/dname-table.h \ libknot/zone/dname-table.c \ + libknot/zone/zone-diff.h \ + libknot/zone/zone-diff.c \ libknot/hash/hash-functions.c \ libknot/hash/cuckoo-hash-table.c \ libknot/hash/universal-system.c \ @@ -548,17 +545,19 @@ libknots_la_SOURCES = \ common/mempattern.c \ common/lists.c \ common/base32.c \ + common/base64.c \ + common/base64.h \ common/lists.h \ + common/heap.h \ + common/heap.c \ common/base32.h \ common/print.c \ common/print.h \ - common/dynamic-array.c \ common/skip-list.c \ common/base32hex.c \ common/skip-list.h \ common/general-tree.h \ common/general-tree.c \ - common/dynamic-array.h \ common/tree.h \ common/base32hex.h \ common/evqueue.h \ @@ -844,14 +843,13 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base32.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base32hex.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/changesets.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf_tests.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cuckoo-hash-table.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cuckoo_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dSFMT.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/da_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ddns.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/descriptor.Plo@am__quote@ @@ -862,7 +860,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dname_tests_realdata.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dthreads.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dthreads_tests.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dynamic-array.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edns.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edns_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edns_tests_realdata.Po@am__quote@ @@ -879,6 +876,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gatherer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/general-tree.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash-functions.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/journal.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/journal_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/knotc_main.Po@am__quote@ @@ -949,6 +947,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zcompile_main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zlexer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-contents.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-diff.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-dump-text.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-dump.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-load.Plo@am__quote@ @@ -1005,13 +1004,6 @@ debug.lo: libknot/util/debug.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 debug.lo `test -f 'libknot/util/debug.c' || echo '$(srcdir)/'`libknot/util/debug.c -conv.lo: libknot/util/conv.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT conv.lo -MD -MP -MF $(DEPDIR)/conv.Tpo -c -o conv.lo `test -f 'libknot/util/conv.c' || echo '$(srcdir)/'`libknot/util/conv.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/conv.Tpo $(DEPDIR)/conv.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/util/conv.c' object='conv.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 conv.lo `test -f 'libknot/util/conv.c' || echo '$(srcdir)/'`libknot/util/conv.c - descriptor.lo: libknot/util/descriptor.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT descriptor.lo -MD -MP -MF $(DEPDIR)/descriptor.Tpo -c -o descriptor.lo `test -f 'libknot/util/descriptor.c' || echo '$(srcdir)/'`libknot/util/descriptor.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/descriptor.Tpo $(DEPDIR)/descriptor.Plo @@ -1089,6 +1081,13 @@ dname-table.lo: libknot/zone/dname-table.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 dname-table.lo `test -f 'libknot/zone/dname-table.c' || echo '$(srcdir)/'`libknot/zone/dname-table.c +zone-diff.lo: libknot/zone/zone-diff.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-diff.lo -MD -MP -MF $(DEPDIR)/zone-diff.Tpo -c -o zone-diff.lo `test -f 'libknot/zone/zone-diff.c' || echo '$(srcdir)/'`libknot/zone/zone-diff.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone-diff.Tpo $(DEPDIR)/zone-diff.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/zone/zone-diff.c' object='zone-diff.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 zone-diff.lo `test -f 'libknot/zone/zone-diff.c' || echo '$(srcdir)/'`libknot/zone/zone-diff.c + hash-functions.lo: libknot/hash/hash-functions.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hash-functions.lo -MD -MP -MF $(DEPDIR)/hash-functions.Tpo -c -o hash-functions.lo `test -f 'libknot/hash/hash-functions.c' || echo '$(srcdir)/'`libknot/hash/hash-functions.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/hash-functions.Tpo $(DEPDIR)/hash-functions.Plo @@ -1355,6 +1354,20 @@ base32.lo: common/base32.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 base32.lo `test -f 'common/base32.c' || echo '$(srcdir)/'`common/base32.c +base64.lo: common/base64.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT base64.lo -MD -MP -MF $(DEPDIR)/base64.Tpo -c -o base64.lo `test -f 'common/base64.c' || echo '$(srcdir)/'`common/base64.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/base64.Tpo $(DEPDIR)/base64.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/base64.c' object='base64.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 base64.lo `test -f 'common/base64.c' || echo '$(srcdir)/'`common/base64.c + +heap.lo: common/heap.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT heap.lo -MD -MP -MF $(DEPDIR)/heap.Tpo -c -o heap.lo `test -f 'common/heap.c' || echo '$(srcdir)/'`common/heap.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/heap.Tpo $(DEPDIR)/heap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/heap.c' object='heap.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 heap.lo `test -f 'common/heap.c' || echo '$(srcdir)/'`common/heap.c + print.lo: common/print.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT print.lo -MD -MP -MF $(DEPDIR)/print.Tpo -c -o print.lo `test -f 'common/print.c' || echo '$(srcdir)/'`common/print.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/print.Tpo $(DEPDIR)/print.Plo @@ -1362,13 +1375,6 @@ print.lo: common/print.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 print.lo `test -f 'common/print.c' || echo '$(srcdir)/'`common/print.c -dynamic-array.lo: common/dynamic-array.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dynamic-array.lo -MD -MP -MF $(DEPDIR)/dynamic-array.Tpo -c -o dynamic-array.lo `test -f 'common/dynamic-array.c' || echo '$(srcdir)/'`common/dynamic-array.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dynamic-array.Tpo $(DEPDIR)/dynamic-array.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/dynamic-array.c' object='dynamic-array.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 dynamic-array.lo `test -f 'common/dynamic-array.c' || echo '$(srcdir)/'`common/dynamic-array.c - skip-list.lo: common/skip-list.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT skip-list.lo -MD -MP -MF $(DEPDIR)/skip-list.Tpo -c -o skip-list.lo `test -f 'common/skip-list.c' || echo '$(srcdir)/'`common/skip-list.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/skip-list.Tpo $(DEPDIR)/skip-list.Plo @@ -1593,20 +1599,6 @@ acl_tests.obj: tests/common/acl_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 acl_tests.obj `if test -f 'tests/common/acl_tests.c'; then $(CYGPATH_W) 'tests/common/acl_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/acl_tests.c'; fi` -da_tests.o: tests/common/da_tests.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT da_tests.o -MD -MP -MF $(DEPDIR)/da_tests.Tpo -c -o da_tests.o `test -f 'tests/common/da_tests.c' || echo '$(srcdir)/'`tests/common/da_tests.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/da_tests.Tpo $(DEPDIR)/da_tests.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/da_tests.c' object='da_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 da_tests.o `test -f 'tests/common/da_tests.c' || echo '$(srcdir)/'`tests/common/da_tests.c - -da_tests.obj: tests/common/da_tests.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT da_tests.obj -MD -MP -MF $(DEPDIR)/da_tests.Tpo -c -o da_tests.obj `if test -f 'tests/common/da_tests.c'; then $(CYGPATH_W) 'tests/common/da_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/da_tests.c'; fi` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/da_tests.Tpo $(DEPDIR)/da_tests.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/da_tests.c' object='da_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 da_tests.obj `if test -f 'tests/common/da_tests.c'; then $(CYGPATH_W) 'tests/common/da_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/da_tests.c'; fi` - events_tests.o: tests/common/events_tests.c @am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT events_tests.o -MD -MP -MF $(DEPDIR)/events_tests.Tpo -c -o events_tests.o `test -f 'tests/common/events_tests.c' || echo '$(srcdir)/'`tests/common/events_tests.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/events_tests.Tpo $(DEPDIR)/events_tests.Po diff --git a/src/common/LICENSE.txt b/src/common/LICENSE.txt index 15e3bef..15e3bef 100644..100755 --- a/src/common/LICENSE.txt +++ b/src/common/LICENSE.txt diff --git a/src/common/acl.c b/src/common/acl.c index c8e0488..c8e0488 100644..100755 --- a/src/common/acl.c +++ b/src/common/acl.c diff --git a/src/common/acl.h b/src/common/acl.h index 7ce8f26..7ce8f26 100644..100755 --- a/src/common/acl.h +++ b/src/common/acl.h diff --git a/src/common/base32.c b/src/common/base32.c index 43b86c1..43b86c1 100644..100755 --- a/src/common/base32.c +++ b/src/common/base32.c diff --git a/src/common/base32.h b/src/common/base32.h index 45df9fa..45df9fa 100644..100755 --- a/src/common/base32.h +++ b/src/common/base32.h diff --git a/src/common/base32hex.c b/src/common/base32hex.c index cd2d2ce..cd2d2ce 100644..100755 --- a/src/common/base32hex.c +++ b/src/common/base32hex.c diff --git a/src/common/base32hex.h b/src/common/base32hex.h index 9ac4fa8..9ac4fa8 100644..100755 --- a/src/common/base32hex.h +++ b/src/common/base32hex.h diff --git a/src/common/base64.c b/src/common/base64.c new file mode 100755 index 0000000..f1f601c --- /dev/null +++ b/src/common/base64.c @@ -0,0 +1,574 @@ +/* base64.c -- Encode binary data using printable characters. + Copyright (C) 1999-2001, 2004-2006, 2009-2012 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Simon Josefsson. Partially adapted from GNU MailUtils + * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review + * from Paul Eggert, Bruno Haible, and Stepan Kasal. + * + * See also RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>. + * + * Be careful with error checking. Here is how you would typically + * use these functions: + * + * bool ok = base64_decode_alloc (in, inlen, &out, &outlen); + * if (!ok) + * FAIL: input was not valid base64 + * if (out == NULL) + * FAIL: memory allocation error + * OK: data in OUT/OUTLEN + * + * size_t outlen = base64_encode_alloc (in, inlen, &out); + * if (out == NULL && outlen == 0 && inlen != 0) + * FAIL: input too long + * if (out == NULL) + * FAIL: memory allocation error + * OK: data in OUT/OUTLEN. + * + */ + +#include <config.h> + +/* Get prototype. */ +#include "base64.h" + +/* Get malloc. */ +#include <stdlib.h> + +/* Get UCHAR_MAX. */ +#include <limits.h> + +#include <string.h> + +/* C89 compliant way to cast 'char' to 'unsigned char'. */ +static inline unsigned char +to_uchar (char ch) +{ + return ch; +} + +/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN. + If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as + possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero + terminate the output buffer. */ +void +base64_encode (const char *restrict in, size_t inlen, + char *restrict out, size_t outlen) +{ + static const char b64str[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + while (inlen && outlen) + { + *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f]; + if (!--outlen) + break; + *out++ = b64str[((to_uchar (in[0]) << 4) + + (--inlen ? to_uchar (in[1]) >> 4 : 0)) + & 0x3f]; + if (!--outlen) + break; + *out++ = + (inlen + ? b64str[((to_uchar (in[1]) << 2) + + (--inlen ? to_uchar (in[2]) >> 6 : 0)) + & 0x3f] + : '='); + if (!--outlen) + break; + *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '='; + if (!--outlen) + break; + if (inlen) + inlen--; + if (inlen) + in += 3; + } + + if (outlen) + *out = '\0'; +} + +/* Allocate a buffer and store zero terminated base64 encoded data + from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e., + the length of the encoded data, excluding the terminating zero. On + return, the OUT variable will hold a pointer to newly allocated + memory that must be deallocated by the caller. If output string + length would overflow, 0 is returned and OUT is set to NULL. If + memory allocation failed, OUT is set to NULL, and the return value + indicates length of the requested memory block, i.e., + BASE64_LENGTH(inlen) + 1. */ +size_t +base64_encode_alloc (const char *in, size_t inlen, char **out) +{ + size_t outlen = 1 + BASE64_LENGTH (inlen); + + /* Check for overflow in outlen computation. + * + * If there is no overflow, outlen >= inlen. + * + * If the operation (inlen + 2) overflows then it yields at most +1, so + * outlen is 0. + * + * If the multiplication overflows, we lose at least half of the + * correct value, so the result is < ((inlen + 2) / 3) * 2, which is + * less than (inlen + 2) * 0.66667, which is less than inlen as soon as + * (inlen > 4). + */ + if (inlen > outlen) + { + *out = NULL; + return 0; + } + + *out = malloc (outlen); + if (!*out) + return outlen; + + base64_encode (in, inlen, *out, outlen); + + return outlen - 1; +} + +/* With this approach this file works independent of the charset used + (think EBCDIC). However, it does assume that the characters in the + Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255. POSIX + 1003.1-2001 require that char and unsigned char are 8-bit + quantities, though, taking care of that problem. But this may be a + potential problem on non-POSIX C99 platforms. + + IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_" + as the formal parameter rather than "x". */ +#define B64(_) \ + ((_) == 'A' ? 0 \ + : (_) == 'B' ? 1 \ + : (_) == 'C' ? 2 \ + : (_) == 'D' ? 3 \ + : (_) == 'E' ? 4 \ + : (_) == 'F' ? 5 \ + : (_) == 'G' ? 6 \ + : (_) == 'H' ? 7 \ + : (_) == 'I' ? 8 \ + : (_) == 'J' ? 9 \ + : (_) == 'K' ? 10 \ + : (_) == 'L' ? 11 \ + : (_) == 'M' ? 12 \ + : (_) == 'N' ? 13 \ + : (_) == 'O' ? 14 \ + : (_) == 'P' ? 15 \ + : (_) == 'Q' ? 16 \ + : (_) == 'R' ? 17 \ + : (_) == 'S' ? 18 \ + : (_) == 'T' ? 19 \ + : (_) == 'U' ? 20 \ + : (_) == 'V' ? 21 \ + : (_) == 'W' ? 22 \ + : (_) == 'X' ? 23 \ + : (_) == 'Y' ? 24 \ + : (_) == 'Z' ? 25 \ + : (_) == 'a' ? 26 \ + : (_) == 'b' ? 27 \ + : (_) == 'c' ? 28 \ + : (_) == 'd' ? 29 \ + : (_) == 'e' ? 30 \ + : (_) == 'f' ? 31 \ + : (_) == 'g' ? 32 \ + : (_) == 'h' ? 33 \ + : (_) == 'i' ? 34 \ + : (_) == 'j' ? 35 \ + : (_) == 'k' ? 36 \ + : (_) == 'l' ? 37 \ + : (_) == 'm' ? 38 \ + : (_) == 'n' ? 39 \ + : (_) == 'o' ? 40 \ + : (_) == 'p' ? 41 \ + : (_) == 'q' ? 42 \ + : (_) == 'r' ? 43 \ + : (_) == 's' ? 44 \ + : (_) == 't' ? 45 \ + : (_) == 'u' ? 46 \ + : (_) == 'v' ? 47 \ + : (_) == 'w' ? 48 \ + : (_) == 'x' ? 49 \ + : (_) == 'y' ? 50 \ + : (_) == 'z' ? 51 \ + : (_) == '0' ? 52 \ + : (_) == '1' ? 53 \ + : (_) == '2' ? 54 \ + : (_) == '3' ? 55 \ + : (_) == '4' ? 56 \ + : (_) == '5' ? 57 \ + : (_) == '6' ? 58 \ + : (_) == '7' ? 59 \ + : (_) == '8' ? 60 \ + : (_) == '9' ? 61 \ + : (_) == '+' ? 62 \ + : (_) == '/' ? 63 \ + : -1) + +static const signed char b64[0x100] = { + B64 (0), B64 (1), B64 (2), B64 (3), + B64 (4), B64 (5), B64 (6), B64 (7), + B64 (8), B64 (9), B64 (10), B64 (11), + B64 (12), B64 (13), B64 (14), B64 (15), + B64 (16), B64 (17), B64 (18), B64 (19), + B64 (20), B64 (21), B64 (22), B64 (23), + B64 (24), B64 (25), B64 (26), B64 (27), + B64 (28), B64 (29), B64 (30), B64 (31), + B64 (32), B64 (33), B64 (34), B64 (35), + B64 (36), B64 (37), B64 (38), B64 (39), + B64 (40), B64 (41), B64 (42), B64 (43), + B64 (44), B64 (45), B64 (46), B64 (47), + B64 (48), B64 (49), B64 (50), B64 (51), + B64 (52), B64 (53), B64 (54), B64 (55), + B64 (56), B64 (57), B64 (58), B64 (59), + B64 (60), B64 (61), B64 (62), B64 (63), + B64 (64), B64 (65), B64 (66), B64 (67), + B64 (68), B64 (69), B64 (70), B64 (71), + B64 (72), B64 (73), B64 (74), B64 (75), + B64 (76), B64 (77), B64 (78), B64 (79), + B64 (80), B64 (81), B64 (82), B64 (83), + B64 (84), B64 (85), B64 (86), B64 (87), + B64 (88), B64 (89), B64 (90), B64 (91), + B64 (92), B64 (93), B64 (94), B64 (95), + B64 (96), B64 (97), B64 (98), B64 (99), + B64 (100), B64 (101), B64 (102), B64 (103), + B64 (104), B64 (105), B64 (106), B64 (107), + B64 (108), B64 (109), B64 (110), B64 (111), + B64 (112), B64 (113), B64 (114), B64 (115), + B64 (116), B64 (117), B64 (118), B64 (119), + B64 (120), B64 (121), B64 (122), B64 (123), + B64 (124), B64 (125), B64 (126), B64 (127), + B64 (128), B64 (129), B64 (130), B64 (131), + B64 (132), B64 (133), B64 (134), B64 (135), + B64 (136), B64 (137), B64 (138), B64 (139), + B64 (140), B64 (141), B64 (142), B64 (143), + B64 (144), B64 (145), B64 (146), B64 (147), + B64 (148), B64 (149), B64 (150), B64 (151), + B64 (152), B64 (153), B64 (154), B64 (155), + B64 (156), B64 (157), B64 (158), B64 (159), + B64 (160), B64 (161), B64 (162), B64 (163), + B64 (164), B64 (165), B64 (166), B64 (167), + B64 (168), B64 (169), B64 (170), B64 (171), + B64 (172), B64 (173), B64 (174), B64 (175), + B64 (176), B64 (177), B64 (178), B64 (179), + B64 (180), B64 (181), B64 (182), B64 (183), + B64 (184), B64 (185), B64 (186), B64 (187), + B64 (188), B64 (189), B64 (190), B64 (191), + B64 (192), B64 (193), B64 (194), B64 (195), + B64 (196), B64 (197), B64 (198), B64 (199), + B64 (200), B64 (201), B64 (202), B64 (203), + B64 (204), B64 (205), B64 (206), B64 (207), + B64 (208), B64 (209), B64 (210), B64 (211), + B64 (212), B64 (213), B64 (214), B64 (215), + B64 (216), B64 (217), B64 (218), B64 (219), + B64 (220), B64 (221), B64 (222), B64 (223), + B64 (224), B64 (225), B64 (226), B64 (227), + B64 (228), B64 (229), B64 (230), B64 (231), + B64 (232), B64 (233), B64 (234), B64 (235), + B64 (236), B64 (237), B64 (238), B64 (239), + B64 (240), B64 (241), B64 (242), B64 (243), + B64 (244), B64 (245), B64 (246), B64 (247), + B64 (248), B64 (249), B64 (250), B64 (251), + B64 (252), B64 (253), B64 (254), B64 (255) +}; + +#if UCHAR_MAX == 255 +# define uchar_in_range(c) true +#else +# define uchar_in_range(c) ((c) <= 255) +#endif + +/* Return true if CH is a character from the Base64 alphabet, and + false otherwise. Note that '=' is padding and not considered to be + part of the alphabet. */ +bool +isbase64 (char ch) +{ + return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)]; +} + +/* Initialize decode-context buffer, CTX. */ +void +base64_decode_ctx_init (struct base64_decode_context *ctx) +{ + ctx->i = 0; +} + +/* If CTX->i is 0 or 4, there are four or more bytes in [*IN..IN_END), and + none of those four is a newline, then return *IN. Otherwise, copy up to + 4 - CTX->i non-newline bytes from that range into CTX->buf, starting at + index CTX->i and setting CTX->i to reflect the number of bytes copied, + and return CTX->buf. In either case, advance *IN to point to the byte + after the last one processed, and set *N_NON_NEWLINE to the number of + verified non-newline bytes accessible through the returned pointer. */ +static inline char * +get_4 (struct base64_decode_context *ctx, + char const *restrict *in, char const *restrict in_end, + size_t *n_non_newline) +{ + if (ctx->i == 4) + ctx->i = 0; + + if (ctx->i == 0) + { + char const *t = *in; + if (4 <= in_end - *in && memchr (t, '\n', 4) == NULL) + { + /* This is the common case: no newline. */ + *in += 4; + *n_non_newline = 4; + return (char *) t; + } + } + + { + /* Copy non-newline bytes into BUF. */ + char const *p = *in; + while (p < in_end) + { + char c = *p++; + if (c != '\n') + { + ctx->buf[ctx->i++] = c; + if (ctx->i == 4) + break; + } + } + + *in = p; + *n_non_newline = ctx->i; + return ctx->buf; + } +} + +#define return_false \ + do \ + { \ + *outp = out; \ + return false; \ + } \ + while (false) + +/* Decode up to four bytes of base64-encoded data, IN, of length INLEN + into the output buffer, *OUT, of size *OUTLEN bytes. Return true if + decoding is successful, false otherwise. If *OUTLEN is too small, + as many bytes as possible are written to *OUT. On return, advance + *OUT to point to the byte after the last one written, and decrement + *OUTLEN to reflect the number of bytes remaining in *OUT. */ +static inline bool +decode_4 (char const *restrict in, size_t inlen, + char *restrict *outp, size_t *outleft) +{ + char *out = *outp; + if (inlen < 2) + return false; + + if (!isbase64 (in[0]) || !isbase64 (in[1])) + return false; + + if (*outleft) + { + *out++ = ((b64[to_uchar (in[0])] << 2) + | (b64[to_uchar (in[1])] >> 4)); + --*outleft; + } + + if (inlen == 2) + return_false; + + if (in[2] == '=') + { + if (inlen != 4) + return_false; + + if (in[3] != '=') + return_false; + } + else + { + if (!isbase64 (in[2])) + return_false; + + if (*outleft) + { + *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0) + | (b64[to_uchar (in[2])] >> 2)); + --*outleft; + } + + if (inlen == 3) + return_false; + + if (in[3] == '=') + { + if (inlen != 4) + return_false; + } + else + { + if (!isbase64 (in[3])) + return_false; + + if (*outleft) + { + *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0) + | b64[to_uchar (in[3])]); + --*outleft; + } + } + } + + *outp = out; + return true; +} + +/* Decode base64-encoded input array IN of length INLEN to output array + OUT that can hold *OUTLEN bytes. The input data may be interspersed + with newlines. Return true if decoding was successful, i.e. if the + input was valid base64 data, false otherwise. If *OUTLEN is too + small, as many bytes as possible will be written to OUT. On return, + *OUTLEN holds the length of decoded bytes in OUT. Note that as soon + as any non-alphabet, non-newline character is encountered, decoding + is stopped and false is returned. If INLEN is zero, then process + only whatever data is stored in CTX. + + Initially, CTX must have been initialized via base64_decode_ctx_init. + Subsequent calls to this function must reuse whatever state is recorded + in that buffer. It is necessary for when a quadruple of base64 input + bytes spans two input buffers. + + If CTX is NULL then newlines are treated as garbage and the input + buffer is processed as a unit. */ + +bool +base64_decode_ctx (struct base64_decode_context *ctx, + const char *restrict in, size_t inlen, + char *restrict out, size_t *outlen) +{ + size_t outleft = *outlen; + bool ignore_newlines = ctx != NULL; + bool flush_ctx = false; + unsigned int ctx_i = 0; + + if (ignore_newlines) + { + ctx_i = ctx->i; + flush_ctx = inlen == 0; + } + + + while (true) + { + size_t outleft_save = outleft; + if (ctx_i == 0 && !flush_ctx) + { + while (true) + { + /* Save a copy of outleft, in case we need to re-parse this + block of four bytes. */ + outleft_save = outleft; + if (!decode_4 (in, inlen, &out, &outleft)) + break; + + in += 4; + inlen -= 4; + } + } + + if (inlen == 0 && !flush_ctx) + break; + + /* Handle the common case of 72-byte wrapped lines. + This also handles any other multiple-of-4-byte wrapping. */ + if (inlen && *in == '\n' && ignore_newlines) + { + ++in; + --inlen; + continue; + } + + /* Restore OUT and OUTLEFT. */ + out -= outleft_save - outleft; + outleft = outleft_save; + + { + char const *in_end = in + inlen; + char const *non_nl; + + if (ignore_newlines) + non_nl = get_4 (ctx, &in, in_end, &inlen); + else + non_nl = in; /* Might have nl in this case. */ + + /* If the input is empty or consists solely of newlines (0 non-newlines), + then we're done. Likewise if there are fewer than 4 bytes when not + flushing context and not treating newlines as garbage. */ + if (inlen == 0 || (inlen < 4 && !flush_ctx && ignore_newlines)) + { + inlen = 0; + break; + } + if (!decode_4 (non_nl, inlen, &out, &outleft)) + break; + + inlen = in_end - in; + } + } + + *outlen -= outleft; + + return inlen == 0; +} + +/* Allocate an output buffer in *OUT, and decode the base64 encoded + data stored in IN of size INLEN to the *OUT buffer. On return, the + size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL, + if the caller is not interested in the decoded length. *OUT may be + NULL to indicate an out of memory error, in which case *OUTLEN + contains the size of the memory block needed. The function returns + true on successful decoding and memory allocation errors. (Use the + *OUT and *OUTLEN parameters to differentiate between successful + decoding and memory error.) The function returns false if the + input was invalid, in which case *OUT is NULL and *OUTLEN is + undefined. */ +bool +base64_decode_alloc_ctx (struct base64_decode_context *ctx, + const char *in, size_t inlen, char **out, + size_t *outlen) +{ + /* This may allocate a few bytes too many, depending on input, + but it's not worth the extra CPU time to compute the exact size. + The exact size is 3 * (inlen + (ctx ? ctx->i : 0)) / 4, minus 1 if the + input ends with "=" and minus another 1 if the input ends with "==". + Dividing before multiplying avoids the possibility of overflow. */ + size_t needlen = 3 * (inlen / 4) + 3; + + *out = malloc (needlen); + if (!*out) + return true; + + if (!base64_decode_ctx (ctx, in, inlen, *out, &needlen)) + { + free (*out); + *out = NULL; + return false; + } + + if (outlen) + *outlen = needlen; + + return true; +} diff --git a/src/common/base64.h b/src/common/base64.h new file mode 100755 index 0000000..aa0b696 --- /dev/null +++ b/src/common/base64.h @@ -0,0 +1,60 @@ +/* base64.h -- Encode binary data using printable characters. + Copyright (C) 2004-2006, 2009-2012 Free Software Foundation, Inc. + Written by Simon Josefsson. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef BASE64_H +# define BASE64_H + +/* Get size_t. */ +# include <stddef.h> + +/* Get bool. */ +# include <stdbool.h> + +/* This uses that the expression (n+(k-1))/k means the smallest + integer >= n/k, i.e., the ceiling of n/k. */ +# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4) + +struct base64_decode_context +{ + unsigned int i; + char buf[4]; +}; + +extern bool isbase64 (char ch); + +extern void base64_encode (const char *restrict in, size_t inlen, + char *restrict out, size_t outlen); + +extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out); + +extern void base64_decode_ctx_init (struct base64_decode_context *ctx); + +extern bool base64_decode_ctx (struct base64_decode_context *ctx, + const char *restrict in, size_t inlen, + char *restrict out, size_t *outlen); + +extern bool base64_decode_alloc_ctx (struct base64_decode_context *ctx, + const char *in, size_t inlen, + char **out, size_t *outlen); + +#define base64_decode(in, inlen, out, outlen) \ + base64_decode_ctx (NULL, in, inlen, out, outlen) + +#define base64_decode_alloc(in, inlen, out, outlen) \ + base64_decode_alloc_ctx (NULL, in, inlen, out, outlen) + +#endif /* BASE64_H */ diff --git a/src/common/crc.h b/src/common/crc.h index cf2e123..7d748be 100644..100755 --- a/src/common/crc.h +++ b/src/common/crc.h @@ -77,3 +77,5 @@ static inline crc_t crc_finalize(crc_t crc) #endif /* __CRC_H__ */ + +/*! @} */ diff --git a/src/common/dSFMT-params.h b/src/common/dSFMT-params.h index c779d8a..c779d8a 100644..100755 --- a/src/common/dSFMT-params.h +++ b/src/common/dSFMT-params.h diff --git a/src/common/dSFMT-params521.h b/src/common/dSFMT-params521.h index f771dc1..f771dc1 100644..100755 --- a/src/common/dSFMT-params521.h +++ b/src/common/dSFMT-params521.h diff --git a/src/common/dSFMT.c b/src/common/dSFMT.c index 090bb80..090bb80 100644..100755 --- a/src/common/dSFMT.c +++ b/src/common/dSFMT.c diff --git a/src/common/dSFMT.h b/src/common/dSFMT.h index f1aa9bd..f1aa9bd 100644..100755 --- a/src/common/dSFMT.h +++ b/src/common/dSFMT.h diff --git a/src/common/dynamic-array.c b/src/common/dynamic-array.c deleted file mode 100644 index 24f2aaa..0000000 --- a/src/common/dynamic-array.c +++ /dev/null @@ -1,225 +0,0 @@ -/* 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 <config.h> -#include <pthread.h> -#include <assert.h> -#include <stdio.h> - -#include <urcu.h> - -//#include "common.h" -#include "common/dynamic-array.h" - -#ifndef ERR_ALLOC_FAILED -#define ERR_ALLOC_FAILED fprintf(stderr, "Allocation failed at %s:%d\n", \ - __FILE__, __LINE__) -#endif - -//#define DA_DEBUG - -#ifndef dbg_da -#ifdef DA_DEBUG -#define dbg_da(msg...) fprintf(stderr, msg) -#else -#define dbg_da(msg...) -#endif -#endif - -/*----------------------------------------------------------------------------*/ -/* Private functions */ -/*----------------------------------------------------------------------------*/ - -enum da_resize_type { - DA_LARGER, DA_SMALLER -}; - -typedef enum da_resize_type da_resize_type_t; - -/*----------------------------------------------------------------------------*/ -/*! - * \retval 1 On success. - * \retval -1 On failure. - */ -static int da_resize(da_array_t *array, da_resize_type_t type) -{ - dbg_da("da_resize(): array pointer: %p, items pointer: %p\n", array, - array->items); - - unsigned new_size = ((type == DA_LARGER) - ? (array->allocated *= 2) - : (array->allocated /= 2)); - - void *new_items = malloc(new_size * array->item_size); - if (new_items == NULL) { - ERR_ALLOC_FAILED; - return -1; - } - - dbg_da("Place for new items: %p\n", new_items); - - // copy the contents from the old array to the new - memcpy(new_items, array->items, array->count * array->item_size); - - // do RCU update - void *old_items = rcu_xchg_pointer(&array->items, new_items); - array->allocated = new_size; - - dbg_da("Old items pointer: %p\n", old_items); - - // wait for readers to finish - //synchronize_rcu(); - // deallocate the old array - dbg_da("RCU synchronized, deallocating old items array at address %p." - "\n", old_items); - free(old_items); - - return 1; -} - -/*----------------------------------------------------------------------------*/ -/* Public functions */ -/*----------------------------------------------------------------------------*/ - -da_array_t *da_create(unsigned count, size_t item_size) -{ - da_array_t *a = (da_array_t *)malloc(sizeof(da_array_t)); - if (a == NULL) { - ERR_ALLOC_FAILED; - return NULL; - } - da_initialize(a, count, item_size); - return a; -} - -/*----------------------------------------------------------------------------*/ - -int da_initialize(da_array_t *array, unsigned count, size_t item_size) -{ - assert(array != NULL); - pthread_mutex_init(&array->mtx, NULL); - pthread_mutex_lock(&array->mtx); - - array->items = malloc(count * item_size); - if (array->items == NULL) { - array->allocated = 0; - array->count = 0; - ERR_ALLOC_FAILED; - pthread_mutex_unlock(&array->mtx); - return -1; - } - - array->allocated = count; - array->count = 0; - array->item_size = item_size; - memset(array->items, 0, count * item_size); - - pthread_mutex_unlock(&array->mtx); - return 0; -} - -/*----------------------------------------------------------------------------*/ - -int da_reserve(da_array_t *array, unsigned count) -{ - pthread_mutex_lock(&array->mtx); - unsigned res = 0; - - assert(array->allocated >= array->count); - if ((array->allocated - array->count) >= count) { - dbg_da("Enough place in the array, no resize needed.\n"); - res = 0; - } else { - dbg_da("Resizing array.\n"); - res = da_resize(array, DA_LARGER); - } - pthread_mutex_unlock(&array->mtx); - - return res; -} - -/*----------------------------------------------------------------------------*/ - -int da_occupy(da_array_t *array, unsigned count) -{ - pthread_mutex_lock(&array->mtx); - unsigned res = 0; - assert(array->allocated >= array->count); - - if ((array->allocated - array->count) < count) { - dbg_da("Not enough place to occupy.\n"); - res = -1; - } else { - array->count += count; - } - - pthread_mutex_unlock(&array->mtx); - return res; -} - -/*----------------------------------------------------------------------------*/ - -unsigned da_try_reserve(const da_array_t *array, unsigned count) -{ - assert(array->allocated >= array->count); - if ((array->allocated - array->count) >= count) { - return 0; - } - - return 1; -} - -/*----------------------------------------------------------------------------*/ - -void da_release(da_array_t *array, unsigned count) -{ - pthread_mutex_lock(&array->mtx); - - assert(array->allocated >= array->count); - assert(array->count >= count); - dbg_da("Decreasing count of items in array.\n"); - array->count -= count; - - pthread_mutex_unlock(&array->mtx); -} - -/*----------------------------------------------------------------------------*/ - -void da_destroy(da_array_t *array) -{ - pthread_mutex_lock(&array->mtx); - void *old_items = rcu_dereference(array->items); - rcu_set_pointer(&array->items, NULL); - pthread_mutex_unlock(&array->mtx); - - //synchronize_rcu(); - free(old_items); - pthread_mutex_destroy(&array->mtx); -} - -/*----------------------------------------------------------------------------*/ - -void *da_get_items(const da_array_t *array) -{ - return array->items; -} - -/*----------------------------------------------------------------------------*/ - -unsigned da_get_count(const da_array_t *array) -{ - return array->count; -} diff --git a/src/common/dynamic-array.h b/src/common/dynamic-array.h deleted file mode 100644 index 77a5d13..0000000 --- a/src/common/dynamic-array.h +++ /dev/null @@ -1,156 +0,0 @@ -/* 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 dynamic-array.h - * - * \author Lubos Slovak <lubos.slovak@nic.cz> - * - * \brief Safe dynamic array implementation. - * - * \todo Somehow check if the array is initialized and do not use otherwise. - * Maybe some magic, or so. - * \todo This structure is too slow because of the mutex. - * - * \addtogroup common_lib - * @{ - */ - -#ifndef _KNOTD_COMMON_DYNAMIC_ARRAY_H_ -#define _KNOTD_COMMON_DYNAMIC_ARRAY_H_ - -#include <string.h> -#include <pthread.h> - -/*----------------------------------------------------------------------------*/ -/*! - * \brief Dynamic array structure. - * - * Before using the dynamic array, it must be initialized using da_initialize(). - * When getting individual items always use da_get_items() to obtain pointer to - * the actual array. - * - * Items in the array cannot be dereferenced (it uses void * for storing the - * the items). It is needed to type-cast the item array (obtained by calling - * da_get_items()) to a proper type before dereferencing. - * - * When adding items, first reserve enough space for them by callling - * da_reserve() and subsequently tell the array about the inserted items by - * calling da_occupy(). When removing, the array must be told about the fact - * by calling da_release(). - * - * For getting the actual number of items in array use da_get_count(). - * - * When the array is no more needed, the da_destroy() function must be called - * before deallocating the structure. - */ -struct da_array { - /*! \brief The actual array. The items can't be dereferenced directly.*/ - void *items; - - /*! - * \brief Size of the stored items in bytes (used in counting of space - * needed. - */ - size_t item_size; - - /*! - * \brief Size of allocated space in number of items that can be stored. - */ - unsigned allocated; - - /*! \brief Number of items actually stored in the array. */ - unsigned count; - - /*! \brief Mutex. */ - pthread_mutex_t mtx; -}; - -typedef struct da_array da_array_t; - -/*----------------------------------------------------------------------------*/ -/*! - * \brief Creates and initializes the dynamic array. - * - * Initialization comprises of allocating place for \a count items of size - * \a item_size and setting the items to zeros. - * - * \retval 0 if successful. - * \retval -1 if not successful. - */ -da_array_t *da_create(unsigned count, size_t item_size); - -/*! - * \brief Initializes the dynamic array. - * - * Initialization comprises of allocating place for \a count items of size - * \a item_size and setting the items to zeros. - * - * \retval 0 if successful. - * \retval -1 if not successful. - */ -int da_initialize(da_array_t *array, unsigned count, size_t item_size); - -/*! - * \brief Reserves space for \a count more items. - * - * \retval 0 if successful and resizing was not necessary. - * \retval 1 if successful and the array was enlarged. - * \retval -1 if not successful - resizing was needed but could not be done. - */ -int da_reserve(da_array_t *array, unsigned count); - -/*! - * \brief Increases the number of items in array by \a count. - * - * \retval 0 If successful. - * \retval -1 If not successful (not enough allocated space, i.e. must run - * da_reserve()). - */ -int da_occupy(da_array_t *array, unsigned count); - -/*! - * \brief Tries to reserve space for \a count more items. - * - * \retval 0 if successful and resizing is not necessary. - * \retval 1 if successful but the array will need to be resized. - */ -unsigned da_try_reserve(const da_array_t *array, unsigned count); - -/*! - * \brief Releases space taken by \a count items. - */ -void da_release(da_array_t *array, unsigned count); - -/*! - * \brief Poperly deallocates the array. - */ -void da_destroy(da_array_t *array); - -/*! - * \brief Returns the array of items as a void *. - */ -void *da_get_items(const da_array_t *array); - -/*! - * \brief Returns count of items in the array. - */ -unsigned da_get_count(const da_array_t *array); - -/*----------------------------------------------------------------------------*/ - -#endif /* _KNOTD_COMMON_DYNAMIC_ARRAY_H_ */ - -/*! @} */ diff --git a/src/common/errors.c b/src/common/errors.c index 3b770e9..3b770e9 100644..100755 --- a/src/common/errors.c +++ b/src/common/errors.c diff --git a/src/common/errors.h b/src/common/errors.h index a2773ac..a2773ac 100644..100755 --- a/src/common/errors.h +++ b/src/common/errors.h diff --git a/src/common/evqueue.c b/src/common/evqueue.c index 7614111..240ced6 100644..100755 --- a/src/common/evqueue.c +++ b/src/common/evqueue.c @@ -126,8 +126,11 @@ void evqueue_free(evqueue_t **q) *q = 0; /* Deinitialize. */ - close(eq->fds[EVQUEUE_READFD]); - close(eq->fds[EVQUEUE_WRITEFD]); + for (int i = 0; i < 2; ++i) { + if (eq->fds[i] > -1) { + close(eq->fds[i]); + } + } free(eq); } diff --git a/src/common/evqueue.h b/src/common/evqueue.h index ffb3860..ffb3860 100644..100755 --- a/src/common/evqueue.h +++ b/src/common/evqueue.h diff --git a/src/common/evsched.c b/src/common/evsched.c index cc2cf7c..8b6f721 100644..100755 --- a/src/common/evsched.c +++ b/src/common/evsched.c @@ -26,6 +26,15 @@ #define OPENBSD_SLAB_BROKEN #endif +/* Heap only cares about x<y. */ +static int compare_event_heap_nodes(event_t **e1, event_t **e2) +{ + if (timercmp(&(*e1)->tv, &(*e2)->tv, <)) return -1; + if (timercmp(&(*e1)->tv, &(*e2)->tv, >)) return 1; + return 0; +} + + /*! * \brief Set event timer to T (now) + dt miliseconds. */ @@ -69,7 +78,7 @@ evsched_t *evsched_new() #ifndef OPENBSD_SLAB_BROKEN slab_cache_init(&s->cache.alloc, sizeof(event_t)); #endif - init_list(&s->calendar); + heap_init(&s->heap, sizeof(event_t *), compare_event_heap_nodes, 0, NULL); return s; } @@ -86,10 +95,16 @@ void evsched_delete(evsched_t **s) pthread_mutex_destroy(&(*s)->rl); pthread_mutex_destroy(&(*s)->mx); pthread_cond_destroy(&(*s)->notify); - node *n = 0, *nxt = 0; - WALK_LIST_DELSAFE(n, nxt, (*s)->calendar) { - evsched_event_free((*s), (event_t*)n); + + while (! EMPTY_HEAP(&(*s)->heap)) /* FIXME: Would be faster to simply walk through the array */ + { + event_t *e = *((event_t**)(HHEAD(&(*s)->heap))); + heap_delmin(&(*s)->heap); + evsched_event_free((*s), e); } + + free((*s)->heap.data); + (*s)->heap.data = NULL;; #ifndef OPENBSD_SLAB_BROKEN /* Free allocator. */ @@ -154,25 +169,26 @@ event_t* evsched_next(evsched_t *s) while(1) { /* Check event queue. */ - if (!EMPTY_LIST(s->calendar)) { + if (!EMPTY_HEAP(&s->heap)) { /* Get current time. */ struct timeval dt; gettimeofday(&dt, 0); /* Get next event. */ - event_t *next_ev = HEAD(s->calendar); + event_t *next_ev = *((event_t**)HHEAD(&s->heap)); /* Immediately return. */ if (timercmp(&dt, &next_ev->tv, >=)) { s->current = next_ev; - rem_node(&next_ev->n); + heap_delmin(&s->heap); pthread_mutex_unlock(&s->mx); pthread_mutex_lock(&s->rl); return next_ev; } /* Wait for next event or interrupt. Unlock calendar. */ + /* FIXME: Blocks this the possibility to add any event earlier? */ struct timespec ts; ts.tv_sec = next_ev->tv.tv_sec; ts.tv_nsec = next_ev->tv.tv_usec * 1000L; @@ -214,31 +230,12 @@ int evsched_schedule(evsched_t *s, event_t *ev, uint32_t dt) /* Update event timer. */ evsched_settimer(ev, dt); - + ev->parent = s; + /* Lock calendar. */ pthread_mutex_lock(&s->mx); - - /* Schedule event. */ - node *n = 0, *prev = 0; - if (!EMPTY_LIST(s->calendar)) { - WALK_LIST(n, s->calendar) { - event_t* cur = (event_t *)n; - if (timercmp(&cur->tv, &ev->tv, <)) { - prev = n; - } else { - break; - } - } - } - - /* Append to list. */ - ev->parent = s; - if (prev) { - insert_node(&ev->n, prev); - } else { - add_head(&s->calendar, &ev->n); - } - + + heap_insert(&s->heap, &ev); /* Unlock calendar. */ pthread_cond_signal(&s->notify); @@ -293,6 +290,8 @@ event_t* evsched_schedule_term(evsched_t *s, uint32_t dt) int evsched_cancel(evsched_t *s, event_t *ev) { + int found; + if (!s || !ev) { return -1; } @@ -303,19 +302,8 @@ int evsched_cancel(evsched_t *s, event_t *ev) /* Lock calendar. */ pthread_mutex_lock(&s->mx); - /* Find in list. */ - event_t *n = 0; - int found = 0; - WALK_LIST(n, s->calendar) { - if (n == ev) { - found = 1; - break; - } - } - - /* Remove from list. */ - if (found) { - rem_node(&ev->n); + if ((found = heap_find(&s->heap, &ev))) { + heap_delete(&s->heap, found); } /* Unlock calendar. */ diff --git a/src/common/evsched.h b/src/common/evsched.h index 8ca9c62..47bf672 100644..100755 --- a/src/common/evsched.h +++ b/src/common/evsched.h @@ -68,7 +68,7 @@ #include <pthread.h> #include "common/slab/slab.h" -#include "common/lists.h" +#include "common/heap.h" #include "common/evqueue.h" /*! @@ -92,7 +92,7 @@ typedef struct { event_t *current; /*!< Current running event. */ pthread_mutex_t mx; /*!< Event queue locking. */ pthread_cond_t notify; /*!< Event queue notification. */ - list calendar; /*!< Event calendar. */ + struct heap heap; struct { slab_cache_t alloc; /*!< Events SLAB cache. */ pthread_mutex_t lock; /*!< Events cache spin lock. */ diff --git a/src/common/fdset.c b/src/common/fdset.c index c915e01..c915e01 100644..100755 --- a/src/common/fdset.c +++ b/src/common/fdset.c diff --git a/src/common/fdset.h b/src/common/fdset.h index 4fbd9bc..4038083 100644..100755 --- a/src/common/fdset.h +++ b/src/common/fdset.h @@ -74,7 +74,7 @@ typedef struct fdset_it_t { /*! * \brief File descriptor set implementation backend. - * \notice Functions documentation following. + * \note Functions documentation following. * \internal */ struct fdset_backend_t diff --git a/src/common/fdset_epoll.c b/src/common/fdset_epoll.c index d4481b5..d4481b5 100644..100755 --- a/src/common/fdset_epoll.c +++ b/src/common/fdset_epoll.c diff --git a/src/common/fdset_epoll.h b/src/common/fdset_epoll.h index 58f25f8..58f25f8 100644..100755 --- a/src/common/fdset_epoll.h +++ b/src/common/fdset_epoll.h diff --git a/src/common/fdset_kqueue.c b/src/common/fdset_kqueue.c index 108c572..108c572 100644..100755 --- a/src/common/fdset_kqueue.c +++ b/src/common/fdset_kqueue.c diff --git a/src/common/fdset_kqueue.h b/src/common/fdset_kqueue.h index 4b650a7..4b650a7 100644..100755 --- a/src/common/fdset_kqueue.h +++ b/src/common/fdset_kqueue.h diff --git a/src/common/fdset_poll.c b/src/common/fdset_poll.c index 19804f5..9b1c135 100644..100755 --- a/src/common/fdset_poll.c +++ b/src/common/fdset_poll.c @@ -71,9 +71,10 @@ int fdset_poll_add(fdset_t *fdset, int fd, int events) } /* Append. */ - int nid = fdset->nfds++;; + int nid = fdset->nfds++; fdset->fds[nid].fd = fd; fdset->fds[nid].events = POLLIN; + fdset->fds[nid].revents = 0; return 0; } diff --git a/src/common/fdset_poll.h b/src/common/fdset_poll.h index 68e9e69..68e9e69 100644..100755 --- a/src/common/fdset_poll.h +++ b/src/common/fdset_poll.h diff --git a/src/common/general-tree.c b/src/common/general-tree.c index e1048e7..e1048e7 100644..100755 --- a/src/common/general-tree.c +++ b/src/common/general-tree.c diff --git a/src/common/general-tree.h b/src/common/general-tree.h index 552638a..552638a 100644..100755 --- a/src/common/general-tree.h +++ b/src/common/general-tree.h diff --git a/src/common/heap.c b/src/common/heap.c new file mode 100755 index 0000000..6fefb11 --- /dev/null +++ b/src/common/heap.c @@ -0,0 +1,149 @@ +/* + * Universal Heap Macros + * + * (c) 2012 Ondrej Filip <feela@network.cz> + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +/*** + * [[intro]] + * Introduction + * ------------ + * + * Binary heap is a simple data structure, which for example supports efficient insertions, deletions + * and access to the minimal inserted item. We define several macros for such operations. + * Note that because of simplicity of heaps, we have decided to define direct macros instead + * of a <<generic:,macro generator>> as for several other data structures in the Libucw. + * + * A heap is represented by a number of elements and by an array of values. Beware that we + * index this array from one, not from zero as do the standard C arrays. + * + * Most macros use these parameters: + * + * - @type - the type of elements + * - @num - a variable (signed or unsigned integer) with the number of elements + * - @heap - a C array of type @type; the heap is stored in `heap[1] .. heap[num]`; `heap[0]` is unused + * - @less - a callback to compare two element values; `less(x, y)` shall return a non-zero value iff @x is lower than @y + * - @swap - a callback to swap two array elements; `swap(heap, i, j, t)` must swap `heap[i]` with `heap[j]` with possible help of temporary variable @t (type @type). + * + * A valid heap must follow these rules: + * + * - `num >= 0` + * - `heap[i] >= heap[i / 2]` for each `i` in `[2, num]` + * + * The first element `heap[1]` is always lower or equal to all other elements. + * + * [[macros]] + * Macros + * ------ + ***/ + +#include "heap.h" +#include <string.h> +#include <stdlib.h> + +void _def_swap(struct heap *h, void *e1, void *e2) +{ + if (e1 == e2) return; + void *tmp = HTEMPELEMENT(h); + memcpy(tmp, e1, h->elm_size); + memcpy(e1, e2, h->elm_size); + memcpy(e2, tmp, h->elm_size); +} + + +int heap_init(struct heap *h, int elm_size, int (*cmp)(void *, void *), int init_size, void (*swap)(struct heap *, void *, void *)) +{ + int isize = init_size ? init_size : INITIAL_HEAP_SIZE; + + h->num = 0; + h->max_size = isize; + h->cmp = cmp; + h->swap = swap ? swap : _def_swap; + h->data = malloc((isize + 1) * elm_size); + h->elm_size = elm_size; + + return h->data ? 1 : 0; +}; + +static inline void _heap_bubble_down(struct heap *h, int e) +{ + int e1; + for (;;) + { + e1 = 2*e; + if(e1 > h->num) break; + if((h->cmp(HELEMENT(h, e),HELEMENT(h,e1)) < 0) && (e1 == h->num || (h->cmp(HELEMENT(h, e),HELEMENT(h,e1+1)) < 0))) break; + if((e1 != h->num) && (h->cmp(HELEMENT(h, e1+1), HELEMENT(h,e1)) < 0)) e1++; + h->swap(h,HELEMENT(h,e),HELEMENT(h,e1)); + e = e1; + } +} + +static inline void _heap_bubble_up(struct heap *h, int e) +{ + int e1; + while (e > 1) + { + e1 = e/2; + if(h->cmp(HELEMENT(h, e1),HELEMENT(h,e)) < 0) break; + h->swap(h,HELEMENT(h,e),HELEMENT(h,e1)); + e = e1; + } + +} + +void heap_delmin(struct heap *h) +{ + if(h->num == 0) return; + if(h->num > 1) + { + h->swap(h,HHEAD(h),HELEMENT(h,h->num)); + } + --h->num; + _heap_bubble_down(h, 1); +} + +int heap_insert(struct heap *h, void *e) +{ + if(h->num == h->max_size) + { + h->max_size = h->max_size * HEAP_INCREASE_STEP; + h->data = realloc(h->data, (h->max_size + 1) * h->elm_size); + } + + h->num++; + memcpy(HELEMENT(h,h->num),e,h->elm_size); + _heap_bubble_up(h,h->num); + + return h->data ? 1 :0 ; +} + +int heap_find(struct heap *h, void *elm) /* FIXME - very slow */ +{ + int i = h->num; + + while(i > 0) + { + if(h->cmp(HELEMENT(h, i),elm) == 0) break; + --i; + } + return i; +} + +void heap_delete(struct heap *h, int e) +{ + h->swap(h, HELEMENT(h, e), HELEMENT(h, h->num)); + h->num--; + if(h->cmp(HELEMENT(h, e), HELEMENT(h, h->num + 1)) < 0) _heap_bubble_up(h, e); + else _heap_bubble_down(h, e); + + if ((h->num > INITIAL_HEAP_SIZE) && (h->num < h->max_size / HEAP_DECREASE_THRESHOLD)) + { + h->max_size = h->max_size / HEAP_INCREASE_STEP; + h->data = realloc(h->data, (h->max_size + 1) * h->elm_size); + } +} + diff --git a/src/common/heap.h b/src/common/heap.h new file mode 100755 index 0000000..d6f8a0b --- /dev/null +++ b/src/common/heap.h @@ -0,0 +1,57 @@ +/* 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 heap.h + * + * \author Ondrej Filip <ondrej.filip@nic.cz> + * + * \brief Universal heap support + * + * + * \addtogroup common_lib + * @{ + */ + +#ifndef _HEAP_H_ +#define _HEAP_H_ + +struct heap { + int num; /* Number of elements */ + int elm_size; /* Size of a single element */ + int max_size; /* Size of allocated memory */ + int (*cmp)(void *, void *); + void (*swap)(struct heap *, void *, void *); + void *data; +}; /* Array follows */ + +#define INITIAL_HEAP_SIZE 512 /* initial heap size */ +#define HEAP_INCREASE_STEP 2 /* multiplier for each inflation, keep conservative */ +#define HEAP_DECREASE_THRESHOLD 2 /* threshold for deflation, keep conservative */ +#define HTEMPELEMENT(h) ((h)->data) /* Pointer to tmp element (for swap) */ +#define HELEMENT(h,num) ((char*)(h)->data + (num) * (h)->elm_size) +#define HHEAD(h) HELEMENT((h),1) +#define EMPTY_HEAP(h) ((h)->num == 0) /* h->num == 0 */ + +int heap_init(struct heap *, int, int (*cmp)(), int, void (*swap)()); +void heap_delmin(struct heap *); +int heap_insert(struct heap *, void *); +int heap_find(struct heap *, void *); +void heap_delete(struct heap *, int); + + +#endif /* _HEAP_H_ */ + +/*! @} */ diff --git a/src/common/latency.c b/src/common/latency.c index a563f58..a563f58 100644..100755 --- a/src/common/latency.c +++ b/src/common/latency.c diff --git a/src/common/latency.h b/src/common/latency.h index d965c56..d965c56 100644..100755 --- a/src/common/latency.h +++ b/src/common/latency.h diff --git a/src/common/libtap/README b/src/common/libtap/README index d57b81d..d57b81d 100644..100755 --- a/src/common/libtap/README +++ b/src/common/libtap/README diff --git a/src/common/libtap/tap.c b/src/common/libtap/tap.c index d6bb995..d6bb995 100644..100755 --- a/src/common/libtap/tap.c +++ b/src/common/libtap/tap.c diff --git a/src/common/libtap/tap.h b/src/common/libtap/tap.h index 89484f4..89484f4 100644..100755 --- a/src/common/libtap/tap.h +++ b/src/common/libtap/tap.h diff --git a/src/common/libtap/tap_unit.h b/src/common/libtap/tap_unit.h index c248fde..c248fde 100644..100755 --- a/src/common/libtap/tap_unit.h +++ b/src/common/libtap/tap_unit.h diff --git a/src/common/lists.c b/src/common/lists.c index 9a93733..9a93733 100644..100755 --- a/src/common/lists.c +++ b/src/common/lists.c diff --git a/src/common/lists.h b/src/common/lists.h index 972ea49..897b1a9 100644..100755 --- a/src/common/lists.h +++ b/src/common/lists.h @@ -71,6 +71,7 @@ typedef struct list { /* In fact two overlayed nodes */ WALK_LIST_DELSAFE(n,nxt,list) { \ free(n); \ } \ + init_list(&list); \ } while(0) void add_tail(list *, node *); diff --git a/src/common/log.c b/src/common/log.c index c70f739..c70f739 100644..100755 --- a/src/common/log.c +++ b/src/common/log.c diff --git a/src/common/log.h b/src/common/log.h index 305020c..305020c 100644..100755 --- a/src/common/log.h +++ b/src/common/log.h diff --git a/src/common/mempattern.c b/src/common/mempattern.c index 5982e18..5982e18 100644..100755 --- a/src/common/mempattern.c +++ b/src/common/mempattern.c diff --git a/src/common/mempattern.h b/src/common/mempattern.h index ae1fa78..ae1fa78 100644..100755 --- a/src/common/mempattern.h +++ b/src/common/mempattern.h diff --git a/src/common/modified_tree.h b/src/common/modified_tree.h index 4c3e325..9ceddd1 100644..100755 --- a/src/common/modified_tree.h +++ b/src/common/modified_tree.h @@ -123,7 +123,7 @@ else if (cmp > 0) \ self->field.avl_right= MOD_TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare, merge, merged); \ else if (merge) { \ - merge(&(elm->data), &(self->data)); \ + merge(&(self->data), &(elm->data)); \ *merged = 1; } \ else \ self->field.avl_right= MOD_TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare, merge, merged); \ diff --git a/src/common/print.c b/src/common/print.c index 9764568..9764568 100644..100755 --- a/src/common/print.c +++ b/src/common/print.c diff --git a/src/common/print.h b/src/common/print.h index 482f55e..482f55e 100644..100755 --- a/src/common/print.h +++ b/src/common/print.h diff --git a/src/common/prng.c b/src/common/prng.c index 250a506..250a506 100644..100755 --- a/src/common/prng.c +++ b/src/common/prng.c diff --git a/src/common/prng.h b/src/common/prng.h index 9f17a78..a64eabb 100644..100755 --- a/src/common/prng.h +++ b/src/common/prng.h @@ -39,3 +39,5 @@ double tls_rand(); #endif //_KNOTD_ACL_H_ + +/*! @} */ diff --git a/src/common/ref.c b/src/common/ref.c index 3b9c033..3b9c033 100644..100755 --- a/src/common/ref.c +++ b/src/common/ref.c diff --git a/src/common/ref.h b/src/common/ref.h index 13a7037..13a7037 100644..100755 --- a/src/common/ref.h +++ b/src/common/ref.h diff --git a/src/common/skip-list.c b/src/common/skip-list.c index cde08d7..cde08d7 100644..100755 --- a/src/common/skip-list.c +++ b/src/common/skip-list.c diff --git a/src/common/skip-list.h b/src/common/skip-list.h index 784f366..784f366 100644..100755 --- a/src/common/skip-list.h +++ b/src/common/skip-list.h diff --git a/src/common/slab/alloc-common.h b/src/common/slab/alloc-common.h index 32878ab..32878ab 100644..100755 --- a/src/common/slab/alloc-common.h +++ b/src/common/slab/alloc-common.h diff --git a/src/common/slab/slab.c b/src/common/slab/slab.c index df30998..9a3e8de 100644..100755 --- a/src/common/slab/slab.c +++ b/src/common/slab/slab.c @@ -231,7 +231,7 @@ void __attribute__ ((destructor)) slab_deinit() * Cache helper functions. */ -/* \notice Not used right now. +/* \note Not used right now. static void slab_dump(slab_t* slab) { printf("%s: buffers (bufsize=%zuB, %u/%u free): \n", diff --git a/src/common/slab/slab.h b/src/common/slab/slab.h index 4ea7e31..4ea7e31 100644..100755 --- a/src/common/slab/slab.h +++ b/src/common/slab/slab.h diff --git a/src/common/sockaddr.c b/src/common/sockaddr.c index b4e75ee..b4e75ee 100644..100755 --- a/src/common/sockaddr.c +++ b/src/common/sockaddr.c diff --git a/src/common/sockaddr.h b/src/common/sockaddr.h index 52e621c..52e621c 100644..100755 --- a/src/common/sockaddr.h +++ b/src/common/sockaddr.h diff --git a/src/common/tree.h b/src/common/tree.h index efea65b..efea65b 100644..100755 --- a/src/common/tree.h +++ b/src/common/tree.h diff --git a/src/config.h.in b/src/config.h.in index bd88e96..b711d4f 100644..100755 --- a/src/config.h.in +++ b/src/config.h.in @@ -27,6 +27,15 @@ /* Define to 1 if you have the `clock_gettime' function. */ #undef HAVE_CLOCK_GETTIME +/* Define if FreeBSD-like cpuset_t exists. */ +#undef HAVE_CPUSET_BSD + +/* Define if Linux-like cpu_set_t exists. */ +#undef HAVE_CPUSET_LINUX + +/* Define if cpuset_t and cpuset(3) exists. */ +#undef HAVE_CPUSET_NETBSD + /* Define to 1 if you have the <dlfcn.h> header file. */ #undef HAVE_DLFCN_H @@ -105,6 +114,9 @@ /* Define to 1 if you have the `pselect' function. */ #undef HAVE_PSELECT +/* Define to 1 if you have the <pthread_np.h> header file. */ +#undef HAVE_PTHREAD_NP_H + /* Define to 1 if you have the `pthread_setaffinity_np' function. */ #undef HAVE_PTHREAD_SETAFFINITY_NP @@ -237,6 +249,9 @@ /* RR debug. */ #undef KNOT_RR_DEBUG +/* Hash table stash debug. */ +#undef KNOT_STASH_DEBUG + /* XFR debug. */ #undef KNOT_XFR_DEBUG diff --git a/src/knot.conf.5 b/src/knot.conf.5 new file mode 100755 index 0000000..d086860 --- /dev/null +++ b/src/knot.conf.5 @@ -0,0 +1,296 @@ +.TH "knot.conf" "5" "August 2012" "CZ.NIC Labs" "Knot DNS, version 1.1" +.SH "NAME" +.LP +.B knot.conf +\- Configuration file manual for Knot DNS server. +.SH "SYNOPSIS" +.LP +.B knot.conf +.SH "DESCRIPTION" +.B knot.conf +serves as an example of the configuration for knotc(8) and knotd(8). +.SH "EXAMPLE" +.LP +# This is a comment. + +# +# There are 4 main sections of this config file: +# system, zones, interfaces and log +# + +# Section 'system' contains general options for the server +system { + + # Identity of the server (see RFC 4892). Not used yet. + identity "I have no mouth and must scream"; + + # Version of the server (see RFC 4892). Not used yet. + version "0.1"; + + # Server identifier + # Use string format "text" + # Or hexstring 0x01ab00 + nsid "myserver0"; + + # Working directory of the server + # Used to store compiled zones and PID file + storage "/tmp/knot-sample"; + + # Custom pidfile path + # default: pidfile is created in 'storage'. + pidfile "/tmp/knot.pid"; + + # Number of workers per interface + # This option is used to force number of threads used per interface + # Default: unset (auto-estimates optimal value from the number of online CPUs) + workers 1; + + # User for running server + # May also specify user.group (f.e. knot.users) + user root; +} + +# Section 'keys' contains list of TSIG keys +keys { + + # TSIG key + # + # format: name key-type "<key>"; + # where key-type may be one of the following: + # hmac-md5 + # hmac-sha1 + # hmac-sha224 + # hmac-sha256 + # hmac-sha384 + # hmac-sha512 + # and <key> is the private key + key0.server0 hmac-md5 "Wg=="; + + # TSIG key for zone + key0.example.com hmac-md5 "==gW"; +} + +# Section 'interfaces' contains definitions of listening interfaces. +interfaces { + + # Interface entry + # + # Format 1: <name> { address <address>; [port <port>;] } + ipv4 { # <name> is an arbitrary symbolic name + address 127.0.0.1; # <address> may be ither IPv4 or IPv6 address + port 53531; # port is required for XFR/IN and NOTIFY/OUT + } + + # Format 2: <name> { address <address>@<port>; } + # shortipv4 { + # address 127.0.0.1@53532; + #} + + # Format 1 (IPv6 interface) + # ipv6 { + # address ::1@53533; + # } + + # Format 2 (IPv6 interface) + # ipv6b { + # address [::1]@53534; + # } + +} + +# Section 'remotes' contains symbolic names for remote servers. +# Syntax for 'remotes' is the same as for 'interfaces'. +remotes { + + # Remote entry + # + # Format 1: <name> { address <address>; [port <port>;] } + server0 { # <name> is an arbitrary symbolic name + address 127.0.0.1; # <address> may be ither IPv4 or IPv6 address + port 53531; # port is optional (default: 53) + key key0.server0; # (optional) specification of TSIG key associated for this remote + via ipv4; # (optional) source interface for queries + via 82.35.64.59; # (optional) source interface for queries, direct IPv4 + via [::cafe]; # (optional) source interface for queries, direct IPv6 + } + + # Format 2: <name> { address <address>@<port>; } + server1 { + address 127.0.0.1@53001; + } +} + +# Section 'zones' contains information about zones to be served. +zones { + + # Shared options for all listed zones + # + + # Build differences from zone file changes + # Possible values: on|off + # Default value: off + ixfr-from-differences off; + + # Enable semantic checks for all zones (if 'on') + # Possible values: on|off + # Default value: off + semantic-checks off; + + # Disable ANY type queries for authoritative answers (if 'on') + # Possible values: on|off + # Default value: off + disable-any off; + + # NOTIFY response timeout + # Possible values: <1,...> (seconds) + # Default value: 60 + notify-timeout 60; + + # Number of retries for NOTIFY + # Possible values: <1,...> + # Default value: 5 + notify-retries 5; + + # Timeout for syncing changes from zone database to zonefile + # Possible values: <1..INT_MAX> (seconds) + # Default value: 1h (1 hour) + # It is also possible to suffix with unit size [s/m/h/d] + # f.e. 1s = 1 day, 1m = 1 minute, 1h = 1 hour, 1d = 1 day + zonefile-sync 1h; + + # File size limit for IXFR journal + # Possible values: <1..INT_MAX> + # Default value: N/A (infinite) + # It is also possible to suffix with unit size [k/M/G] + # f.e. 1k, 100M, 2G + ixfr-fslimit 1G; + + # Zone entry + # + # Format: <zone-name> { file "<path-to-zone-file>"; } + example.com { # <zone-name> is the DNS name of the zone (zone root) + # <path-to-zone-file> may be either absolute or relative, in which case + # it is considered relative to the current directory from which the server + # was started. + file "samples/example.com.zone"; + + # Build differences from zone file changes + # Possible values: on|off + # Default value: off + ixfr-from-differences off; + + # Disable ANY type queries for authoritative answers (if 'on') + # Possible values: on|off + # Default value: off + disable-any off; + + # Enable zone semantic checks + # Possible values: on|off + # Default value: off + semantic-checks on; + + # NOTIFY response timeout (specific for current zone) + # Possible values: <1,...> (seconds) + # Default value: 60 + notify-timeout 60; + + # Number of retries for NOTIFY (specific for current zone) + # Possible values: <1,...> + # Default value: 5 + notify-retries 5; + + # Timeout for syncing changes from zone database to zonefile + # Possible values: <1..INT_MAX> (seconds) + # Default value: inherited from zones.zonefile-sync + # It is also possible to suffix with unit size [s/m/h/d] + # f.e. 1s = 1 day, 1m = 1 minute, 1h = 1 hour, 1d = 1 day + zonefile-sync 1h; + + # XFR master server + xfr-in server0; + + # ACL list of XFR slaves + xfr-out server0, server1; + + # ACL list of servers allowed to send NOTIFY queries + notify-in server0; + + # List of servers to send NOTIFY to + notify-out server0, server1; + } +} + +# Section 'log' configures logging of server messages. +# +# Logging recognizes 3 symbolic names of log devices: +# stdout - Standard output +# stderr - Standard error output +# syslog - Syslog +# +# In addition, arbitrary number of log files may be specified (see below). +# +# Log messages are characterized by severity and category. +# Supported severities: +# debug - Debug messages. Must be turned on at compile time. +# info - Informational messages. +# notice - Notices and hints. +# warning - Warnings. An action from the operator may be required. +# error - Recoverable error. Some action should be taken. +# fatal - Non-recoverable errors resulting in server shutdown. +# (Not supported yet.) +# all - All severities. +# +# Categories designate the source of the log message and roughly correspond +# to server modules +# Supported categories: +# server - Messages related to general operation of the server. +# zone - Messages related to zones, zone parsing and loading. +# answering - Messages regarding query processing and response creation. +# any - All categories +# +# More severities (separated by commas) may be listed for each category. +# All applicable severities must be listed. +# (I.e. specifying 'error' severity does mean: 'log error messages', +# and NOT 'log all messages of severity error and above'.) +# +# Default settings (in case there are no entries in 'log' section or the section +# is missing at all): +# +# stderr { any error; } +# syslog { any error; } +log { + + # Log entry + # + # Format 1: + # <log> { + # <category1> <severity1> [, <severity2> ...]; + # <category2> <severity1> [, <severity2> ...]; + # ... + # } + syslog { # <log> is a symbolic name of a log device (see above) + # log errors of any category + any error; # for <category> and <severity> see above + # log also warnings and notices from category 'zone' + zone warning, notice; + # log info from server + server info; + } + + # Log fatal, warnings and errors to stderr + stderr { + any error, warning; + } + + # Format 2: + # file <path> { + # <category1> <severity1> [, <severity2> ...]; + # <category2> <severity1> [, <severity2> ...]; + # } + file "/tmp/knot-sample/knotd.debug" { # <path> is absolute or relative path to log file + server debug; + } +} +.SH "SEE ALSO" +.LP +knotd(8), knotc(8) diff --git a/src/knot.service b/src/knot.service index 36864d0..36864d0 100644..100755 --- a/src/knot.service +++ b/src/knot.service diff --git a/src/knot.spec b/src/knot.spec index 5856aab..5856aab 100644..100755 --- a/src/knot.spec +++ b/src/knot.spec diff --git a/src/knot.sysconfig b/src/knot.sysconfig index 99daeba..99daeba 100644..100755 --- a/src/knot.sysconfig +++ b/src/knot.sysconfig diff --git a/src/knot/common.h b/src/knot/common.h index c321b94..ddf24b1 100644..100755 --- a/src/knot/common.h +++ b/src/knot/common.h @@ -85,14 +85,6 @@ typedef unsigned int uint; /*!< \brief Unsigned. */ /*! \brief Eliminate compiler warning with unused parameters. */ #define UNUSED(param) (void)(param) -/*! \brief Type-safe minimum macro. */ -#define MIN(a, b) \ - ({ typeof (a) _a = (a); typeof (b) _b = (b); _a < _b ? _a : _b; }) - -/*! \brief Type-safe maximum macro. */ -#define MAX(a, b) \ - ({ typeof (a) _a = (a); typeof (b) _b = (b); _a > _b ? _a : _b; }) - /* Optimisation macros. */ #ifndef likely /*! \brief Optimize for x to be true value. */ diff --git a/src/knot/conf/cf-lex.l b/src/knot/conf/cf-lex.l index 16b0343..58d1f4b 100644..100755 --- a/src/knot/conf/cf-lex.l +++ b/src/knot/conf/cf-lex.l @@ -90,6 +90,7 @@ notify-out { lval.t = yytext; return NOTIFY_OUT; } workers { lval.t = yytext; return WORKERS; } user { lval.t = yytext; return USER; } pidfile { lval.t = yytext; return PIDFILE; } +ixfr-from-differences { lval.t = yytext; return BUILD_DIFFS; } 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 b415662..69a5415 100644..100755 --- a/src/knot/conf/cf-parse.y +++ b/src/knot/conf/cf-parse.y @@ -172,6 +172,7 @@ static void conf_zone_start(void *scanner, char *name) { this_zone->ixfr_fslimit = -1; // Default policy applies this_zone->dbsync_timeout = -1; // Default policy applies this_zone->disable_any = -1; // Default policy applies + this_zone->build_diffs = -1; // Default policy applies // Append mising dot to ensure FQDN size_t nlen = strlen(name); @@ -266,6 +267,7 @@ static int conf_mask(void* scanner, int nval, int prefixlen) { %token <tok> XFR_OUT %token <tok> NOTIFY_IN %token <tok> NOTIFY_OUT +%token <tok> BUILD_DIFFS %token <tok> INTERFACES ADDRESS PORT %token <tok> IPA @@ -630,12 +632,31 @@ zone_acl: ; zone_start: - | TEXT { conf_zone_start(scanner, $1.t); } | USER { conf_zone_start(scanner, strdup($1.t)); } | REMOTES { conf_zone_start(scanner, strdup($1.t)); } | LOG_SRC { conf_zone_start(scanner, strdup($1.t)); } | LOG { conf_zone_start(scanner, strdup($1.t)); } | LOG_LEVEL { conf_zone_start(scanner, strdup($1.t)); } + | NUM '/' TEXT { + if ($1.i < 0 || $1.i > 255) { + char buf[256] = ""; + snprintf(buf, sizeof(buf), "rfc2317 origin prefix '%ld' out of bounds", $1.i); + cf_error(scanner, buf); + } + size_t len = 3 + 1 + strlen($3.t) + 1; /* <0,255> '/' rest */ + char *name = malloc(len * sizeof(char)); + if (name == NULL) { + cf_error(scanner, "out of memory"); + } else { + name[0] = '\0'; + if (snprintf(name, len, "%ld/%s", $1.i, $3.t) < 0) { + cf_error(scanner,"failed to convert rfc2317 origin to string"); + } + } + free($3.t); + conf_zone_start(scanner, name); + } + | TEXT { conf_zone_start(scanner, $1.t); } ; zone: @@ -643,6 +664,7 @@ zone: | zone zone_acl '}' | zone zone_acl_list | zone FILENAME TEXT ';' { this_zone->file = $3.t; } + | zone BUILD_DIFFS BOOL ';' { this_zone->build_diffs = $3.i; } | zone SEMANTIC_CHECKS BOOL ';' { this_zone->enable_checks = $3.i; } | zone DISABLE_ANY BOOL ';' { this_zone->disable_any = $3.i; } | zone DBSYNC_TIMEOUT NUM ';' { this_zone->dbsync_timeout = $3.i; } @@ -669,6 +691,7 @@ zones: ZONES '{' | zones zone '}' | zones DISABLE_ANY BOOL ';' { new_config->disable_any = $3.i; } + | zones BUILD_DIFFS BOOL ';' { new_config->build_diffs = $3.i; } | zones SEMANTIC_CHECKS BOOL ';' { new_config->zone_checks = $3.i; } | zones IXFR_FSLIMIT SIZE ';' { new_config->ixfr_fslimit = $3.l; } | zones IXFR_FSLIMIT NUM ';' { new_config->ixfr_fslimit = $3.i; } diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index 4bbf622..e0bd0ea 100644..100755 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -73,74 +73,6 @@ void cf_error(void *scanner, const char *msg) _parser_res = KNOTD_EPARSEFAIL; } -/* - * Config helper functions. - */ - -/*! \brief Free TSIG key. */ -static void key_free(conf_key_t *k) -{ - /* Secure erase. */ - if (k->k.secret) { - memset(k->k.secret, 0, strlen(k->k.secret)); - } - free(k->k.secret); - knot_dname_free(&k->k.name); - free(k); -} - -/*! \brief Free config interfaces. */ -static void iface_free(conf_iface_t *iface) -{ - if (!iface) { - return; - } - - free(iface->name); - free(iface->address); - free(iface); -} - -/*! \brief Free config logs. */ -static void log_free(conf_log_t *log) -{ - if (!log) { - return; - } - - if (log->file) { - free(log->file); - } - - /* Free loglevel mapping. */ - node *n = 0, *nxt = 0; - WALK_LIST_DELSAFE(n, nxt, log->map) { - free((conf_log_map_t*)n); - } - - free(log); -} - -/*! \brief Free config zones. */ -static void zone_free(conf_zone_t *zone) -{ - if (!zone) { - return; - } - - /* Free ACL lists. */ - WALK_LIST_FREE(zone->acl.xfr_in); - WALK_LIST_FREE(zone->acl.xfr_out); - WALK_LIST_FREE(zone->acl.notify_in); - WALK_LIST_FREE(zone->acl.notify_out); - - free(zone->name); - free(zone->file); - free(zone->db); - free(zone->ixfr_db); - free(zone); -} - /*! * \brief Call config hooks that need updating. * @@ -214,6 +146,11 @@ static int conf_process(conf_t *conf) if (zone->dbsync_timeout < 0) { zone->dbsync_timeout = conf->dbsync_timeout; } + + // Default policy for ixfr-from-differences + if (zone->build_diffs < 0) { + zone->build_diffs = conf->build_diffs; + } // Default policy for semantic checks if (zone->enable_checks < 0) { @@ -286,7 +223,12 @@ static int conf_process(conf_t *conf) *dpos = '\0'; } + /* Copy origin and remove bad characters. */ memcpy(dpos, zone->name, zname_len + 1); + for (int i = 0; i < zname_len; ++i) { + if (dpos[i] == '/') dpos[i] = '_'; + } + memcpy(dpos + zname_len, "db", 3); zone->db = dest; @@ -299,16 +241,26 @@ static int conf_process(conf_t *conf) ret = KNOTD_ENOMEM; /* Error report. */ continue; } - strncpy(dest, conf->storage, stor_len + 1); + dpos = dest; + memcpy(dpos, conf->storage, stor_len + 1); + dpos += stor_len; if (conf->storage[stor_len - 1] != '/') { - strncat(dest, "/", 1); + *(dpos++) = '/'; + *dpos = '\0'; } const char *dbext = "diff.db"; - strncat(dest, zone->name, zname_len); - strncat(dest, dbext, strlen(dbext)); + memcpy(dpos, zone->name, zname_len + 1); + for (int i = 0; i < zname_len; ++i) { + if (dpos[i] == '/') dpos[i] = '_'; + } + memcpy(dpos + zname_len, dbext, strlen(dbext) + 1); zone->ixfr_db = dest; } + + /* Update UID and GID. */ + if (conf->uid < 0) conf->uid = getuid(); + if (conf->gid < 0) conf->gid = getgid(); return ret; } @@ -475,6 +427,7 @@ conf_t *conf_new(const char* path) c->ixfr_fslimit = -1; c->uid = -1; c->gid = -1; + c->build_diffs = 0; /* Disable by default. */ return c; } @@ -553,33 +506,33 @@ void conf_truncate(conf_t *conf, int unload_hooks) // Free keys WALK_LIST_DELSAFE(n, nxt, conf->keys) { - key_free((conf_key_t *)n); + conf_free_key((conf_key_t *)n); } // Free interfaces WALK_LIST_DELSAFE(n, nxt, conf->ifaces) { - iface_free((conf_iface_t*)n); + conf_free_iface((conf_iface_t*)n); } conf->ifaces_count = 0; init_list(&conf->ifaces); // Free logs WALK_LIST_DELSAFE(n, nxt, conf->logs) { - log_free((conf_log_t*)n); + conf_free_log((conf_log_t*)n); } conf->logs_count = 0; init_list(&conf->logs); // Free remotes WALK_LIST_DELSAFE(n, nxt, conf->remotes) { - iface_free((conf_iface_t*)n); + conf_free_iface((conf_iface_t*)n); } conf->remotes_count = 0; init_list(&conf->remotes); // Free zones WALK_LIST_DELSAFE(n, nxt, conf->zones) { - zone_free((conf_zone_t*)n); + conf_free_zone((conf_zone_t*)n); } conf->zones_count = 0; init_list(&conf->zones); @@ -784,3 +737,63 @@ char* strcpath(char *path) return path; } +void conf_free_zone(conf_zone_t *zone) +{ + if (!zone) { + return; + } + + /* Free ACL lists. */ + WALK_LIST_FREE(zone->acl.xfr_in); + WALK_LIST_FREE(zone->acl.xfr_out); + WALK_LIST_FREE(zone->acl.notify_in); + WALK_LIST_FREE(zone->acl.notify_out); + + free(zone->name); + free(zone->file); + free(zone->db); + free(zone->ixfr_db); + free(zone); +} + +void conf_free_key(conf_key_t *k) +{ + /* Secure erase. */ + if (k->k.secret) { + memset(k->k.secret, 0, strlen(k->k.secret)); + } + free(k->k.secret); + knot_dname_free(&k->k.name); + free(k); +} + +void conf_free_iface(conf_iface_t *iface) +{ + if (!iface) { + return; + } + + free(iface->name); + free(iface->address); + free(iface); +} + +void conf_free_log(conf_log_t *log) +{ + if (!log) { + return; + } + + if (log->file) { + free(log->file); + } + + /* Free loglevel mapping. */ + node *n = 0, *nxt = 0; + WALK_LIST_DELSAFE(n, nxt, log->map) { + free((conf_log_map_t*)n); + } + + free(log); +} + diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h index 7e1b61f..ae41454 100644..100755 --- a/src/knot/conf/conf.h +++ b/src/knot/conf/conf.h @@ -96,6 +96,7 @@ typedef struct conf_zone_t { int disable_any; /*!< Disable ANY type queries for AA.*/ int notify_retries; /*!< NOTIFY query retries. */ int notify_timeout; /*!< Timeout for NOTIFY response (s). */ + int build_diffs; /*!< Calculate differences from changes. */ struct { list xfr_in; /*!< Remotes accepted for for xfr-in.*/ list xfr_out; /*!< Remotes accepted for xfr-out.*/ @@ -197,6 +198,7 @@ typedef struct conf_t { int notify_timeout; /*!< Timeout for NOTIFY response in seconds. */ int dbsync_timeout; /*!< Default interval between syncing to zonefile.*/ size_t ixfr_fslimit; /*!< File size limit for IXFR journal. */ + int build_diffs; /*!< Calculate differences from changes. */ /* * Implementation specifics @@ -323,22 +325,6 @@ static inline conf_t* conf() { return s_config; // Inline for performance reasons. } -/*! - * \brief Lock configuration for reading. - * - * \return Configuration context. - */ -static inline void conf_read_lock() { - rcu_read_lock(); -} - -/*! - * \brief Unlock configuration for reading. - */ -static inline void conf_read_unlock() { - rcu_read_unlock(); -} - /* * Utilities. */ @@ -363,6 +349,18 @@ char* strcdup(const char *s1, const char *s2); */ char* strcpath(char *path); +/*! \brief Free zone config. */ +void conf_free_zone(conf_zone_t *zone); + +/*! \brief Free TSIG key config. */ +void conf_free_key(conf_key_t *k); + +/*! \brief Free interface config. */ +void conf_free_iface(conf_iface_t *iface); + +/*! \brief Free log config. */ +void conf_free_log(conf_log_t *log); + #endif /* _KNOTD_CONF_H_ */ /*! @} */ diff --git a/src/knot/conf/logconf.c b/src/knot/conf/logconf.c index 4d7334f..4d7334f 100644..100755 --- a/src/knot/conf/logconf.c +++ b/src/knot/conf/logconf.c diff --git a/src/knot/conf/logconf.h b/src/knot/conf/logconf.h index 7b9e054..7b9e054 100644..100755 --- a/src/knot/conf/logconf.h +++ b/src/knot/conf/logconf.h diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c index 97412dd..07a9e38 100644..100755 --- a/src/knot/ctl/knotc_main.c +++ b/src/knot/ctl/knotc_main.c @@ -43,7 +43,8 @@ enum knotc_flag_t { F_VERBOSE = 1 << 1, F_WAIT = 1 << 2, F_INTERACTIVE = 1 << 3, - F_AUTO = 1 << 4 + F_AUTO = 1 << 4, + F_UNPRIVILEGED= 1 << 5 }; static inline unsigned has_flag(unsigned flags, enum knotc_flag_t f) { @@ -55,9 +56,9 @@ void help(int argc, char **argv) { printf("Usage: %sc [parameters] start|stop|restart|reload|running|" "compile [additional]\n", PACKAGE_NAME); - printf("Parameters:\n" + printf("\nParameters:\n" " -c [file], --config=[file] Select configuration file.\n" - " -j [num], --jobs=[num] Number of parallel tasks to run (only for 'compile').\n" + " -j [num], --jobs=[num] Number of parallel tasks to run when compiling.\n" " -f, --force Force operation - override some checks.\n" " -v, --verbose Verbose mode - additional runtime information.\n" " -V, --version Print %s server version.\n" @@ -66,7 +67,7 @@ void help(int argc, char **argv) " -a, --auto Enable automatic recompilation (start or reload).\n" " -h, --help Print help and usage.\n", PACKAGE_NAME); - printf("Actions:\n" + printf("\nActions:\n" " start Start %s server zone (no-op if running).\n" " stop Stop %s server (no-op if not running).\n" " restart Stops and then starts %s server.\n" @@ -75,8 +76,8 @@ void help(int argc, char **argv) " running Check if server is running.\n" " checkconf Check server configuration.\n" "\n" - " checkzone Check zones (accepts specific zones, f.e. " - "'knotc checkzone example1.com example2.com').\n" + " checkzone Check zones (accepts specific zones, \n" + " e.g. 'knotc checkzone example1.com example2.com').\n" " compile Compile zones (accepts specific zones, see above).\n", PACKAGE_NAME, PACKAGE_NAME, PACKAGE_NAME, PACKAGE_NAME); } @@ -142,10 +143,15 @@ pid_t wait_cmd(pid_t proc, int *rc) return proc; } -pid_t start_cmd(const char *argv[], int argc) +pid_t start_cmd(const char *argv[], int argc, int flags) { pid_t chproc = fork(); if (chproc == 0) { + + /* Alter privileges. */ + if (flags & F_UNPRIVILEGED) { + proc_update_privileges(conf()->uid, conf()->gid); + } /* Duplicate, it doesn't run from stack address anyway. */ char **args = malloc((argc + 1) * sizeof(char*)); @@ -180,7 +186,7 @@ pid_t start_cmd(const char *argv[], int argc) int exec_cmd(const char *argv[], int argc) { int ret = 0; - pid_t proc = start_cmd(argv, argc); + pid_t proc = start_cmd(argv, argc, 0); wait_cmd(proc, &ret); return ret; } @@ -292,15 +298,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid, int rc = 0; if (strcmp(action, "start") == 0) { // Check pidfile for w+ - FILE* chkf = fopen(pidfile, "w+"); - if (chkf == NULL) { - log_server_error("PID file '%s' is not writeable, " - "refusing to start\n", pidfile); - return 1; - } else { - fclose(chkf); - chkf = NULL; - } + log_server_info("Starting server...\n"); // Check PID valid_cmd = 1; @@ -332,7 +330,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid, } // Lock configuration - conf_read_lock(); + rcu_read_lock(); // Prepare command const char *cfg = conf()->filename; @@ -346,7 +344,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid, }; // Unlock configuration - conf_read_unlock(); + rcu_read_unlock(); // Execute command if (has_flag(flags, F_INTERACTIVE)) { @@ -381,6 +379,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid, if (strcmp(action, "stop") == 0) { // Check PID + log_server_info("Stopping server...\n"); valid_cmd = 1; rc = 0; if (pid <= 0 || !pid_running(pid)) { @@ -440,7 +439,6 @@ int execute(const char *action, char **argv, int argc, pid_t pid, } } - log_server_info("Restarting server.\n"); rc = execute("start", argv, argc, -1, flags, jobs, pidfile); } if (strcmp(action, "reload") == 0) { @@ -527,7 +525,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid, valid_cmd = 1; // Lock configuration - conf_read_lock(); + rcu_read_lock(); // Generate databases for all zones node *n = 0; @@ -604,7 +602,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid, } fflush(stdout); fflush(stderr); - pid_t zcpid = start_cmd(args, ac); + pid_t zcpid = start_cmd(args, ac, F_UNPRIVILEGED); zctask_add(tasks, jobs, zcpid, zone); ++running; } @@ -617,7 +615,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid, free(tasks); // Unlock configuration - conf_read_unlock(); + rcu_read_unlock(); } if (!valid_cmd) { log_server_error("Invalid command: '%s'\n", action); @@ -707,7 +705,7 @@ int main(int argc, char **argv) log_server_error("Couldn't open configuration file " "'%s'.\n", config_fn); } else { - log_server_error("Failed to parse configuration '%s'.\n", + log_server_error("Failed to load configuration '%s'.\n", config_fn); } free(default_fn); @@ -722,7 +720,7 @@ int main(int argc, char **argv) log_levels_add(LOGT_STDOUT, LOG_ANY, LOG_MASK(LOG_INFO)|LOG_MASK(LOG_DEBUG)); } - + // Fetch PID char* pidfile = pid_filename(); if (!pidfile) { diff --git a/src/knot/ctl/process.c b/src/knot/ctl/process.c index bb61804..d3fa2fc 100644..100755 --- a/src/knot/ctl/process.c +++ b/src/knot/ctl/process.c @@ -21,6 +21,9 @@ #include <errno.h> #include <string.h> #include <signal.h> +#include <grp.h> +#include <unistd.h> +#include <assert.h> #include "knot/common.h" #include "knot/ctl/process.h" @@ -29,7 +32,7 @@ char* pid_filename() { - conf_read_lock(); + rcu_read_lock(); /* Read configuration. */ char* ret = 0; @@ -37,7 +40,7 @@ char* pid_filename() ret = strdup(conf()->pidfile); } - conf_read_unlock(); + rcu_read_unlock(); return ret; } @@ -113,6 +116,7 @@ int pid_write(const char* fn) int pid_remove(const char* fn) { if (unlink(fn) < 0) { + perror("unlink"); return KNOTD_EINVAL; } @@ -124,3 +128,45 @@ int pid_running(pid_t pid) return kill(pid, 0) == 0; } +void proc_update_privileges(int uid, int gid) +{ +#ifdef HAVE_SETGROUPS + /* Drop supplementary groups. */ + if (uid != getuid() || gid != getgid()) { + if (setgroups(0, NULL) < 0) { + log_server_warning("Failed to drop supplementary groups" + " for uid '%d' (%s).\n", + getuid(), strerror(errno)); + } + } +#endif + + /* Watch uid/gid. */ + if (gid != getgid()) { + log_server_info("Changing group id to '%d'.\n", gid); + if (setregid(gid, gid) < 0) { + log_server_error("Failed to change gid to '%d'.\n", + gid); + } + } + if (uid != getuid()) { + log_server_info("Changing user id to '%d'.\n", uid); + if (setreuid(uid, uid) < 0) { + log_server_error("Failed to change uid to '%d'.\n", + uid); + } + } + + /* Check storage writeability. */ + char *lfile = strcdup(conf()->storage, "/knot.lock"); + assert(lfile != NULL); + FILE* fp = fopen(lfile, "w"); + if (fp == NULL) { + log_server_warning("Storage directory '%s' is not writeable.\n", + conf()->storage); + } else { + fclose(fp); + unlink(lfile); + } + free(lfile); +} diff --git a/src/knot/ctl/process.h b/src/knot/ctl/process.h index d8f2f4c..a387add 100644..100755 --- a/src/knot/ctl/process.h +++ b/src/knot/ctl/process.h @@ -83,6 +83,15 @@ int pid_remove(const char* fn); */ int pid_running(pid_t pid); +/*! + * \brief Update process privileges to new UID/GID. + * + * \param uid New user ID. + * \param gid New group ID. + * + */ +void proc_update_privileges(int uid, int gid); + #endif // _KNOTD_PROCESS_H_ /*! @} */ diff --git a/src/knot/main.c b/src/knot/main.c index 99ee1cf..4486a1c 100644..100755 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -20,6 +20,7 @@ #include <unistd.h> #include <getopt.h> #include <limits.h> + #ifdef HAVE_CAP_NG_H #include <cap-ng.h> #endif /* HAVE_CAP_NG_H */ @@ -72,7 +73,7 @@ void help(int argc, char **argv) { printf("Usage: %sd [parameters]\n", PACKAGE_NAME); - printf("Parameters:\n" + printf("\nParameters:\n" " -c, --config [file] Select configuration file.\n" " -d, --daemonize Run server as a daemon.\n" " -v, --verbose Verbose mode - additional runtime information.\n" @@ -138,6 +139,7 @@ int main(int argc, char **argv) emptyset.sa_flags = 0; sigaction(SIGALRM, &emptyset, NULL); // Interrupt sigaction(SIGPIPE, &emptyset, NULL); // Mask + rcu_register_thread(); // Setup event queue evqueue_set(evqueue_new()); @@ -158,11 +160,10 @@ int main(int argc, char **argv) server_t *server = server_create(); // Initialize configuration - conf_read_lock(); + rcu_read_lock(); conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0); conf_add_hook(conf(), CONF_ALL, server_conf_hook, server); - conf_add_hook(conf(), CONF_ALL, zones_ns_conf_hook, server->nameserver); - conf_read_unlock(); + rcu_read_unlock(); // Find implicit configuration file if (!config_fn) { @@ -229,7 +230,7 @@ int main(int argc, char **argv) log_server_error("Couldn't open configuration file " "'%s'.\n", config_fn); } else { - log_server_error("Failed to parse configuration '%s'.\n", + log_server_error("Failed to load configuration '%s'.\n", config_fn); } server_wait(server); @@ -242,21 +243,28 @@ int main(int argc, char **argv) } log_server_info("\n"); - // Create server instance - char* pidfile = pid_filename(); + /* Alter privileges. */ + proc_update_privileges(conf()->uid, conf()->gid); + + /* Load zones and add hook. */ + zones_ns_conf_hook(conf(), server->nameserver); + conf_add_hook(conf(), CONF_ALL, zones_ns_conf_hook, server->nameserver); // Run server int res = 0; + int has_pid = 0; + char* pidfile = pid_filename(); log_server_info("Starting server...\n"); if ((server_start(server)) == KNOTD_EOK) { // Save PID - int has_pid = 1; + has_pid = 1; int rc = pid_write(pidfile); if (rc < 0) { has_pid = 0; log_server_warning("Failed to create " - "PID file '%s'.\n", pidfile); + "PID file '%s' (%s).\n", + pidfile, strerror(errno)); } // Change directory if daemonized @@ -370,7 +378,7 @@ int main(int argc, char **argv) server_destroy(&server); // Remove PID file - if (pid_remove(pidfile) < 0) { + if (has_pid && pid_remove(pidfile) < 0) { log_server_warning("Failed to remove PID file.\n"); } @@ -381,6 +389,8 @@ int main(int argc, char **argv) // Destroy event loop evqueue_t *q = evqueue(); evqueue_free(&q); + + rcu_unregister_thread(); // Free default config filename if exists free(config_fn); diff --git a/src/knot/other/debug.h b/src/knot/other/debug.h index 1a8698e..1a8698e 100644..100755 --- a/src/knot/other/debug.h +++ b/src/knot/other/debug.h diff --git a/src/knot/other/error.c b/src/knot/other/error.c index 70c84a3..7f005e7 100644..100755 --- a/src/knot/other/error.c +++ b/src/knot/other/error.c @@ -41,6 +41,8 @@ const error_table_t knotd_error_msgs[] = { {KNOTD_ENOIPV6, "IPv6 support disabled."}, {KNOTD_EMALF, "Malformed data."}, {KNOTD_ESPACE, "Not enough space provided."}, - {KNOTD_EEXPIRED, "Resource is expired."}, + {KNOTD_EEXPIRED, "Resource is expired."}, + {KNOTD_ENODIFF, "Cannot create zone diff."}, + {KNOTD_EUPTODATE, "Zone is up-to-date."}, {KNOTD_ERROR, 0} }; diff --git a/src/knot/other/error.h b/src/knot/other/error.h index 65c51cf..4b9efee 100644..100755 --- a/src/knot/other/error.h +++ b/src/knot/other/error.h @@ -63,8 +63,10 @@ enum knot_error_t { KNOTD_EMALF, /*!< \brief Malformed data. */ KNOTD_ESPACE, /*!< \brief Not enough space provided. */ KNOTD_EEXPIRED, /*!< \brief Resource is expired. */ + KNOTD_ENODIFF, /*!< \brief Cannot create zone diff. */ + KNOTD_EUPTODATE, /*!< \brief Zone is up-to-date. */ - KNOTD_ERROR_COUNT = 21 + KNOTD_ERROR_COUNT = 23 }; /*! \brief Table linking error messages to error codes. */ diff --git a/src/knot/server/dthreads.c b/src/knot/server/dthreads.c index e2b45f2..0c38cfc 100644..100755 --- a/src/knot/server/dthreads.c +++ b/src/knot/server/dthreads.c @@ -21,15 +21,25 @@ #include <stdio.h> #include <unistd.h> #include <errno.h> +#include <urcu.h> + #ifdef HAVE_CAP_NG_H #include <cap-ng.h> #endif /* HAVE_CAP_NG_H */ +#ifdef HAVE_PTHREAD_NP_H +#include <pthread_np.h> +#endif /* HAVE_PTHREAD_NP_H */ #include "knot/common.h" #include "knot/server/dthreads.h" #include "common/log.h" #include "knot/other/error.h" +/* BSD cpu set compatibility. */ +#if defined(HAVE_CPUSET_BSD) +typedef cpuset_t cpu_set_t; +#endif + /*! \brief Lock thread state for R/W. */ static inline void lock_thread_rw(dthread_t *thread) { @@ -126,6 +136,7 @@ static void *thread_ep(void *data) sigaddset(&ignset, SIGPIPE); sigaddset(&ignset, SIGUSR1); pthread_sigmask(SIG_BLOCK, &ignset, 0); /*! \todo Review under BSD (issue #1441). */ + rcu_register_thread(); dbg_dt("dthreads: [%p] entered ep\n", thread); @@ -207,6 +218,7 @@ static void *thread_ep(void *data) lock_thread_rw(thread); thread->state |= ThreadJoinable; unlock_thread_rw(thread); + rcu_unregister_thread(); // Return return 0; @@ -854,22 +866,44 @@ int dt_stop(dt_unit_t *unit) // return KNOTD_EOK; //} -int dt_setaffinity(dthread_t *thread, void *mask, size_t len) + + +int dt_setaffinity(dthread_t *thread, unsigned* cpu_id, size_t cpu_count) { - if (thread == NULL || mask == NULL) { + if (thread == NULL) { return KNOTD_EINVAL; } #ifdef HAVE_PTHREAD_SETAFFINITY_NP - if (len != sizeof(cpu_set_t)) { - return KNOTD_EINVAL; + int ret = -1; + +/* Linux, FreeBSD interface. */ +#if defined(HAVE_CPUSET_LINUX) || defined(HAVE_CPUSET_BSD) + cpu_set_t set; + CPU_ZERO(&set); + for (unsigned i = 0; i < cpu_count; ++i) { + CPU_SET(cpu_id[i], &set); } - pthread_t tid = pthread_self(); - int ret = pthread_setaffinity_np(tid, len, (cpu_set_t*)mask); + ret = pthread_setaffinity_np(thread->_thr, sizeof(cpu_set_t), &set); +/* NetBSD interface. */ +#elif defined(HAVE_CPUSET_NETBSD) + cpuset_t *set = cpuset_create(); + if (set == NULL) { + return KNOTD_ENOMEM; + } + cpuset_zero(set); + for (unsigned i = 0; i < cpu_count; ++i) { + cpuset_set(cpu_id[i], &set); + } + ret = pthread_setaffinity_np(thread->_thr, cpuset_size(set), set); + cpuset_destroy(set); +#endif /* interface */ + if (ret < 0) { return KNOTD_ERROR; } -#else + +#else /* HAVE_PTHREAD_SETAFFINITY_NP */ return KNOTD_ENOTSUP; #endif @@ -988,8 +1022,17 @@ int dt_compact(dt_unit_t *unit) int dt_online_cpus() { int ret = -1; +/* Linux, Solaris, OS X 10.4+ */ #ifdef _SC_NPROCESSORS_ONLN ret = (int) sysconf(_SC_NPROCESSORS_ONLN); +#else +/* FreeBSD, NetBSD, OpenBSD, OS X < 10.4 */ +#if HAVE_SYSCTLBYNAME + size_t rlen = sizeof(int); + if (sysctlbyname("hw.ncpu", &ret, &rlen, NULL, 0) < 0) { + ret = -1; + } +#endif #endif return ret; } diff --git a/src/knot/server/dthreads.h b/src/knot/server/dthreads.h index 8ba457b..758bc28 100644..100755 --- a/src/knot/server/dthreads.h +++ b/src/knot/server/dthreads.h @@ -254,12 +254,13 @@ int dt_stop(dt_unit_t *unit); * \brief Set thread affinity to masked CPU's. * * \param thread Target thread instance. - * \param mask CPU mask (should be pointer to cpu_set_t). + * \param cpu_id Array of CPU IDs to set affinity to. + * \param cpu_count Number of CPUs in the array, set to 0 for no CPU. * * \retval KNOTD_EOK on success. * \retval KNOTD_EINVAL on invalid parameters. */ -int dt_setaffinity(dthread_t *thread, void *mask, size_t len); +int dt_setaffinity(dthread_t *thread, unsigned* cpu_id, size_t cpu_count); /*! * \brief Set thread to execute another runnable. diff --git a/src/knot/server/journal.c b/src/knot/server/journal.c index f901837..fa84021 100644..100755 --- a/src/knot/server/journal.c +++ b/src/knot/server/journal.c @@ -117,8 +117,8 @@ static int journal_recover(journal_t *j) } /* Write back. */ - lseek(j->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET); - if (!sfwrite(qstate, 2 * sizeof(uint16_t), j->fd)) { + int seek_ret = lseek(j->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET); + if (seek_ret < 0 || !sfwrite(qstate, 2 * sizeof(uint16_t), j->fd)) { dbg_journal("journal: failed to write back queue state\n"); return KNOTD_ERROR; } @@ -146,6 +146,7 @@ int journal_write_in(journal_t *j, journal_node_t **rn, uint64_t id, size_t len) /* Calculate remaining bytes to reach file size limit. */ size_t fs_remaining = j->fslimit - j->fsize; + int seek_ret = 0; /* Increase free segment if on the end of file. */ journal_node_t *n = j->nodes + j->qtail; @@ -193,8 +194,8 @@ int journal_write_in(journal_t *j, journal_node_t **rn, uint64_t id, size_t len) /* Write back evicted node. */ head->flags = JOURNAL_FREE; - lseek(j->fd, JOURNAL_HSIZE + (j->qhead + 1) * node_len, SEEK_SET); - if (!sfwrite(head, node_len, j->fd)) { + seek_ret = lseek(j->fd, JOURNAL_HSIZE + (j->qhead + 1) * node_len, SEEK_SET); + if (seek_ret < 0 || !sfwrite(head, node_len, j->fd)) { return KNOTD_ERROR; } @@ -204,8 +205,8 @@ int journal_write_in(journal_t *j, journal_node_t **rn, uint64_t id, size_t len) /* Write back query state. */ j->qhead = (j->qhead + 1) % j->max_nodes; uint16_t qstate[2] = {j->qhead, j->qtail}; - lseek(j->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET); - if (!sfwrite(qstate, 2 * sizeof(uint16_t), j->fd)) { + seek_ret = lseek(j->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET); + if (seek_ret < 0 || !sfwrite(qstate, 2 * sizeof(uint16_t), j->fd)) { return KNOTD_ERROR; } @@ -260,8 +261,8 @@ int journal_write_out(journal_t *journal, journal_node_t *n) journal->free.pos + journal->free.len); /* Write back free segment state. */ - lseek(journal->fd, JOURNAL_HSIZE, SEEK_SET); - if (!sfwrite(&journal->free, node_len, journal->fd)) { + int seek_ret = lseek(journal->fd, JOURNAL_HSIZE, SEEK_SET); + if (seek_ret < 0 || !sfwrite(&journal->free, node_len, journal->fd)) { /* Node is marked valid and failed to shrink free space, * node will be overwritten on the next write. Return error. */ @@ -278,8 +279,8 @@ int journal_write_out(journal_t *journal, journal_node_t *n) * qtail - highest valid node identifier (most recently used) */ uint16_t qstate[2] = {journal->qhead, journal->qtail}; - lseek(journal->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET); - if (!sfwrite(qstate, 2 * sizeof(uint16_t), journal->fd)) { + seek_ret = lseek(journal->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET); + if (seek_ret < 0 || !sfwrite(qstate, 2 * sizeof(uint16_t), journal->fd)) { dbg_journal("journal: failed to write back queue state\n"); return KNOTD_ERROR; } @@ -297,11 +298,15 @@ int journal_update_crc(int fd) char buf[4096]; ssize_t rb = 0; crc_t crc = crc_init(); - lseek(fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET); + if (lseek(fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET) < 0) { + return KNOTD_ERROR; + } while((rb = read(fd, buf, sizeof(buf))) > 0) { crc = crc_update(crc, (const unsigned char *)buf, rb); } - lseek(fd, MAGIC_LENGTH, SEEK_SET); + if (lseek(fd, MAGIC_LENGTH, SEEK_SET) < 0) { + return KNOTD_ERROR; + } if (!sfwrite(&crc, sizeof(crc_t), fd)) { dbg_journal("journal: couldn't write CRC to fd=%d\n", fd); return KNOTD_ERROR; @@ -508,7 +513,12 @@ journal_t* journal_open(const char *fn, size_t fslimit, int mode, uint16_t bflag /* Compare */ if (crc == crc_calc) { - lseek(fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET); /* Rewind. */ + /* Rewind. */ + if (lseek(fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET) < 0) { + fcntl(fd, F_SETLK, &fl); + close(fd); + return NULL; + } } else { log_server_warning("Journal file '%s' CRC error, " "it will be flushed.\n", fn); @@ -717,10 +727,10 @@ int journal_read(journal_t *journal, uint64_t id, journal_cmp_t cf, char *dst) (unsigned long long)id, n->pos, n->pos + n->len, n->flags); /* Seek journal node. */ - lseek(journal->fd, n->pos, SEEK_SET); + int seek_ret = lseek(journal->fd, n->pos, SEEK_SET); /* Read journal node content. */ - if (!sfread(dst, n->len, journal->fd)) { + if (seek_ret < 0 || !sfread(dst, n->len, journal->fd)) { return KNOTD_ERROR; } @@ -741,8 +751,8 @@ int journal_write(journal_t *journal, uint64_t id, const char *src, size_t size) } /* Write data to permanent storage. */ - lseek(journal->fd, n->pos, SEEK_SET); - if (!sfwrite(src, size, journal->fd)) { + int seek_ret = lseek(journal->fd, n->pos, SEEK_SET); + if (seek_ret < 0 || !sfwrite(src, size, journal->fd)) { return KNOTD_ERROR; } @@ -765,7 +775,9 @@ int journal_map(journal_t *journal, uint64_t id, char **dst, size_t size) /* Reserve data in permanent storage. */ /*! \todo This is only needed when inflating journal file. */ - lseek(journal->fd, n->pos, SEEK_SET); + if (lseek(journal->fd, n->pos, SEEK_SET) < 0) { + return KNOTD_ERROR; + } char nbuf[4096] = {0}; size_t wb = sizeof(nbuf); while (size > 0) { @@ -868,8 +880,8 @@ int journal_update(journal_t *journal, journal_node_t *n) i, (unsigned long long)n->id, n->flags); /* Write back. */ - lseek(journal->fd, jn_fpos, SEEK_SET); - if (!sfwrite(n, node_len, journal->fd)) { + int seek_ret = lseek(journal->fd, jn_fpos, SEEK_SET); + if (seek_ret < 0 || !sfwrite(n, node_len, journal->fd)) { dbg_journal("journal: failed to writeback node=%llu to %ld\n", (unsigned long long)n->id, jn_fpos); return KNOTD_ERROR; diff --git a/src/knot/server/journal.h b/src/knot/server/journal.h index 322e25d..fd0e3a6 100644..100755 --- a/src/knot/server/journal.h +++ b/src/knot/server/journal.h @@ -359,3 +359,5 @@ void journal_release(journal_t *journal); int journal_update_crc(int fd); #endif /* _KNOTD_JOURNAL_H_ */ + +/*! @} */ diff --git a/src/knot/server/notify.c b/src/knot/server/notify.c index aa0a52b..d0292e8 100644..100755 --- a/src/knot/server/notify.c +++ b/src/knot/server/notify.c @@ -242,39 +242,49 @@ int notify_process_request(knot_nameserver_t *ns, if (notify->parsed < notify->size) { if (knot_packet_parse_rest(notify) != KNOT_EOK) { dbg_notify("notify: failed to parse NOTIFY query\n"); - knot_ns_error_response(ns, knot_packet_id(notify), - ¬ify->header.flags1, - KNOT_RCODE_FORMERR, buffer, - size); + knot_ns_error_response_from_query(ns, notify, + KNOT_RCODE_FORMERR, + buffer, size); return KNOTD_EOK; } } + // check if it makes sense - if the QTYPE is SOA + if (knot_packet_qtype(notify) != KNOT_RRTYPE_SOA) { + // send back FORMERR + knot_ns_error_response_from_query(ns, notify, + KNOT_RCODE_FORMERR, buffer, + size); + return KNOTD_EOK; + } + // create NOTIFY response dbg_notify("notify: creating response\n"); ret = notify_create_response(notify, buffer, size); if (ret != KNOTD_EOK) { dbg_notify("notify: failed to create NOTIFY response\n"); - knot_ns_error_response(ns, knot_packet_id(notify), - ¬ify->header.flags1, - KNOT_RCODE_SERVFAIL, buffer, size); + knot_ns_error_response_from_query(ns, notify, + KNOT_RCODE_SERVFAIL, buffer, + size); return KNOTD_EOK; } // find the zone + rcu_read_lock(); const knot_dname_t *qname = knot_packet_qname(notify); const knot_zone_t *z = knot_zonedb_find_zone_for_name( ns->zone_db, qname); if (z == NULL) { + rcu_read_unlock(); dbg_notify("notify: failed to find zone by name\n"); - knot_ns_error_response(ns, knot_packet_id(notify), - ¬ify->header.flags1, - KNOT_RCODE_REFUSED, buffer, size); + knot_ns_error_response_from_query(ns, notify, + KNOT_RCODE_FORMERR, buffer, + size); return KNOTD_EOK; } notify_check_and_schedule(ns, z, from); - + rcu_read_unlock(); return KNOTD_EOK; } @@ -294,13 +304,16 @@ int notify_process_response(knot_nameserver_t *nameserver, *size = 0; /* Find matching zone. */ + rcu_read_lock(); const knot_dname_t *zone_name = knot_packet_qname(notify); knot_zone_t *zone = knot_zonedb_find_zone(nameserver->zone_db, zone_name); if (!zone) { + rcu_read_unlock(); return KNOTD_ENOENT; } if (!knot_zone_data(zone)) { + rcu_read_unlock(); return KNOTD_ENOENT; } @@ -318,6 +331,7 @@ int notify_process_response(knot_nameserver_t *nameserver, /* Found waiting NOTIFY query? */ if (!match) { + rcu_read_unlock(); pthread_mutex_unlock(&zd->lock); return KNOTD_ERROR; } @@ -327,6 +341,8 @@ int notify_process_response(knot_nameserver_t *nameserver, /* Zone was removed/reloaded. */ pthread_mutex_unlock(&zd->lock); + + rcu_read_unlock(); return KNOTD_EOK; } diff --git a/src/knot/server/notify.h b/src/knot/server/notify.h index a9ba807..a9ba807 100644..100755 --- a/src/knot/server/notify.h +++ b/src/knot/server/notify.h diff --git a/src/knot/server/server.c b/src/knot/server/server.c index 5611a0c..06f89b5 100644..100755 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -22,8 +22,6 @@ #include <errno.h> #include <openssl/evp.h> #include <assert.h> -#include <grp.h> - #include "common/prng.h" #include "knot/common.h" @@ -219,7 +217,7 @@ static int server_bind_sockets(server_t *server) */ /* Lock configuration. */ - conf_read_lock(); + rcu_read_lock(); /* Prepare helper lists. */ int bound = 0; @@ -290,7 +288,7 @@ static int server_bind_sockets(server_t *server) } /* Unlock configuration. */ - conf_read_unlock(); + rcu_read_unlock(); /* Publish new list. */ list* oldlist = rcu_xchg_pointer(&server->ifaces, newlist); @@ -331,7 +329,7 @@ static int server_bind_handlers(server_t *server) } /* Lock config. */ - conf_read_lock(); + rcu_read_lock(); /* Estimate number of threads/manager. */ int thr_count = 0; @@ -388,7 +386,7 @@ static int server_bind_handlers(server_t *server) } /* Unlock config. */ - conf_read_unlock(); + rcu_read_unlock(); return KNOTD_EOK; } @@ -552,7 +550,7 @@ int server_start(server_t *server) xfr_start(server->xfr_h); /* Lock configuration. */ - conf_read_lock(); + rcu_read_lock(); // Start dispatchers int ret = KNOTD_EOK; @@ -573,7 +571,7 @@ int server_start(server_t *server) } /* Unlock configuration. */ - conf_read_unlock(); + rcu_read_unlock(); dbg_server("server: server started\n"); @@ -653,16 +651,13 @@ void server_stop(server_t *server) { dbg_server("server: stopping server\n"); - /* Wait for XFR master. */ - xfr_stop(server->xfr_h); + /* Send termination event. */ + evsched_schedule_term(server->sched, 0); /* Interrupt XFR handler execution. */ if (server->xfr_h->interrupt) { server->xfr_h->interrupt(server->xfr_h); } - - /* Send termination event. */ - evsched_schedule_term(server->sched, 0); /* Lock RCU. */ rcu_read_lock(); @@ -743,51 +738,9 @@ int server_conf_hook(const struct conf_t *conf, void *data) "configured interfaces.\n"); } } - - /* Lock configuration. */ - conf_read_lock(); - int priv_failed = 0; - -#ifdef HAVE_SETGROUPS - /* Drop supplementary groups. */ - if (conf->gid > -1 || conf->uid > -1) { - ret = setgroups(0, NULL); - - /* Collect results. */ - if (ret < 0) { - log_server_error("Failed to set supplementary groups " - "for uid '%d' (%s).\n", - getuid(), strerror(errno)); - priv_failed = 1; - } - } -#endif - - /* Watch uid/gid. */ - if (conf->gid > -1 && conf->gid != getgid()) { - log_server_info("Changing group id to '%d'.\n", conf->gid); - if (setregid(conf->gid, conf->gid) < 0) { - log_server_error("Failed to change gid to '%d'.\n", - conf->gid); - priv_failed = 1; - } - } - if (conf->uid > -1 && conf->uid != getuid()) { - log_server_info("Changing user id to '%d'.\n", conf->uid); - if (setreuid(conf->uid, conf->uid) < 0) { - log_server_error("Failed to change uid to '%d'.\n", - conf->uid); - priv_failed = 1; - } - } - - if (priv_failed) { - ret = KNOTD_EACCES; - } /* Exit if the server is not running. */ if (ret != KNOTD_EOK || !(server->state & ServerRunning)) { - conf_read_unlock(); return KNOTD_ENOTRUNNING; } @@ -807,9 +760,6 @@ int server_conf_hook(const struct conf_t *conf, void *data) } } - /* Unlock config. */ - conf_read_unlock(); - return ret; } diff --git a/src/knot/server/server.h b/src/knot/server/server.h index 79a4729..79a4729 100644..100755 --- a/src/knot/server/server.h +++ b/src/knot/server/server.h diff --git a/src/knot/server/socket.c b/src/knot/server/socket.c index 4deb862..1e79f89 100644..100755 --- a/src/knot/server/socket.c +++ b/src/knot/server/socket.c @@ -99,6 +99,8 @@ int socket_connect(int fd, const char *addr, unsigned short port) int socket_bind(int socket, int family, const char *addr, unsigned short port) { /* Check address family. */ + int flag = 1; + int ret = 0; struct sockaddr* paddr = 0; socklen_t addrlen = 0; struct sockaddr_in saddr; @@ -150,12 +152,23 @@ int socket_bind(int socket, int family, const char *addr, unsigned short port) addr, buf); } -#endif + + /* Make the socket IPv6 only to allow 'any' for IPv4 and IPv6 at the same time. */ +#ifdef IPV6_V6ONLY + if (family == AF_INET6) { + /* Do not support mapping IPv4 in IPv6 sockets. */ + ret = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, + &flag, sizeof(flag)); + if (ret < 0) { + return KNOTD_EINVAL; + } + } +#endif /* IPV6_V6ONLY */ +#endif /* DISABLE_IPV6 */ } /* Reuse old address if taken. */ - int flag = 1; - int ret = setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, + ret = setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); if (ret < 0) { return KNOTD_EINVAL; @@ -164,7 +177,7 @@ int socket_bind(int socket, int family, const char *addr, unsigned short port) /* Bind to specified address. */ int res = bind(socket, paddr, addrlen); if (res < 0) { - log_server_error("Cannot bind to socket (%d).\n", + log_server_error("Cannot bind to socket (errno %d).\n", errno); return knot_map_errno(EADDRINUSE, EINVAL, EACCES, ENOMEM); } diff --git a/src/knot/server/socket.h b/src/knot/server/socket.h index 2185f03..2185f03 100644..100755 --- a/src/knot/server/socket.h +++ b/src/knot/server/socket.h diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c index 977de0b..60f4301 100644..100755 --- a/src/knot/server/tcp-handler.c +++ b/src/knot/server/tcp-handler.c @@ -104,7 +104,10 @@ static void tcp_sweep(fdset_t *set, int fd, void* data) int r_port = 0; struct sockaddr_storage addr; socklen_t len = sizeof(addr); - getpeername(fd, (struct sockaddr*)&addr, &len); + if (getpeername(fd, (struct sockaddr*)&addr, &len) < 0) { + dbg_net("tcp: sweep getpeername() on invalid socket=%d\n", fd); + return; + } /* Translate */ if (addr.ss_family == AF_INET) { @@ -158,15 +161,13 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen log_server_error("Socket type %d is not supported, " "IPv6 support is probably disabled.\n", w->ioh->type); - return KNOTD_ECONNREFUSED; + return KNOTD_EINVAL; } /* Receive data. */ int n = tcp_recv(fd, qbuf, qbuf_maxlen, &addr); if (n <= 0) { dbg_net("tcp: client on fd=%d disconnected\n", fd); - fdset_remove(w->fdset, fd); - close(fd); if (n == KNOTD_EAGAIN) { char r_addr[SOCKADDR_STRLEN]; sockaddr_tostr(&addr, r_addr, sizeof(r_addr)); @@ -184,7 +185,7 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen knot_packet_t *packet = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY); if (packet == NULL) { - int ret = knot_ns_error_response_from_query(ns, qbuf, n, + int ret = knot_ns_error_response_from_query_wire(ns, qbuf, n, KNOT_RCODE_SERVFAIL, qbuf, &resp_len); @@ -198,8 +199,8 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen int parse_res = knot_ns_parse_packet(qbuf, n, packet, &qtype); if (unlikely(parse_res != KNOT_EOK)) { if (parse_res > 0) { /* Returned RCODE */ - int ret = knot_ns_error_response_from_query(ns, qbuf, n, - parse_res, qbuf, &resp_len); + int ret = knot_ns_error_response_from_query(ns, packet, + parse_res, qbuf, &resp_len); if (ret == KNOT_EOK) { tcp_reply(fd, qbuf, resp_len); @@ -235,10 +236,9 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen /* Prepare context. */ res = xfr_request_init(&xfr, xfrt, XFR_FLAG_TCP, packet); if (res != KNOTD_EOK) { - knot_ns_error_response(ns, knot_packet_id(packet), - &packet->header.flags1, - KNOT_RCODE_SERVFAIL, qbuf, - &resp_len); + knot_ns_error_response_from_query(ns, packet, + KNOT_RCODE_SERVFAIL, + qbuf, &resp_len); res = KNOTD_EOK; break; } @@ -252,9 +252,9 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen return xfr_answer(ns, &xfr); case KNOT_QUERY_UPDATE: - knot_ns_error_response(ns, knot_packet_id(packet), - &packet->header.flags1, - KNOT_RCODE_NOTIMPL, qbuf, &resp_len); + knot_ns_error_response_from_query(ns, packet, + KNOT_RCODE_NOTIMPL, + qbuf, &resp_len); res = KNOTD_EOK; break; @@ -268,17 +268,17 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen case KNOT_RESPONSE_NORMAL: /*!< TCP handler doesn't send queries. */ case KNOT_RESPONSE_AXFR: /*!< Processed in XFR handler. */ case KNOT_RESPONSE_IXFR: /*!< Processed in XFR handler. */ - knot_ns_error_response(ns, knot_packet_id(packet), - &packet->header.flags1, - KNOT_RCODE_REFUSED, qbuf, &resp_len); + knot_ns_error_response_from_query(ns, packet, + KNOT_RCODE_REFUSED, + qbuf, &resp_len); res = KNOTD_EOK; break; /* Unknown opcodes. */ default: - knot_ns_error_response(ns, knot_packet_id(packet), - &packet->header.flags1, - KNOT_RCODE_FORMERR, qbuf, &resp_len); + knot_ns_error_response_from_query(ns, packet, + KNOT_RCODE_FORMERR, + qbuf, &resp_len); res = KNOTD_EOK; break; } @@ -326,7 +326,11 @@ static int tcp_accept(int fd) struct timeval tv; tv.tv_sec = TCP_ACTIVITY_WD; tv.tv_usec = 0; - setsockopt(incoming, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + if (setsockopt(incoming, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + log_server_warning("Couldn't set up TCP connection " + "watchdog timer for fd=%d.\n", + incoming); + } #endif } @@ -389,7 +393,7 @@ int tcp_send(int fd, uint8_t *msg, size_t msglen) */ #ifdef TCP_CORK int cork = 1; - setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork)); + int uncork = setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork)); #endif /* Send message size. */ @@ -406,9 +410,11 @@ int tcp_send(int fd, uint8_t *msg, size_t msglen) } #ifdef TCP_CORK - /* Uncork. */ - cork = 0; - setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork)); + /* Uncork only if corked successfuly. */ + if (uncork == 0) { + cork = 0; + setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork)); + } #endif return sent; } @@ -444,7 +450,9 @@ int tcp_recv(int fd, uint8_t *buf, size_t len, sockaddr_t *addr) /* Get peer name. */ if (addr) { socklen_t alen = addr->len; - getpeername(fd, addr->ptr, &alen); + if (getpeername(fd, addr->ptr, &alen) < 0) { + return KNOTD_EMALF; + } } /* Receive payload. */ @@ -582,6 +590,12 @@ int tcp_loop_worker(dthread_t *thread) "set to %ds\n", it.fd, TCP_ACTIVITY_WD); } + /*! \todo Refactor to allow erase on iterator.*/ + if (ret == KNOTD_ECONNREFUSED) { + fdset_remove(w->fdset, it.fd); + close(it.fd); + break; + } } diff --git a/src/knot/server/tcp-handler.h b/src/knot/server/tcp-handler.h index ab6baab..ab6baab 100644..100755 --- a/src/knot/server/tcp-handler.h +++ b/src/knot/server/tcp-handler.h diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c index dcb5b1f..fc4e9b1 100644..100755 --- a/src/knot/server/udp-handler.c +++ b/src/knot/server/udp-handler.c @@ -85,7 +85,7 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len, if (packet == NULL) { dbg_net("udp: failed to create packet on fd=%d\n", fd); - int ret = knot_ns_error_response_from_query(ns, qbuf, qbuflen, + int ret = knot_ns_error_response_from_query_wire(ns, qbuf, qbuflen, KNOT_RCODE_SERVFAIL, qbuf, resp_len); @@ -101,15 +101,25 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len, if (unlikely(res != KNOTD_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(ns, qbuf, - qbuflen, - res, qbuf, - resp_len); +// 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) { knot_packet_free(&packet); return KNOTD_EMALF; } + } else { + assert(res < 0); + int ret = knot_ns_error_response_from_query_wire( + ns, qbuf, qbuflen, KNOT_RCODE_SERVFAIL, qbuf, + resp_len); + + if (ret != KNOT_EOK) { + knot_packet_free(&packet); + return ret; + } } knot_packet_free(&packet); @@ -122,14 +132,6 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len, res = KNOTD_ERROR; switch(qtype) { - /* Response types. */ - case KNOT_RESPONSE_NORMAL: - res = zones_process_response(ns, addr, packet, qbuf, resp_len); - break; - case KNOT_RESPONSE_NOTIFY: - res = notify_process_response(ns, packet, addr, qbuf, resp_len); - break; - /* Query types. */ case KNOT_QUERY_NORMAL: res = zones_normal_query_answer(ns, packet, addr, qbuf, @@ -140,9 +142,9 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len, * Bind responds with FORMERR. */ /*! \note Draft exists for AXFR/UDP, but has not been standardized. */ - knot_ns_error_response(ns, knot_packet_id(packet), - &packet->header.flags1, - KNOT_RCODE_FORMERR, qbuf, resp_len); + knot_ns_error_response_from_query(ns, packet, + KNOT_RCODE_FORMERR, qbuf, + resp_len); res = KNOTD_EOK; break; case KNOT_QUERY_IXFR: @@ -151,7 +153,7 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len, * but I have found no tool or slave server to actually attempt * IXFR/UDP. */ - knot_packet_set_qtype(packet, KNOT_RRTYPE_SOA); +// knot_packet_set_qtype(packet, KNOT_RRTYPE_SOA); res = zones_normal_query_answer(ns, packet, addr, qbuf, resp_len, NS_TRANSPORT_UDP); @@ -163,27 +165,26 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len, case KNOT_QUERY_UPDATE: dbg_net("udp: UPDATE query on fd=%d not implemented\n", fd); - knot_ns_error_response(ns, knot_packet_id(packet), - &packet->header.flags1, - KNOT_RCODE_NOTIMPL, qbuf, resp_len); + knot_ns_error_response_from_query(ns, packet, + KNOT_RCODE_NOTIMPL, qbuf, + resp_len); res = KNOTD_EOK; break; /* Unhandled opcodes. */ case KNOT_RESPONSE_AXFR: /*!< Processed in XFR handler. */ case KNOT_RESPONSE_IXFR: /*!< Processed in XFR handler. */ - knot_ns_error_response(ns, knot_packet_id(packet), - &packet->header.flags1, - KNOT_RCODE_REFUSED, qbuf, - resp_len); + knot_ns_error_response_from_query(ns, packet, + KNOT_RCODE_REFUSED, qbuf, + resp_len); res = KNOTD_EOK; break; /* Unknown opcodes */ default: - knot_ns_error_response(ns, knot_packet_id(packet), - &packet->header.flags1, - KNOT_RCODE_FORMERR, qbuf, resp_len); + knot_ns_error_response_from_query(ns, packet, + KNOT_RCODE_FORMERR, qbuf, + resp_len); res = KNOTD_EOK; break; } @@ -204,17 +205,14 @@ static inline int udp_master_recvfrom(dthread_t *thread, stat_t *thread_stat) /* Set CPU affinity to improve load distribution on multicore systems. * Partial overlapping mask to be nice to scheduler. */ -#ifdef HAVE_PTHREAD_SETAFFINITY_NP int cpcount = dt_online_cpus(); if (cpcount > 0) { - unsigned tid = dt_get_id(thread); - cpu_set_t cpus; - CPU_ZERO(&cpus); - CPU_SET(tid % cpcount, &cpus); - CPU_SET((tid + 1) % cpcount, &cpus); - dt_setaffinity(thread, &cpus, sizeof(cpu_set_t)); + unsigned cpu[2]; + cpu[0] = dt_get_id(thread); + cpu[1] = (cpu[0] + 1) % cpcount; + cpu[0] = cpu[0] % cpcount; + dt_setaffinity(thread, cpu, 2); } -#endif knot_nameserver_t *ns = h->server->nameserver; @@ -389,6 +387,7 @@ static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat) free(addrs); free(iov); free(msgs); + close(sock); return KNOTD_ENOMEM; } @@ -407,17 +406,14 @@ static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat) /* Set CPU affinity to improve load distribution on multicore systems. * Partial overlapping mask to be nice to scheduler. */ -#ifdef HAVE_PTHREAD_SETAFFINITY_NP int cpcount = dt_online_cpus(); if (cpcount > 0) { - unsigned tid = dt_get_id(thread); - cpu_set_t cpus; - CPU_ZERO(&cpus); - CPU_SET(tid % cpcount, &cpus); - CPU_SET((tid + 1) % cpcount, &cpus); - dt_setaffinity(thread, &cpus, sizeof(cpu_set_t)); + unsigned cpu[2]; + cpu[0] = dt_get_id(thread); + cpu[1] = (cpu[0] + 1) % cpcount; + cpu[0] = cpu[0] % cpcount; + dt_setaffinity(thread, cpu, 2); } -#endif /* Loop until all data is read. */ ssize_t n = 0; @@ -433,7 +429,9 @@ static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat) /* Error and interrupt handling. */ if (unlikely(n <= 0)) { - if (errno != EINTR && errno != 0) { + if (errno != EINTR && errno != 0 && n < 0) { + log_server_error("I/O failure in UDP - errno %d " + "(Linux/recvmmsg)", errno); dbg_net("udp: recvmmsg() failed: %d\n", errno); } @@ -493,7 +491,10 @@ void __attribute__ ((constructor)) udp_master_init() #ifdef MSG_WAITFORONE /* Check for recvmmsg() support. */ if (dlsym(RTLD_DEFAULT, "recvmmsg") != 0) { - _udp_master = udp_master_recvmmsg; + int r = recvmmsg(0, NULL, 0, 0, 0); + if (errno != ENOSYS) { + _udp_master = udp_master_recvmmsg; + } } /* Check for sendmmsg() support. */ diff --git a/src/knot/server/udp-handler.h b/src/knot/server/udp-handler.h index f5fcd04..f5fcd04 100644..100755 --- a/src/knot/server/udp-handler.h +++ b/src/knot/server/udp-handler.h diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c index a77f1f1..7863c9a 100644..100755 --- a/src/knot/server/xfr-handler.c +++ b/src/knot/server/xfr-handler.c @@ -45,12 +45,19 @@ #define XFR_QUERY_WD 10 /*!< SOA/NOTIFY query timeout [s]. */ #define XFR_SWEEP_INTERVAL 2 /*! [seconds] between sweeps. */ #define XFR_BUFFER_SIZE 65535 /*! Do not change this - maximum value for UDP packet length. */ +#define XFR_MSG_DLTTR 9 /*! Index of letter differentiating IXFR/AXFR in log msg. */ /*! \brief Send interrupt to all workers. */ void xfr_interrupt(xfrhandler_t *h) { for(unsigned i = 0; i < h->unit->size; ++i) { - evqueue_write(h->workers[i]->q, "", 1); + evqueue_t *q = h->workers[i]->q; + if (evqueue_write(q, "", 1) == 1) { + close(q->fds[EVQUEUE_WRITEFD]); + q->fds[EVQUEUE_WRITEFD] = -1; + } else { + dt_stop_id(h->unit->threads[i]); + } } } @@ -63,6 +70,55 @@ static void xfr_request_deinit(knot_ns_xfr_t *r) } } +/*! + * \brief Clean pending transfer data. + */ +static int xfr_xfrin_cleanup(xfrworker_t *w, knot_ns_xfr_t *data) +{ + int ret = KNOTD_EOK; + knot_changesets_t *chs = 0; + + dbg_xfr_verb("Cleaning up after XFR-in.\n"); + + switch(data->type) { + case XFR_TYPE_AIN: + if (data->flags & XFR_FLAG_AXFR_FINISHED) { + knot_zone_contents_deep_free( + &data->new_contents, 1); + } else { + if (data->data) { + xfrin_constructed_zone_t *constr_zone = + (xfrin_constructed_zone_t *)data->data; + knot_zone_contents_deep_free( + &(constr_zone->contents), 0); + xfrin_free_orphan_rrsigs(&(constr_zone->rrsigs)); + free(data->data); + data->data = 0; + } + } + break; + case XFR_TYPE_IIN: + if (data->data) { + chs = (knot_changesets_t *)data->data; + knot_free_changesets(&chs); + data->data = NULL; + } + + // this function is called before new contents are created + assert(data->new_contents == NULL); + + break; + } + + /* Cleanup other data - so that the structure may be reused. */ + data->packet_nr = 0; + data->tsig_data_size = 0; + + dbg_xfr_detail("Done.\n"); + + return ret; +} + /*! \brief Free allocated xfer descriptor (also deinitializes). */ static void xfr_free_task(knot_ns_xfr_t *task) { @@ -83,7 +139,8 @@ static void xfr_free_task(knot_ns_xfr_t *task) } /* Unlock if XFR/IN.*/ - if (task->type == XFR_TYPE_AIN || task->type == XFR_TYPE_IIN) { + int is_xfer = task->type == XFR_TYPE_AIN || task->type == XFR_TYPE_IIN; + if (is_xfer) { knot_zone_t *zone = task->zone; zonedata_t *zd = (zonedata_t *)knot_zone_data(zone); if (zd) { @@ -92,9 +149,24 @@ static void xfr_free_task(knot_ns_xfr_t *task) } } - /* Deinitialize */ - xfr_request_deinit(task); + /* Free TSIG buffers. */ + if (task->digest) { + free(task->digest); + task->digest = NULL; + task->digest_size = 0; + } + if (task->tsig_data) { + free(task->tsig_data); + task->tsig_data = NULL; + task->tsig_data_size = 0; + } + if (!task->session_closed) { + /* Cleanup pending request. */ + if (is_xfer) { + xfr_xfrin_cleanup(w, task); + } + /* Remove fd-related data. */ xfrhandler_t *h = w->master; pthread_mutex_lock(&h->tasks_mx); @@ -102,6 +174,12 @@ static void xfr_free_task(knot_ns_xfr_t *task) pthread_mutex_unlock(&h->tasks_mx); close(task->session); } + + /* No further access to zone. */ + knot_zone_release(task->zone); + + /* Deinitialize */ + xfr_request_deinit(task); free(task); } @@ -139,11 +217,15 @@ static knot_ns_xfr_t *xfr_handler_task(xfrworker_t *w, int fd) static int xfr_udp_timeout(knot_ns_xfr_t *data) { /* Close socket. */ + rcu_read_lock(); knot_zone_t *z = data->zone; if (z && knot_zone_get_contents(z) && knot_zone_data(z)) { - log_zone_info("%s Failed, timeout exceeded.\n", - data->msgpref); + if (!(knot_zone_flags(z) & KNOT_ZONE_DISCARDED)) { + log_zone_info("%s Failed, timeout exceeded.\n", + data->msgpref); + } } + rcu_read_unlock(); /* Invalidate pending query. */ xfr_free_task(data); @@ -161,15 +243,93 @@ static int xfr_udp_timeout(knot_ns_xfr_t *data) */ static int xfr_process_udp_resp(xfrworker_t *w, int fd, knot_ns_xfr_t *data) { + /* Check if zone is valid. */ + int ret = KNOTD_ECONNREFUSED; + rcu_read_lock(); + if (knot_zone_flags(data->zone) & KNOT_ZONE_DISCARDED) { + rcu_read_unlock(); + return ret; + } + rcu_read_unlock(); + /* Receive msg. */ - ssize_t n = recvfrom(data->session, data->wire, data->wire_size, 0, data->addr.ptr, &data->addr.len); + ssize_t n = recvfrom(data->session, data->wire, data->wire_size, + 0, data->addr.ptr, &data->addr.len); size_t resp_len = data->wire_size; - if (n > 0) { - udp_handle(fd, data->wire, n, &resp_len, &data->addr, w->ns); + if (n <= 0) { + return ret; + } + + // parse packet + knot_packet_t *re = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE); + if (re == NULL) { + return KNOTD_ENOMEM; + } + + knot_packet_type_t rt = KNOT_RESPONSE_NORMAL; + ret = knot_ns_parse_packet(data->wire, n, re, &rt); + if (ret != KNOTD_EOK) { + knot_packet_free(&re); + return KNOTD_EOK; /* Ignore */ + } + + /* Ignore other packets. */ + if (rt != KNOT_RESPONSE_NORMAL && rt != KNOT_RESPONSE_NOTIFY) { + knot_packet_free(&re); + return KNOTD_EOK; /* Ignore */ + } + ret = knot_packet_parse_rest(re); + if (ret != KNOT_EOK) { + knot_packet_free(&re); + return KNOTD_EOK; /* Ignore */ + } + + // check TSIG + const knot_rrset_t * tsig_rr = knot_packet_tsig(re); + if (data->tsig_key != NULL) { + /*! \todo Not sure about prev_time_signed, but this is the first + * reply and we should pass query sign time as the time + * may be different. Leaving to 0. + */ + ret = knot_tsig_client_check(tsig_rr, data->wire, n, + data->digest, data->digest_size, + data->tsig_key, 0); + if (ret != KNOT_EOK) { + log_server_error("%s %s\n", + data->msgpref, knot_strerror(ret)); + knot_packet_free(&re); + return KNOTD_ECONNREFUSED; + } + + } + + // process response + switch(rt) { + case KNOT_RESPONSE_NORMAL: + ret = zones_process_response(w->ns, &data->addr, re, + data->wire, &resp_len); + break; + case KNOT_RESPONSE_NOTIFY: + ret = notify_process_response(w->ns, re, &data->addr, + data->wire, &resp_len); + break; + default: + break; } + knot_packet_free(&re); + + /* Check up-to-date zone. */ + if (ret == KNOTD_EUPTODATE) { + log_server_info("%s %s\n", data->msgpref, knotd_strerror(ret)); + ret = KNOTD_ECONNREFUSED; + } + /* Invalidate pending query. */ - return KNOTD_ECONNREFUSED; + if (ret == KNOTD_EOK) { + ret = KNOTD_ECONNREFUSED; + } + return ret; } /*! \brief Sweep non-replied connection. */ @@ -250,46 +410,6 @@ static knot_ns_xfr_t *xfr_register_task(xfrworker_t *w, const knot_ns_xfr_t *req } /*! - * \brief Clean pending transfer data. - */ -static int xfr_xfrin_cleanup(xfrworker_t *w, knot_ns_xfr_t *data) -{ - int ret = KNOTD_EOK; - knot_changesets_t *chs = 0; - - switch(data->type) { - case XFR_TYPE_AIN: - if (data->flags & XFR_FLAG_AXFR_FINISHED) { - knot_zone_contents_deep_free( - &data->new_contents, 1); - } else { - if (data->data) { - xfrin_constructed_zone_t *constr_zone = - (xfrin_constructed_zone_t *)data->data; - knot_zone_contents_deep_free( - &(constr_zone->contents), 0); - xfrin_free_orphan_rrsigs(&(constr_zone->rrsigs)); - free(data->data); - data->data = 0; - } - } - break; - case XFR_TYPE_IIN: - if (data->data) { - chs = (knot_changesets_t *)data->data; - knot_free_changesets(&chs); - } - - // this function is called before new contents are created - assert(data->new_contents == NULL); - - break; - } - - return ret; -} - -/*! * \brief Finalize XFR/IN transfer. * * \param w XFR worker. @@ -382,7 +502,7 @@ static int xfr_xfrin_finalize(xfrworker_t *w, knot_ns_xfr_t *data) /* Switch zone contents. */ switch_ret = xfrin_switch_zone(data->zone, data->new_contents, - data->type); + data->type); if (switch_ret != KNOT_EOK) { log_zone_error("%s Failed to replace " @@ -401,7 +521,7 @@ static int xfr_xfrin_finalize(xfrworker_t *w, knot_ns_xfr_t *data) break; } - xfrin_cleanup_successful_update( &chs->changes); + xfrin_cleanup_successful_update(&chs->changes); /* Free changesets, but not the data. */ knot_free_changesets(&chs); @@ -418,24 +538,6 @@ static int xfr_xfrin_finalize(xfrworker_t *w, knot_ns_xfr_t *data) } /*! - * \brief Prepare TSIG for XFR. - */ -static int xfr_prepare_tsig(knot_ns_xfr_t *xfr, knot_key_t *key) -{ - int ret = KNOT_EOK; - xfr->tsig_key = key; - xfr->tsig_size = tsig_wire_maxsize(key); - xfr->digest_max_size = tsig_alg_digest_length( - key->algorithm); - xfr->digest = malloc(xfr->digest_max_size); - memset(xfr->digest, 0 , xfr->digest_max_size); - dbg_xfr("xfr: found TSIG key (MAC len=%zu), adding to transfer\n", - xfr->digest_max_size); - - return ret; -} - -/*! * \brief Check TSIG if exists. */ static int xfr_check_tsig(knot_ns_xfr_t *xfr, knot_rcode_t *rcode, char **tag) @@ -473,15 +575,11 @@ static int xfr_check_tsig(knot_ns_xfr_t *xfr, knot_rcode_t *rcode, char **tag) // return REFUSED xfr->tsig_key = 0; *rcode = KNOT_RCODE_REFUSED; - return KNOT_EXFRREFUSED; + return KNOT_EXFRDENIED; } if (tsig_rr) { tsig_algorithm_t alg = tsig_rdata_alg(tsig_rr); if (tsig_alg_digest_length(alg) == 0) { - log_server_info("%s Unsupported digest algorithm " - "requested, treating as " - "bad key.\n", - xfr->msgpref); *rcode = KNOT_RCODE_NOTAUTH; xfr->tsig_key = NULL; xfr->tsig_rcode = KNOT_TSIG_RCODE_BADKEY; @@ -598,8 +696,14 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, /* Read DNS/TCP packet. */ int ret = 0; int rcvd = tcp_recv(fd, buf, buflen, 0); + + /* Raise read-lock and check if zone is still valid. */ + rcu_read_lock(); + int zone_discarded = (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED); + + /* Handle incoming packet. */ data->wire_size = rcvd; - if (rcvd <= 0) { + if (rcvd <= 0 || zone_discarded) { data->wire_size = 0; ret = KNOT_ECONN; } else { @@ -621,14 +725,15 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, /* AXFR-style IXFR. */ if (ret == KNOT_ENOIXFR) { assert(data->type == XFR_TYPE_IIN); - log_server_notice("%s Fallback to AXFR/IN.\n", data->msgpref); + log_server_notice("%s Fallback to AXFR.\n", data->msgpref); data->type = XFR_TYPE_AIN; - data->msgpref[0] = 'A'; + data->msgpref[XFR_MSG_DLTTR] = 'A'; ret = knot_ns_process_axfrin(w->ns, data); } /* Check return code for errors. */ - dbg_xfr_verb("xfr: processed incoming XFR packet (res = %d)\n", ret); + dbg_xfr_verb("xfr: processed incoming XFR packet (%s)\n", + knot_strerror(ret)); /* Finished xfers. */ int xfer_finished = 0; @@ -637,8 +742,8 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, } /* IXFR refused, try again with AXFR. */ - if (zone && data->type == XFR_TYPE_IIN && ret == KNOT_EXFRREFUSED) { - log_server_notice("%s Transfer failed, fallback to AXFR/IN.\n", + if (data->type == XFR_TYPE_IIN && ret == KNOT_EXFRREFUSED) { + log_server_notice("%s Transfer failed, fallback to AXFR.\n", data->msgpref); size_t bufsize = buflen; data->wire_size = buflen; /* Reset maximum bufsize */ @@ -650,20 +755,26 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, data->wire, bufsize); /* Switch to AIN type XFR and return now. */ if (ret == bufsize) { + rcu_read_unlock(); + xfr_xfrin_cleanup(w, data); data->type = XFR_TYPE_AIN; - data->msgpref[0] = 'A'; + data->msgpref[XFR_MSG_DLTTR] = 'A'; return KNOTD_EOK; } } } + + rcu_read_unlock(); /* Handle errors. */ - if (ret == KNOT_ENOXFR) { - log_server_warning("%s Finished, %s\n", - data->msgpref, knot_strerror(ret)); - } else if (ret < 0) { - log_server_error("%s %s\n", - data->msgpref, knot_strerror(ret)); + if (!zone_discarded) { + if (ret == KNOT_ENOXFR) { + log_server_warning("%s Finished, %s\n", + data->msgpref, knot_strerror(ret)); + } else if (ret < 0) { + log_server_error("%s %s\n", + data->msgpref, knot_strerror(ret)); + } } /* Check finished zone. */ @@ -683,12 +794,11 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, knot_zone_t *zone = (knot_zone_t *)data->zone; zonedata_t *zd = (zonedata_t *)knot_zone_data(zone); - /* Only for successful xfers. */ - if (ret > 0) { + /* Only for successful xfers on non-discarded zones. */ + if (ret > 0 && !zone_discarded) { ret = xfr_xfrin_finalize(w, data); /* AXFR bootstrap timeout. */ - rcu_read_lock(); if (ret != KNOTD_EOK && !knot_zone_contents(zone)) { /* Schedule request (60 - 90s random delay). */ int tmr_s = AXFR_BOOTSTRAP_RETRY; @@ -702,25 +812,11 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, /* Update timers. */ server_t *server = (server_t *)knot_ns_get_data(w->ns); zones_timers_update(zone, zd->conf, server->sched); - rcu_read_unlock(); - } else { /* Cleanup */ xfr_xfrin_cleanup(w, data); } - /* Free TSIG buffers. */ - if (data->digest) { - free(data->digest); - data->digest = 0; - data->digest_size = 0; - } - if (data->tsig_data) { - free(data->tsig_data); - data->tsig_data = 0; - data->tsig_data_size = 0; - } - /* Disconnect. */ result = KNOTD_ECONNREFUSED; /* Make it disconnect. */ } @@ -753,6 +849,7 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data) /* Enqueue to worker that has zone locked for XFR/IN. */ int ret = pthread_mutex_trylock(&zd->xfr_in.lock); + rcu_read_lock(); if (ret != 0) { dbg_xfr_verb("xfr: XFR/IN switching to another thread, " "zone '%s' is already in transfer\n", @@ -769,8 +866,11 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data) strerror_r(errno, ebuf, sizeof(ebuf)); dbg_xfr("xfr: couldn't write request to evqueue: %s\n", ebuf); + rcu_read_unlock(); return KNOTD_ERROR; } + + rcu_read_unlock(); return KNOTD_EOK; } else { zd->xfr_in.wrkr = w; @@ -798,6 +898,7 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data) } if (ret < 0) { + rcu_read_unlock(); pthread_mutex_unlock(&zd->xfr_in.lock); if (fd >= 0) { close(fd); @@ -811,6 +912,7 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data) /* Duplicate existing socket descriptor. */ data->session = dup(data->session); if (data->session < 0) { + rcu_read_unlock(); pthread_mutex_unlock(&zd->xfr_in.lock); log_server_warning("Not enough memory to duplicate \n" "sockets.\n"); @@ -819,12 +921,11 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data) } /* Fetch zone contents. */ - rcu_read_lock(); const knot_zone_contents_t *contents = knot_zone_contents(zone); if (!contents && data->type == XFR_TYPE_IIN) { pthread_mutex_unlock(&zd->xfr_in.lock); rcu_read_unlock(); - log_server_warning("%s Refusing to start IXFR/IN on zone with no " + log_server_warning("%s Refusing to start IXFR on zone with no " "contents.\n", data->msgpref); close(data->session); data->session = -1; @@ -958,7 +1059,7 @@ static int xfr_answer_ixfr(knot_nameserver_t *ns, knot_ns_xfr_t *xfr) "Fallback to AXFR.\n", xfr->msgpref); xfr->type = XFR_TYPE_AOUT; - xfr->msgpref[0] = 'A'; + xfr->msgpref[XFR_MSG_DLTTR] = 'A'; return xfr_answer_axfr(ns, xfr); } else if (chsload == KNOTD_EMALF) { xfr->rcode = KNOT_RCODE_FORMERR; @@ -973,7 +1074,7 @@ static int xfr_answer_ixfr(knot_nameserver_t *ns, knot_ns_xfr_t *xfr) /* Finally, answer. */ if (chsload == KNOTD_EOK) { ret = knot_ns_answer_ixfr(ns, xfr); - dbg_xfr("xfr: ns_answer_ixfr() = %d.\n", ret); + dbg_xfr("xfr: ns_answer_ixfr() = %s.\n", knot_strerror(ret)); } return ret; @@ -987,6 +1088,7 @@ static int xfr_update_msgpref(knot_ns_xfr_t *req, const char *keytag) return KNOTD_EINVAL; } + rcu_read_lock(); char r_addr[SOCKADDR_STRLEN]; char *r_key = NULL; int r_port = sockaddr_portnum(&req->addr); @@ -1013,13 +1115,12 @@ static int xfr_update_msgpref(knot_ns_xfr_t *req, const char *keytag) } /* Prepare log message. */ - conf_read_lock(); const char *zname = req->zname; if (zname == NULL && req->zone != NULL) { zonedata_t *zd = (zonedata_t *)knot_zone_data(req->zone); if (zd == NULL) { free(r_key); - conf_read_unlock(); + rcu_read_unlock(); return KNOTD_EINVAL; } else { zname = zd->conf->name; @@ -1028,16 +1129,16 @@ static int xfr_update_msgpref(knot_ns_xfr_t *req, const char *keytag) const char *pformat = NULL; switch (req->type) { case XFR_TYPE_AIN: - pformat = "AXFR transfer of '%s/IN' with '%s@%d'%s:"; + pformat = "Incoming AXFR transfer of '%s' with '%s@%d'%s:"; break; case XFR_TYPE_IIN: - pformat = "IXFR transfer of '%s/IN' with '%s@%d'%s:"; + pformat = "Incoming IXFR transfer of '%s' with '%s@%d'%s:"; break; case XFR_TYPE_AOUT: - pformat = "AXFR transfer of '%s/OUT' to '%s@%d'%s:"; + pformat = "Outgoing AXFR transfer of '%s' to '%s@%d'%s:"; break; case XFR_TYPE_IOUT: - pformat = "IXFR transfer of '%s/OUT' to '%s@%d'%s:"; + pformat = "Outgoing IXFR transfer of '%s' to '%s@%d'%s:"; break; case XFR_TYPE_NOTIFY: pformat = "NOTIFY query of '%s' to '%s@%d'%s:"; @@ -1065,7 +1166,7 @@ static int xfr_update_msgpref(knot_ns_xfr_t *req, const char *keytag) req->msgpref = msg; } - conf_read_unlock(); + rcu_read_unlock(); free(r_key); return KNOTD_EOK; } @@ -1277,9 +1378,11 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *xfr) return KNOTD_EINVAL; } + rcu_read_lock(); int ret = knot_ns_init_xfr(ns, xfr); + int xfr_failed = (ret != KNOT_EOK); - const char * errstr = knot_strerror(ret); + const char *errstr = knot_strerror(ret); // use the QNAME as the zone name to get names also for // zones that are not in the server @@ -1310,6 +1413,23 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *xfr) } free(keytag); + /* Announce. */ + log_server_info("%s Started.\n", xfr->msgpref); + switch (ret) { + case KNOT_EXFRDENIED: + log_server_info("%s TSIG required, but not found in query.\n", + xfr->msgpref); + break; + case KNOT_TSIG_EBADKEY: + log_server_info("%s Unsupported digest " + "algorithm requested, " + "treating as bad key.\n", + xfr->msgpref); + break; + default: + break; + } + /* Prepare place for TSIG data */ xfr->tsig_data = malloc(KNOT_NS_TSIG_DATA_MAX_SIZE); if (xfr->tsig_data) { @@ -1356,6 +1476,7 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *xfr) free(xfr->tsig_data); xfr->tsig_data = NULL; xfr_request_deinit(xfr); + rcu_read_unlock(); /* Cleanup. */ free(xfr->digest); @@ -1381,6 +1502,8 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen) return KNOTD_ENOTRUNNING; } + rcu_read_lock(); + /* Update request. */ xfr.wire = buf; xfr.wire_size = buflen; @@ -1389,12 +1512,15 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen) xfr_update_msgpref(&xfr, NULL); /* Check if not already processing. */ - zonedata_t *zd = NULL; - if (xfr.zone != NULL) { - zd = (zonedata_t *)knot_zone_data(xfr.zone); - } + zonedata_t *zd = (zonedata_t *)knot_zone_data(xfr.zone); - conf_read_lock(); + /* Check if the zone is not discarded. */ + if (knot_zone_flags(xfr.zone) & KNOT_ZONE_DISCARDED) { + xfr_request_deinit(&xfr); + knot_zone_release(xfr.zone); + rcu_read_unlock(); + return KNOTD_EOK; + } /* Handle request. */ knot_ns_xfr_t *task = NULL; @@ -1423,6 +1549,7 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen) log_server_error("%s %s\n", xfr.msgpref, knotd_strerror(ret)); } + knot_zone_release(xfr.zone); /* No further access to zone. */ } break; @@ -1431,21 +1558,21 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen) /* Register task. */ task = xfr_register_task(w, &xfr); if (!task) { + knot_zone_release(xfr.zone); /* No further access to zone. */ ret = KNOTD_ENOMEM; - break; + } else { + /* Add timeout. */ + fdset_set_watchdog(w->fdset, task->session, XFR_QUERY_WD); + log_server_info("%s Query issued.\n", xfr.msgpref); + ret = KNOTD_EOK; } - - /* Add timeout. */ - fdset_set_watchdog(w->fdset, task->session, XFR_QUERY_WD); - log_server_info("%s Query issued.\n", xfr.msgpref); - ret = KNOTD_EOK; break; default: log_server_error("Unknown XFR request type (%d).\n", xfr.type); break; } - conf_read_unlock(); + rcu_read_unlock(); /* Deinitialize (it is already registered, or discarded). * Right now, this only frees temporary msgpref. @@ -1506,7 +1633,6 @@ int xfr_worker(dthread_t *thread) fdset_begin(w->fdset, &it); int rfd_event = 0; while(nfds > 0) { - /* Check if it request. */ if (it.fd == rfd) { rfd_event = 1; /* Delay new tasks after processing. */ @@ -1527,6 +1653,8 @@ int xfr_worker(dthread_t *thread) ret = xfr_process_event(w, it.fd, data, buf, buflen); if (ret != KNOTD_EOK) { xfr_free_task(data); + /*! \todo Refactor to allow erase on iterator.*/ + break; } } @@ -1551,6 +1679,11 @@ int xfr_worker(dthread_t *thread) next_sweep.tv_sec += XFR_SWEEP_INTERVAL; } } + + /* Check for interrupt request. */ + if (ret == KNOTD_ENOTRUNNING) { + break; + } } /* Stop whole unit. */ @@ -1559,3 +1692,28 @@ int xfr_worker(dthread_t *thread) thread->data = 0; return KNOTD_EOK; } + +int xfr_prepare_tsig(knot_ns_xfr_t *xfr, knot_key_t *key) +{ + if (xfr == NULL || key == NULL) { + return KNOTD_EINVAL; + } + + int ret = KNOT_EOK; + xfr->tsig_key = key; + xfr->tsig_size = tsig_wire_maxsize(key); + xfr->digest_max_size = tsig_alg_digest_length( + key->algorithm); + xfr->digest = malloc(xfr->digest_max_size); + if (xfr->digest == NULL) { + xfr->tsig_key = NULL; + xfr->tsig_size = 0; + xfr->digest_max_size = 0; + return KNOTD_ENOMEM; + } + memset(xfr->digest, 0 , xfr->digest_max_size); + dbg_xfr("xfr: found TSIG key (MAC len=%zu), adding to transfer\n", + xfr->digest_max_size); + + return ret; +} diff --git a/src/knot/server/xfr-handler.h b/src/knot/server/xfr-handler.h index 02eb189..e2f5643 100644..100755 --- a/src/knot/server/xfr-handler.h +++ b/src/knot/server/xfr-handler.h @@ -170,6 +170,17 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *req); */ int xfr_worker(dthread_t *thread); +/*! + * \brief Prepare TSIG for XFR. + * \param xfr XFR request. + * \param key Used TSIG key. + * + * \retval KNOTD_EOK on success. + * \retval KNOTD_EINVAL on NULL parameters. + * \retval KNOTD_ENOMEM when out of memory. + */ +int xfr_prepare_tsig(knot_ns_xfr_t *xfr, knot_key_t *key); + #endif // _KNOTD_XFRHANDLER_H_ /*! @} */ diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c index 37c1316..95ab2b1 100644..100755 --- a/src/knot/server/zones.c +++ b/src/knot/server/zones.c @@ -39,6 +39,7 @@ #include "libknot/updates/changesets.h" #include "libknot/tsig-op.h" #include "libknot/packet/response.h" +#include "libknot/zone/zone-diff.h" static const size_t XFRIN_CHANGESET_BINARY_SIZE = 100; static const size_t XFRIN_CHANGESET_BINARY_STEP = 100; @@ -46,7 +47,9 @@ static const size_t XFRIN_BOOTSTRAP_DELAY = 60; /*!< AXFR bootstrap avg. delay * /* Forward declarations. */ static int zones_dump_zone_text(knot_zone_contents_t *zone, const char *zf); - +static int zones_dump_zone_binary(knot_zone_contents_t *zone, + const char *zonedb, + const char *zonefile); /*----------------------------------------------------------------------------*/ /*! @@ -117,11 +120,13 @@ static int zonedata_destroy(knot_zone_t *zone) /* Close IXFR db. */ journal_release(zd->ixfr_db); + + /* Free assigned config. */ + conf_free_zone(zd->conf); free(zd); /* Invalidate. */ - zone->dtor = 0; zone->data = 0; return KNOTD_EOK; @@ -172,7 +177,7 @@ static int zonedata_init(conf_zone_t *cfg, knot_zone_t *zone) char ebuf[256] = {0}; strerror_r(errno, ebuf, sizeof(ebuf)); log_server_warning("Couldn't open journal file for zone '%s', " - "disabling IXFR/IN. (%s)\n", cfg->name, ebuf); + "disabling incoming IXFR. (%s)\n", cfg->name, ebuf); } /* Initialize IXFR database syncing event. */ @@ -180,7 +185,7 @@ static int zonedata_init(conf_zone_t *cfg, knot_zone_t *zone) /* Set and install destructor. */ zone->data = zd; - zone->dtor = zonedata_destroy; + knot_zone_set_dtor(zone, zonedata_destroy); /* Set zonefile SOA serial. */ const knot_rrset_t *soa_rrs = 0; @@ -305,6 +310,12 @@ static int zones_expire_ev(event_t *e) zonedata_t *zd = (zonedata_t *)zone->data; rcu_read_lock(); + /* Check if zone is not discarded. */ + if (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED) { + rcu_read_unlock(); + return KNOTD_EOK; + } + /* Do not issue SOA query if transfer is pending. */ int locked = pthread_mutex_trylock(&zd->xfr_in.lock); if (locked != 0) { @@ -336,10 +347,12 @@ static int zones_expire_ev(event_t *e) pthread_mutex_unlock(&zd->xfr_in.lock); log_server_warning("Non-existent zone expired. Ignoring.\n"); rcu_read_unlock(); - return 0; + return KNOTD_EOK; } /* Publish expired zone. */ + /* Need to keep a reference in case zone get's deleted in meantime. */ + knot_zone_retain(zone); rcu_read_unlock(); synchronize_rcu(); rcu_read_lock(); @@ -368,7 +381,10 @@ static int zones_expire_ev(event_t *e) pthread_mutex_unlock(&zd->xfr_in.lock); rcu_read_unlock(); - return 0; + /* Release holding reference. */ + knot_zone_release(zone); + + return KNOTD_EOK; } /*! @@ -377,16 +393,23 @@ static int zones_expire_ev(event_t *e) static int zones_refresh_ev(event_t *e) { dbg_zones("zones: REFRESH or RETRY timer event\n"); + rcu_read_lock(); knot_zone_t *zone = (knot_zone_t *)e->data; if (zone == NULL || zone->data == NULL) { + rcu_read_unlock(); return KNOTD_EINVAL; } /* Cancel pending timers. */ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone); + + /* Check if zone is not discarded. */ + if (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED) { + rcu_read_unlock(); + return KNOTD_EOK; + } /* Check for contents. */ - rcu_read_lock(); if (!knot_zone_contents(zone)) { /* Bootstrap from XFR master. */ @@ -429,13 +452,20 @@ static int zones_refresh_ev(event_t *e) ++zd->xfr_in.scheduled; pthread_mutex_unlock(&zd->xfr_in.lock); + /* Retain pointer to zone for processing. */ + knot_zone_retain(xfr_req.zone); + /* Unlock zone contents. */ rcu_read_unlock(); /* Mark as finished to prevent stalling. */ evsched_event_finished(e->parent); + int ret = xfr_request(zd->server->xfr_h, &xfr_req); + if (ret != KNOTD_EOK) { + knot_zone_release(xfr_req.zone); /* Discard */ + } + return ret; - return xfr_request(zd->server->xfr_h, &xfr_req); } /* Do not issue SOA query if transfer is pending. */ @@ -485,17 +515,21 @@ static int zones_refresh_ev(event_t *e) size_t buflen = SOCKET_MTU_SZ; - /*! \todo [TSIG] CHANGE!!! only for compatibility now. */ - knot_ns_xfr_t xfr_req; - memset(&xfr_req, 0, sizeof(knot_ns_xfr_t)); - xfr_req.wire = qbuf; + knot_ns_xfr_t req; + memset(&req, 0, sizeof(knot_ns_xfr_t)); + req.wire = qbuf; + + /* Select TSIG key. */ + if (zd->xfr_in.tsig_key.name) { + xfr_prepare_tsig(&req, &zd->xfr_in.tsig_key); + } /* Create query. */ int sock = -1; char strbuf[256] = "Generic error."; const char *errstr = strbuf; sockaddr_t *master = &zd->xfr_in.master; - int ret = xfrin_create_soa_query(zone->name, &xfr_req, &buflen); + int ret = xfrin_create_soa_query(zone->name, &req, &buflen); if (ret == KNOT_EOK) { /* Create socket on random port. */ @@ -548,26 +582,30 @@ static int zones_refresh_ev(event_t *e) evsched_event_finished(e->parent); /* Watch socket. */ - knot_ns_xfr_t req; - memset(&req, 0, sizeof(req)); req.session = sock; req.type = XFR_TYPE_SOA; req.flags |= XFR_FLAG_UDP; req.zone = zone; + req.wire = NULL; memcpy(&req.addr, master, sizeof(sockaddr_t)); memcpy(&req.saddr, &zd->xfr_in.via, sizeof(sockaddr_t)); sockaddr_update(&req.addr); sockaddr_update(&req.saddr); + + /* Retain pointer to zone and issue. */ + knot_zone_retain(req.zone); if (ret == KNOTD_EOK) { ret = xfr_request(zd->server->xfr_h, &req); } if (ret != KNOTD_EOK) { + free(req.digest); + knot_zone_release(req.zone); /* Discard */ log_server_warning("Failed to issue SOA query for zone '%s' (%s).\n", zd->conf->name, errstr); } free(qbuf); - + /* Unlock RCU. */ rcu_read_unlock(); @@ -580,21 +618,28 @@ static int zones_refresh_ev(event_t *e) static int zones_notify_send(event_t *e) { dbg_notify("notify: NOTIFY timer event\n"); - + rcu_read_lock(); notify_ev_t *ev = (notify_ev_t *)e->data; if (ev == NULL) { + rcu_read_unlock(); log_zone_error("NOTIFY invalid event received\n"); return KNOTD_EINVAL; } + knot_zone_t *zone = ev->zone; if (zone == NULL || zone->data == NULL) { + rcu_read_unlock(); log_zone_error("NOTIFY invalid event data received\n"); evsched_event_free(e->parent, e); free(ev); return KNOTD_EINVAL; } - - rcu_read_lock(); + + /* Check if zone is not discarded. */ + if (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED) { + rcu_read_unlock(); /* Event will be freed on zonedata_destroy.*/ + return KNOTD_EOK; + } /* Check for answered/cancelled query. */ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone); @@ -675,7 +720,13 @@ static int zones_notify_send(event_t *e) req.zone = zone; memcpy(&req.addr, &ev->addr, sizeof(sockaddr_t)); memcpy(&req.saddr, &ev->saddr, sizeof(sockaddr_t)); - xfr_request(zd->server->xfr_h, &req); + + /* Retain pointer to zone and issue request. */ + knot_zone_retain(req.zone); + ret = xfr_request(zd->server->xfr_h, &req); + if (ret != KNOTD_EOK) { + knot_zone_release(req.zone); /* Discard */ + } } free(qbuf); @@ -724,6 +775,8 @@ static int zones_zonefile_sync_ev(event_t *e) journal_t *j = journal_retain(zd->ixfr_db); int ret = zones_zonefile_sync(zone, j); journal_release(j); + + rcu_read_lock(); if (ret == KNOTD_EOK) { log_zone_info("Applied differences of '%s' to zonefile.\n", zd->conf->name); @@ -732,13 +785,14 @@ static int zones_zonefile_sync_ev(event_t *e) "to zonefile.\n", zd->conf->name); } + rcu_read_unlock(); /* Reschedule. */ - conf_read_lock(); + rcu_read_lock(); evsched_schedule(e->parent, e, zd->conf->dbsync_timeout * 1000); dbg_zones("zones: next IXFR database SYNC of '%s' in %d seconds\n", zd->conf->name, zd->conf->dbsync_timeout); - conf_read_unlock(); + rcu_read_unlock(); return ret; } @@ -813,11 +867,12 @@ static int zones_set_acl(acl_t **acl, list* acl_list) static int zones_load_zone(knot_zone_t **dst, const char *zone_name, const char *source, const char *filename) { - if (dst == NULL) { + if (dst == NULL || zone_name == NULL || source == NULL) { return KNOTD_EINVAL; } *dst = NULL; + /* Duplicate zone name. */ size_t zlen = strlen(zone_name); char *zname = NULL; if (zlen > 0) { @@ -827,8 +882,13 @@ static int zones_load_zone(knot_zone_t **dst, const char *zone_name, } else { return KNOTD_EINVAL; } - zname[zlen - 1] = '\0'; /* Trim last dot */ + if (filename == NULL) { + log_server_error("No file name for zone '%s'.\n", zname); + free(zname); + return KNOTD_EINVAL; + } + /* Check if the compiled file still exists. */ struct stat st; @@ -841,109 +901,90 @@ static int zones_load_zone(knot_zone_t **dst, const char *zone_name, return KNOTD_EZONEINVAL; } - /* Check path */ - if (filename) { - dbg_zones("zones: parsing zone database '%s'\n", filename); - zloader_t *zl = 0; - int ret = knot_zload_open(&zl, filename); - switch(ret) { - case KNOT_EOK: - /* OK */ - break; - case KNOT_EACCES: - log_server_error("Failed to open compiled zone '%s' " - "(Permission denied).\n", filename); - free(zname); - return KNOTD_EZONEINVAL; - case KNOT_ENOENT: - log_server_error("Couldn't find compiled zone. " - "Please recompile '%s'.\n", zname); - free(zname); - return KNOTD_EZONEINVAL; - case KNOT_ECRC: - log_server_error("Compiled zone db CRC mismatch, " - "db is corrupted or .crc file is " - "deleted. Please recompile '%s'.\n", - zname); - free(zname); - return KNOTD_EZONEINVAL; - case KNOT_EMALF: - log_server_error("Compiled db '%s' is too old. " - "Please recompile '%s'.\n", - filename, zname); - free(zname); - return KNOTD_EZONEINVAL; - case KNOT_EFEWDATA: - case KNOT_ERROR: - case KNOT_ENOMEM: - default: - log_server_error("Failed to load compiled zone file " - "'%s'.\n", filename); - free(zname); - return KNOTD_EZONEINVAL; - } - - /* Check the source file */ - int src_changed = strcmp(source, zl->source) != 0; - if (src_changed || knot_zload_needs_update(zl)) { - log_server_warning("Database for zone '%s' is not " - "up-to-date. Please recompile.\n", - zname); - } - - *dst = knot_zload_load(zl); - - /* Check loaded name. */ - const knot_dname_t *dname = knot_zone_name(*dst); - knot_dname_t *dname_req = 0; - dname_req = knot_dname_new_from_str(zone_name, zlen, 0); - if (knot_dname_compare(dname, dname_req) != 0) { - log_server_warning("Origin of the zone db file is " - "different than '%s'\n", - zone_name); - knot_zone_deep_free(dst, 0); - - } - knot_dname_free(&dname_req); - - /* CLEANUP */ -// knot_zone_contents_dump(zone->contents, 1); -// int errs = knot_zone_contents_integrity_check(zone->contents); -// fprintf(stderr, "INTEGRITY CHECK OF ZONE. ERRORS: %d\n", errs); - - if (*dst != NULL) { - /* save the timestamp from the zone db file */ - struct stat s; - if (stat(filename, &s) < 0) { - dbg_zones("zones: failed to stat() zone db, " - "something is seriously wrong\n"); - knot_zone_deep_free(dst, 0); - } else { - knot_zone_set_version(*dst, s.st_mtime); - } - } - + /* Attempt to open compiled zone for loading. */ + int ret = KNOTD_EOK; + zloader_t *zl = NULL; + dbg_zones("zones: parsing zone database '%s'\n", filename); + switch(knot_zload_open(&zl, filename)) { + case KNOT_EOK: + /* OK */ + break; + case KNOT_EACCES: + log_server_error("Failed to open compiled zone '%s' " + "(Permission denied).\n", filename); + free(zname); + return KNOTD_EZONEINVAL; + case KNOT_ENOENT: + log_server_error("Couldn't find compiled zone. " + "Please recompile '%s'.\n", zname); + free(zname); + return KNOTD_EZONEINVAL; + case KNOT_ECRC: + log_server_error("Compiled zone db CRC mismatch, " + "db is corrupted or .crc file is " + "deleted. Please recompile '%s'.\n", + zname); + free(zname); + return KNOTD_EZONEINVAL; + case KNOT_EMALF: + log_server_error("Compiled db '%s' is too old. " + "Please recompile '%s'.\n", + filename, zname); + free(zname); + return KNOTD_EZONEINVAL; + case KNOT_EFEWDATA: + case KNOT_ERROR: + case KNOT_ENOMEM: + default: + log_server_error("Failed to load compiled zone file " + "'%s'.\n", filename); + free(zname); + return KNOTD_EZONEINVAL; + } + + /* Check the source file */ + assert(zl != NULL); + int src_changed = strcmp(source, zl->source) != 0; + if (src_changed || knot_zload_needs_update(zl)) { + log_server_warning("Database for zone '%s' is not " + "up-to-date. Please recompile.\n", + zname); + } + + *dst = knot_zload_load(zl); + if (*dst == NULL) { + log_server_error("Failed to load db '%s' for zone '%s'.\n", + filename, zname); knot_zload_close(zl); - - if (*dst == NULL) { - log_server_error("Failed to load " - "db '%s' for zone '%s'.\n", - filename, zname); - free(zname); - return KNOTD_EZONEINVAL; - } - } else { - /* db is null. */ - log_server_error("No file name for zone '%s'.\n", zname); free(zname); - return KNOTD_EINVAL; + return KNOTD_EZONEINVAL; } - - /* CLEANUP */ -// knot_zone_dump(zone, 1); + + /* Check if loaded origin matches. */ + const knot_dname_t *dname = knot_zone_name(*dst); + knot_dname_t *dname_req = NULL; + dname_req = knot_dname_new_from_str(zone_name, zlen, 0); + if (knot_dname_compare(dname, dname_req) != 0) { + log_server_error("Origin of the zone db file is " + "different than '%s'\n", + zone_name); + knot_zone_deep_free(dst, 0); + ret = KNOTD_EZONEINVAL; + } else { + /* Save the timestamp from the zone db file. */ + if (stat(filename, &st) < 0) { + dbg_zones("zones: failed to stat() zone db, " + "something is seriously wrong\n"); + knot_zone_deep_free(dst, 0); + ret = KNOTD_EZONEINVAL; + } else { + knot_zone_set_version(*dst, st.st_mtime); + } + } + knot_dname_free(&dname_req); + knot_zload_close(zl); free(zname); - - return KNOTD_EOK; + return ret; } /*----------------------------------------------------------------------------*/ @@ -1130,11 +1171,11 @@ static int zones_load_changesets(const knot_zone_t *zone, dbg_zones_detail("Bad arguments: zd->ixfr_db=%p\n", zone->data); return KNOTD_EINVAL; } - - conf_read_lock(); + + rcu_read_lock(); dbg_xfr("xfr: loading changesets for zone '%s' from serial %u to %u\n", zd->conf->name, from, to); - conf_read_unlock(); + rcu_read_unlock(); /* Retain journal for changeset loading. */ journal_t *j = journal_retain(zd->ixfr_db); @@ -1167,7 +1208,7 @@ static int zones_load_changesets(const knot_zone_t *zone, ret = knot_changesets_check_size(dst); --dst->count; if (ret != KNOT_EOK) { - --dst->count; + //--dst->count; dbg_xfr("xfr: failed to check changesets size: %s\n", knot_strerror(ret)); journal_release(j); @@ -1211,10 +1252,10 @@ static int zones_load_changesets(const knot_zone_t *zone, /* Unpack binary data. */ int unpack_ret = zones_changesets_from_binary(dst); - if (unpack_ret != KNOT_EOK) { + if (unpack_ret != KNOTD_EOK) { dbg_xfr("xfr: failed to unpack changesets " - "from binary, %s\n", knot_strerror(unpack_ret)); - return KNOTD_ERROR; + "from binary, %s\n", knotd_strerror(unpack_ret)); + return unpack_ret; } /* Check for complete history. */ @@ -1288,7 +1329,7 @@ static int zones_journal_apply(knot_zone_t *zone) &contents); if (apply_ret != KNOT_EOK) { log_server_error("Failed to apply changesets to" - "'%s' - Apply failed: %s\n", + " '%s' - Apply failed: %s\n", zd->conf->name, knot_strerror(apply_ret)); ret = KNOTD_ERROR; @@ -1297,27 +1338,27 @@ static int zones_journal_apply(knot_zone_t *zone) xfrin_rollback_update(zone->contents, &contents, &chsets->changes); - } - - /* Switch zone immediately. */ - rcu_read_unlock(); - apply_ret = xfrin_switch_zone(zone, contents, - XFR_TYPE_IIN); - rcu_read_lock(); - if (apply_ret == KNOT_EOK) { - xfrin_cleanup_successful_update( - &chsets->changes); } else { - log_server_error("Failed to apply changesets to" - " '%s' - Switch failed: %s\n", - zd->conf->name, - knot_strerror(apply_ret)); - ret = KNOTD_ERROR; - - // Cleanup old and new contents - xfrin_rollback_update(zone->contents, - &contents, - &chsets->changes); + /* Switch zone immediately. */ + rcu_read_unlock(); + apply_ret = xfrin_switch_zone(zone, contents, + XFR_TYPE_IIN); + rcu_read_lock(); + if (apply_ret == KNOT_EOK) { + xfrin_cleanup_successful_update( + &chsets->changes); + } else { + log_server_error("Failed to apply " + "changesets to '%s' - Switch failed: " + "%s\n", zd->conf->name, + knot_strerror(apply_ret)); + ret = KNOTD_ERROR; + + // Cleanup old and new contents + xfrin_rollback_update(zone->contents, + &contents, + &chsets->changes); + } } } } else { @@ -1350,9 +1391,9 @@ static int zones_journal_apply(knot_zone_t *zone) * \retval KNOTD_ERROR on unspecified error. */ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, - knot_nameserver_t *ns, const knot_zonedb_t *db_old) + knot_nameserver_t *ns) { - if (z == NULL || dst == NULL || ns == NULL || db_old == NULL) { + if (z == NULL || dst == NULL || ns == NULL) { return KNOTD_EINVAL; } @@ -1367,17 +1408,19 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, } /* Try to find the zone in the current zone db. */ - knot_zone_t *zone = knot_zonedb_find_zone(db_old, dname); + rcu_read_lock(); + knot_zone_t *zone = knot_zonedb_find_zone(ns->zone_db, dname); + rcu_read_unlock(); /* Attempt to bootstrap if db or source does not exist. */ int zone_changed = 0; struct stat s = {}; int stat_ret = stat(z->file, &s); - if (zone != NULL && stat_ret == 0) { + if (zone != NULL) { /* if found, check timestamp of the file against the * loaded zone */ - if (knot_zone_version(zone) < s.st_mtime) { + if (stat_ret == 0 && knot_zone_version(zone) < s.st_mtime) { zone_changed = 1; } } else { @@ -1385,6 +1428,7 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, } /* Reload zone file. */ + int is_bootstrapped = 0; int ret = KNOTD_ERROR; if (zone_changed) { /* Zone file not exists and has master set. */ @@ -1398,9 +1442,7 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, zone = knot_zone_new_empty(owner); if (zone != NULL) { ret = KNOTD_EOK; - log_server_info("Will attempt to bootstrap zone" - " %s from AXFR master.\n", - z->name); + is_bootstrapped = 1; } else { dbg_zones("zones: failed to create " "stub zone '%s'.\n", z->name); @@ -1410,9 +1452,21 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, dbg_zones_verb("zones: loading zone '%s' from '%s'\n", z->name, z->db); ret = zones_load_zone(&zone, z->name, z->file, z->db); + const knot_node_t *apex = NULL; + const knot_rrset_t *soa = NULL; if (ret == KNOTD_EOK) { - log_server_info("Loaded zone '%s'\n", - z->name); + apex = knot_zone_contents_apex( + knot_zone_contents(zone)); + soa = knot_node_rrset(apex, + KNOT_RRTYPE_SOA); + int64_t sn = 0; + if (apex && soa) { + sn = knot_rdata_soa_serial( + knot_rrset_rdata(soa)); + if (sn < 0) sn = 0; + } + log_server_info("Loaded zone '%s' serial %u\n", + z->name, (uint32_t)sn); } } @@ -1425,7 +1479,6 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, /* Initialize zone-related data. */ zonedata_init(z, zone); *dst = zone; - } } else { dbg_zones_verb("zones: found '%s' in old database, " @@ -1442,9 +1495,20 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, if (zone != NULL) { zonedata_t *zd = (zonedata_t *)knot_zone_data(zone); assert(zd != NULL); + + /* Log bootstrapped zone. */ + if (is_bootstrapped) { + log_server_info("Will attempt to bootstrap zone" + " %s from AXFR master in %us.\n", + z->name, + zd->xfr_in.bootstrap_retry / 1000); + } /* Update refs. */ - zd->conf = z; + if (zd->conf != z) { + conf_free_zone(zd->conf); + zd->conf = z; + } /* Update ACLs. */ dbg_zones("Updating zone ACLs.\n"); @@ -1486,7 +1550,13 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, } /* Apply changesets from journal. */ - zones_journal_apply(zone); + int ar = zones_journal_apply(zone); + if (ar != KNOTD_EOK && ar != KNOTD_ERANGE && ar != KNOTD_ENOENT) { + log_server_warning("Failed to apply changesets " + "for zone '%s': %s\n", + z->name, knotd_strerror(ar)); + } + /* Update events scheduled for zone. */ evsched_t *sch = ((server_t *)knot_ns_get_data(ns))->sched; @@ -1526,10 +1596,32 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, rcu_read_unlock(); } + + /* Calculate differences. */ + rcu_read_lock(); + knot_zone_t *z_old = knot_zonedb_find_zone(ns->zone_db, + dname); + /* Ensure both new and old have zone contents. */ + knot_zone_contents_t *zc = knot_zone_get_contents(zone); + knot_zone_contents_t *zc_old = knot_zone_get_contents(z_old); + if (z->build_diffs && zc != NULL && zc_old != NULL && zone_changed) { + int bd = zones_create_and_save_changesets(z_old, zone); + if (bd == KNOTD_ENODIFF) { + log_zone_warning("Zone file for '%s' changed, " + "but serial didn't - " + "won't create changesets.\n", + z->name); + } else if (bd != KNOTD_EOK) { + log_zone_warning("Failed to calculate differences" + " from the zone file update: " + "%s\n", knotd_strerror(bd)); + } + } + rcu_read_unlock(); } /* CLEANUP */ -// knot_zone_contents_dump(knot_zone_get_contents(zone), 1); +// knot_zone_contents_dump(knot_zone_get_contents(zone), 1); /* Directly discard zone. */ knot_dname_free(&dname); @@ -1539,8 +1631,7 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, /*! \brief Structure for multithreaded zone loading. */ struct zonewalk_t { knot_nameserver_t *ns; - const knot_zonedb_t *db_old; - knot_zonedb_t *db_new; + knot_zonedb_t *db_new; pthread_mutex_t lock; int inserted; unsigned qhead; @@ -1581,8 +1672,7 @@ static int zonewalker(dthread_t *thread) continue; } - int ret = zones_insert_zone(zw->q[i], zones + inserted, zw->ns, - zw->db_old); + int ret = zones_insert_zone(zw->q[i], zones + inserted, zw->ns); if (ret == KNOTD_EOK) { ++inserted; } @@ -1592,11 +1682,15 @@ static int zonewalker(dthread_t *thread) pthread_mutex_lock(&zw->lock); zw->inserted += inserted; for (int i = 0; i < inserted; ++i) { + zonedata_t *zd = (zonedata_t *)knot_zone_data(zones[i]); if (knot_zonedb_add_zone(zw->db_new, zones[i]) != KNOT_EOK) { - zonedata_t *zd = (zonedata_t *)knot_zone_data(zones[i]); log_server_error("Failed to insert zone '%s' " "into database.\n", zd->conf->name); knot_zone_deep_free(zones + i, 0); + } else { + /* Unlink zone config from conf(), + * transferring ownership to zonedata. */ + rem_node(&zd->conf->n); } } pthread_mutex_unlock(&zw->lock); @@ -1613,14 +1707,12 @@ static int zonewalker(dthread_t *thread) * * \param ns Name server instance. * \param zone_conf Zone configuration. - * \param db_old Old zone database. * \param db_new New zone database. * * \return Number of inserted zones. */ static int zones_insert_zones(knot_nameserver_t *ns, const list *zone_conf, - const knot_zonedb_t *db_old, knot_zonedb_t *db_new) { int inserted = 0; @@ -1636,7 +1728,6 @@ static int zones_insert_zones(knot_nameserver_t *ns, if (zw != NULL) { memset(zw, 0, zwlen); zw->ns = ns; - zw->db_old = db_old; zw->db_new = db_new; zw->inserted = 0; if (pthread_mutex_init(&zw->lock, NULL) < 0) { @@ -1713,6 +1804,18 @@ dbg_zones_exec( "from database.\n", name); free(name); ); + /* Invalidate ACLs - since we would need to copy each + * remote data and keep ownership, I think it's no harm + * to drop all ACLs for the discarded zone. + * refs #1976 */ + zonedata_t *zd = (zonedata_t*)knot_zone_data(old_zone); + conf_zone_t *zconf = zd->conf; + WALK_LIST_FREE(zconf->acl.xfr_in); + WALK_LIST_FREE(zconf->acl.xfr_out); + WALK_LIST_FREE(zconf->acl.notify_in); + WALK_LIST_FREE(zconf->acl.notify_out); + + /* Remove from zone db. */ knot_zone_t * rm = knot_zonedb_remove_zone(db_old, knot_zone_name(old_zone)); assert(rm == old_zone); @@ -1758,8 +1861,10 @@ static int zones_verify_tsig_query(const knot_packet_t *query, /* * 2) Find the particular key used by the TSIG. + * Check not only name, but also the algorithm. */ - if (key && kname && knot_dname_compare(key->name, kname) == 0) { + if (key && kname && knot_dname_compare(key->name, kname) == 0 + && key->algorithm == alg) { dbg_zones_verb("Found claimed TSIG key for comparison\n"); } else { *rcode = KNOT_RCODE_NOTAUTH; @@ -1848,18 +1953,19 @@ static int zones_check_tsig_query(const knot_zone_t *zone, assert(rcode != NULL); assert(tsig_key_zone != NULL); - const knot_rrset_t *tsig = NULL; + const knot_rrset_t *tsig = knot_packet_tsig(query); - if (knot_packet_additional_rrset_count(query) > 0) { - /*! \todo warning */ - tsig = knot_packet_additional_rrset(query, - knot_packet_additional_rrset_count(query) - 1); - if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) { - dbg_zones_verb("found TSIG in normal query\n"); - } else { - tsig = NULL; /* Invalidate if not TSIG RRTYPE. */ - } - } + // not required, TSIG is already found +// if (knot_packet_additional_rrset_count(query) > 0) { +// /*! \todo warning */ +// tsig = knot_packet_additional_rrset(query, +// knot_packet_additional_rrset_count(query) - 1); +// if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) { +// dbg_zones_verb("found TSIG in normal query\n"); +// } else { +// tsig = NULL; /* Invalidate if not TSIG RRTYPE. */ +// } +// } if (tsig == NULL) { // no TSIG, this is completely valid @@ -1894,7 +2000,7 @@ static int zones_check_tsig_query(const knot_zone_t *zone, } // save TSIG RR to query structure - knot_packet_set_tsig(query, tsig); +// knot_packet_set_tsig(query, tsig); return ret; } @@ -1913,34 +2019,39 @@ int zones_update_db_from_config(const conf_t *conf, knot_nameserver_t *ns, /* Lock RCU to ensure none will deallocate any data under our hands. */ rcu_read_lock(); - + /* Grab a pointer to the old database */ - *db_old = ns->zone_db; - if (*db_old == NULL) { + if (ns->zone_db == NULL) { + rcu_read_unlock(); log_server_error("Missing zone database in nameserver structure" ".\n"); - rcu_read_unlock(); return KNOTD_ERROR; } + rcu_read_unlock(); /* Create new zone DB */ knot_zonedb_t *db_new = knot_zonedb_new(); if (db_new == NULL) { - rcu_read_unlock(); return KNOTD_ERROR; } log_server_info("Loading %d compiled zones...\n", conf->zones_count); /* Insert all required zones to the new zone DB. */ - int inserted = zones_insert_zones(ns, &conf->zones, *db_old, db_new); - + /*! \warning RCU must not be locked as some contents switching will + be required. */ + int inserted = zones_insert_zones(ns, &conf->zones, db_new); + log_server_info("Loaded %d out of %d zones.\n", inserted, conf->zones_count); if (inserted != conf->zones_count) { log_server_warning("Not all the zones were loaded.\n"); } + + /* Lock RCU to ensure none will deallocate any data under our hands. */ + rcu_read_lock(); + *db_old = ns->zone_db; dbg_zones_detail("zones: old db in nameserver: %p, old db stored: %p, " "new db: %p\n", ns->zone_db, *db_old, db_new); @@ -2020,19 +2131,29 @@ int zones_zonefile_sync(knot_zone_t *zone, journal_t *journal) if (zd->zonefile_serial != serial_to) { /* Save zone to zonefile. */ - conf_read_lock(); dbg_zones("zones: syncing '%s' differences to '%s' " "(SOA serial %u)\n", zd->conf->name, zd->conf->file, serial_to); ret = zones_dump_zone_text(contents, zd->conf->file); if (ret != KNOTD_EOK) { - dbg_zones("zones: failed to sync '%s' to '%s'\n", - zd->conf->name, zd->conf->file); + log_zone_warning("Failed to apply differences " + "'%s' to '%s'\n", + zd->conf->name, zd->conf->file); pthread_mutex_unlock(&zd->lock); rcu_read_unlock(); return ret; } - conf_read_unlock(); + + /* Save zone to binary db file. */ + ret = zones_dump_zone_binary(contents, zd->conf->db, zd->conf->file); + if (ret != KNOTD_EOK) { + log_zone_warning("Failed to apply differences " + "'%s' to '%s'\n", + zd->conf->name, zd->conf->db); + pthread_mutex_unlock(&zd->lock); + rcu_read_unlock(); + return KNOTD_ERROR; + } /* Update journal entries. */ dbg_zones_verb("zones: unmarking all dirty nodes " @@ -2144,15 +2265,16 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, ? *rsize : 0); // check for TSIG in the query - if (knot_packet_additional_rrset_count(query) > 0) { - /*! \todo warning */ - const knot_rrset_t *tsig = knot_packet_additional_rrset(query, - knot_packet_additional_rrset_count(query) - 1); - if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) { - dbg_zones_verb("found TSIG in normal query\n"); - knot_packet_set_tsig(query, tsig); - } - } + // not required, TSIG is already found if it is there +// if (knot_packet_additional_rrset_count(query) > 0) { +// /*! \todo warning */ +// const knot_rrset_t *tsig = knot_packet_additional_rrset(query, +// knot_packet_additional_rrset_count(query) - 1); +// if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) { +// dbg_zones_verb("found TSIG in normal query\n"); +// knot_packet_set_tsig(query, tsig); +// } +// } knot_rcode_t rcode = 0; @@ -2170,7 +2292,10 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, break; } - if (zone == NULL && knot_packet_tsig(query) == NULL) { + if (rcode == KNOT_RCODE_NOERROR + && ((zone == NULL && knot_packet_tsig(query) == NULL) + || (knot_packet_qclass(query) != KNOT_CLASS_IN + && knot_packet_qclass(query) != KNOT_CLASS_ANY))) { /*! \todo If there is TSIG, this should be probably handled * as a key error. */ @@ -2181,10 +2306,9 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, dbg_zones_verb("Failed preparing response structure: %s.\n", knot_strerror(rcode)); if (resp == NULL) { - knot_ns_error_response(nameserver, - knot_packet_id(query), - &query->header.flags1, - rcode, resp_wire, rsize); + knot_ns_error_response_from_query(nameserver, query, + rcode, resp_wire, + rsize); rcu_read_unlock(); return KNOT_EOK; } @@ -2198,7 +2322,8 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, assert(rcode == KNOT_RCODE_NOERROR); uint16_t tsig_rcode = 0; knot_key_t *tsig_key_zone = NULL; - uint64_t tsig_prev_time_signed = 0; /*! \todo Verify, as it was uninitialized! */ + uint64_t tsig_prev_time_signed = 0; + /*! \todo Verify, as it was uninitialized! */ size_t answer_size = *rsize; int ret = KNOT_EOK; @@ -2206,6 +2331,7 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, if (zone == NULL) { assert(knot_packet_tsig(query) != NULL); // treat as BADKEY error + /*! \todo Is this OK?? */ rcode = KNOT_RCODE_NOTAUTH; tsig_rcode = KNOT_TSIG_RCODE_BADKEY; ret = KNOT_TSIG_EBADKEY; @@ -2228,8 +2354,20 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, tsig_wire_maxsize(tsig_key_zone); knot_packet_set_tsig_size(resp, tsig_max_size); } - ret = knot_ns_answer_normal(nameserver, zone, resp, - resp_wire, &answer_size); + + // handle IXFR queries + if (knot_packet_qtype(query) == KNOT_RRTYPE_IXFR) { + assert(transport == NS_TRANSPORT_UDP); + ret = knot_ns_answer_ixfr_udp(nameserver, zone, + resp, resp_wire, + &answer_size); + } else { + ret = knot_ns_answer_normal(nameserver, zone, + resp, resp_wire, + &answer_size, + transport == + NS_TRANSPORT_UDP); + } dbg_zones_detail("rsize = %zu\n", *rsize); dbg_zones_detail("answer_size = %zu\n", answer_size); @@ -2334,6 +2472,9 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, tsig_rdata_mac_length(tsig), digest, &digest_size, tsig_key_zone, tsig_rcode, tsig_prev_time_signed); + + // no need to keep the digest + free(digest); *rsize = answer_size; } else { @@ -2385,6 +2526,7 @@ int zones_process_response(knot_nameserver_t *nameserver, } /* Find matching zone and ID. */ + rcu_read_lock(); const knot_dname_t *zone_name = knot_packet_qname(packet); /*! \todo Change the access to the zone db. */ knot_zone_t *zone = knot_zonedb_find_zone( @@ -2392,7 +2534,6 @@ int zones_process_response(knot_nameserver_t *nameserver, zone_name); /* Get zone contents. */ - rcu_read_lock(); const knot_zone_contents_t *contents = knot_zone_contents(zone); @@ -2423,17 +2564,10 @@ int zones_process_response(knot_nameserver_t *nameserver, evsched_t *sched = ((server_t *)knot_ns_get_data(nameserver))->sched; if (ret == 0) { - char r_addr[SOCKADDR_STRLEN]; - int r_port = sockaddr_portnum(from); - sockaddr_tostr(from, r_addr, sizeof(r_addr)); - log_zone_info("SOA query of '%s' to '%s@%d': Answered, no " - "transfer needed.\n", - zd->conf->name, r_addr, r_port); - /* Reinstall timers. */ zones_timers_update(zone, zd->conf, sched); rcu_read_unlock(); - return KNOTD_EOK; + return KNOTD_EUPTODATE; } assert(ret > 0); @@ -2471,9 +2605,13 @@ int zones_process_response(knot_nameserver_t *nameserver, /* Unlock zone contents. */ rcu_read_unlock(); - /* Enqueue XFR request. */ - return xfr_request(((server_t *)knot_ns_get_data( - nameserver))->xfr_h, &xfr_req); + /* Retain pointer to zone for processing. */ + knot_zone_retain(xfr_req.zone); + ret = xfr_request(((server_t *)knot_ns_get_data( + nameserver))->xfr_h, &xfr_req); + if (ret != KNOTD_EOK) { + knot_zone_release(xfr_req.zone); /* Discard */ + } } return KNOTD_EOK; @@ -2492,45 +2630,6 @@ knot_ns_xfr_type_t zones_transfer_to_use(zonedata_t *data) /*----------------------------------------------------------------------------*/ -static int zones_find_zone_for_xfr(const knot_zone_contents_t *zone, - const char **zonefile, const char **zonedb) -{ - /* find the zone file name and zone db file name for the zone */ - conf_t *cnf = conf(); - node *n = NULL; - WALK_LIST(n, cnf->zones) { - conf_zone_t *zone_conf = (conf_zone_t *)n; - knot_dname_t *zone_name = knot_dname_new_from_str( - zone_conf->name, strlen(zone_conf->name), NULL); - if (zone_name == NULL) { - return KNOTD_ENOMEM; - } - - int r = knot_dname_compare(zone_name, knot_node_owner( - knot_zone_contents_apex(zone))); - - /* Directly discard dname, won't be needed. */ - knot_dname_free(&zone_name); - - if (r == 0) { - /* found the right zone */ - *zonefile = zone_conf->file; - *zonedb = zone_conf->db; - return KNOTD_EOK; - } - } - - char *name = knot_dname_to_str(knot_node_owner( - knot_zone_contents_apex(zone))); - dbg_zones("zones: no zone found for the zone received by transfer " - "(%s).\n", name); - free(name); - - return KNOTD_ENOENT; -} - -/*----------------------------------------------------------------------------*/ - static int zones_open_free_filename(const char *old_name, char **new_name) { /* find zone name not present on the disk */ @@ -2571,7 +2670,6 @@ static int zones_dump_zone_text(knot_zone_contents_t *zone, const char *fname) FILE *f = fdopen(fd, "w"); if (f == NULL) { log_zone_warning("Failed to open file descriptor for text zone.\n"); - close(fd); unlink(new_fname); free(new_fname); return KNOTD_ERROR; @@ -2580,11 +2678,14 @@ static int zones_dump_zone_text(knot_zone_contents_t *zone, const char *fname) if (zone_dump_text(zone, f) != KNOTD_EOK) { log_zone_warning("Failed to save the transferred zone to '%s'.\n", new_fname); - close(fd); + fclose(f); unlink(new_fname); free(new_fname); return KNOTD_ERROR; } + + /* Set zone file rights to 0640. */ + fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); /* Swap temporary zonefile and new zonefile. */ fclose(f); @@ -2596,8 +2697,7 @@ static int zones_dump_zone_text(knot_zone_contents_t *zone, const char *fname) free(new_fname); return KNOTD_ERROR; } - - + free(new_fname); return KNOTD_EOK; } @@ -2619,18 +2719,23 @@ static int zones_dump_zone_binary(knot_zone_contents_t *zone, return KNOTD_ERROR; } - crc_t crc_value; + crc_t crc_value = 0; if (knot_zdump_dump(zone, fd, zonefile, &crc_value) != KNOT_EOK) { close(fd); unlink(new_zonedb); free(new_zonedb); return KNOTD_ERROR; } + + /* Set compiled zone rights to 0640. */ + fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + + /* Close compiled zone. */ + close(fd); /* Delete old CRC file. */ char *zonedb_crc = knot_zdump_crc_file(zonedb); if (zonedb_crc == NULL) { - close(fd); unlink(new_zonedb); free(new_zonedb); return KNOTD_ENOMEM; @@ -2643,7 +2748,6 @@ static int zones_dump_zone_binary(knot_zone_contents_t *zone, dbg_zdump("Failed to create CRC file path from %s.\n", new_zonedb); free(zonedb_crc); - close(fd); unlink(new_zonedb); free(new_zonedb); return KNOTD_ENOMEM; @@ -2654,13 +2758,18 @@ static int zones_dump_zone_binary(knot_zone_contents_t *zone, if (f_crc == NULL) { dbg_zdump("Cannot open CRC file %s!\n", zonedb_crc); + free(zonedb_crc); unlink(new_zonedb); + free(new_zonedb); return KNOTD_ERROR; } else { fprintf(f_crc, "%lu\n", (unsigned long)crc_value); fclose(f_crc); } + + /* Set CRC file rights to 0640. */ + chmod(new_zonedb_crc, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); /* Swap CRC files. */ int ret = KNOTD_EOK; @@ -2689,7 +2798,6 @@ static int zones_dump_zone_binary(knot_zone_contents_t *zone, free(new_zonedb_crc); free(zonedb_crc); - close(fd); free(new_zonedb); @@ -2700,33 +2808,48 @@ static int zones_dump_zone_binary(knot_zone_contents_t *zone, int zones_save_zone(const knot_ns_xfr_t *xfr) { - if (xfr == NULL || xfr->new_contents == NULL) { + if (xfr == NULL || xfr->new_contents == NULL || xfr->zone == NULL) { return KNOTD_EINVAL; } - knot_zone_contents_t *zone = xfr->new_contents; - - const char *zonefile = NULL; - const char *zonedb = NULL; + rcu_read_lock(); - int ret = zones_find_zone_for_xfr(zone, &zonefile, &zonedb); - if (ret != KNOTD_EOK) { - return ret; + zonedata_t *zd = (zonedata_t *)knot_zone_data(xfr->zone); + knot_zone_contents_t *new_zone = xfr->new_contents; + + const char *zonefile = zd->conf->file; + const char *zonedb = zd->conf->db; + + /* Check if the new zone apex dname matches zone name. */ + knot_dname_t *cur_name = knot_dname_new_from_str(zd->conf->name, + strlen(zd->conf->name), + NULL); + const knot_dname_t *new_name = NULL; + new_name = knot_node_owner(knot_zone_contents_apex(new_zone)); + int r = knot_dname_compare(cur_name, new_name); + knot_dname_free(&cur_name); + if (r != 0) { + rcu_read_unlock(); + return KNOTD_EINVAL; } assert(zonefile != NULL && zonedb != NULL); /* dump the zone into text zone file */ - ret = zones_dump_zone_text(zone, zonefile); + int ret = zones_dump_zone_text(new_zone, zonefile); if (ret != KNOTD_EOK) { + rcu_read_unlock(); return KNOTD_ERROR; } /* dump the zone into binary db file */ - ret = zones_dump_zone_binary(zone, zonedb, zonefile); + ret = zones_dump_zone_binary(new_zone, zonedb, zonefile); if (ret != KNOTD_EOK) { + rcu_read_unlock(); return KNOTD_ERROR; } + rcu_read_unlock(); + return KNOTD_EOK; } @@ -2970,10 +3093,10 @@ static int zones_store_changeset(const knot_changeset_t *chs, journal_t *j, /* Reschedule sync timer. */ if (tmr) { /* Fetch sync timeout. */ - conf_read_lock(); + rcu_read_lock(); int timeout = zd->conf->dbsync_timeout; timeout *= 1000; /* Convert to ms. */ - conf_read_unlock(); + rcu_read_unlock(); /* Reschedule. */ dbg_xfr_verb("xfr: resuming SYNC " @@ -3145,6 +3268,79 @@ int zones_xfr_load_changesets(knot_ns_xfr_t *xfr, uint32_t serial_from, /*----------------------------------------------------------------------------*/ +int zones_create_and_save_changesets(const knot_zone_t *old_zone, + const knot_zone_t *new_zone) +{ + if (old_zone == NULL || old_zone->contents == NULL + || new_zone == NULL || new_zone->contents == NULL) { + dbg_zones("zones: create_changesets: " + "NULL arguments.\n"); + return KNOTD_EINVAL; + } + + knot_ns_xfr_t xfr; + memset(&xfr, 0, sizeof(xfr)); + xfr.zone = (knot_zone_t *)old_zone; + knot_changesets_t *changesets; + int ret = knot_zone_diff_create_changesets(old_zone->contents, + new_zone->contents, + &changesets); + if (ret != KNOT_EOK) { + if (ret == KNOT_ERANGE) { + dbg_zones_detail("zones: create_changesets: " + "New serial was lower than the old " + "one.\n"); + knot_free_changesets(&changesets); + return KNOTD_ERANGE; + } else if (ret == KNOT_ENODIFF) { + dbg_zones_detail("zones: create_changesets: " + "New serial was the same as the old " + "one.\n"); + knot_free_changesets(&changesets); + return KNOTD_ENODIFF; + } else { + dbg_zones("zones: create_changesets: " + "Could not create changesets. Reason: %s\n", + knot_strerror(ret)); + knot_free_changesets(&changesets); + return KNOTD_ERROR; + } + } + + xfr.data = changesets; + journal_t *journal = zones_store_changesets_begin(&xfr); + if (journal == NULL) { + dbg_zones("zones: create_changesets: " + "Could not start journal operation.\n"); + return KNOTD_ERROR; + } + + ret = zones_store_changesets(&xfr); + if (ret != KNOTD_EOK) { + zones_store_changesets_rollback(journal); + dbg_zones("zones: create_changesets: " + "Could not store in the journal. Reason: %s.\n", + knotd_strerror(ret)); + + return ret; + } + + ret = zones_store_changesets_commit(journal); + if (ret != KNOTD_EOK) { + dbg_zones("zones: create_changesets: " + "Could not commit to journal. Reason: %s.\n", + knotd_strerror(ret)); + + return ret; + } + + knot_free_changesets(&changesets); + + return KNOTD_EOK; +} + +/*----------------------------------------------------------------------------*/ + int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch) { if (!sch || !zone) { @@ -3180,7 +3376,7 @@ int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch) pthread_mutex_unlock(&zd->lock); /* Check XFR/IN master server. */ - conf_read_lock(); + rcu_read_lock(); if (zd->xfr_in.master.ptr) { /* Schedule REFRESH timer. */ @@ -3198,13 +3394,12 @@ int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch) /* Do not issue NOTIFY queries if stub. */ if (!knot_zone_contents(zone)) { - conf_read_unlock(); + rcu_read_unlock(); return KNOTD_EOK; } /* Schedule NOTIFY to slaves. */ conf_remote_t *r = 0; - conf_read_lock(); WALK_LIST(r, cfzone->acl.notify_out) { /* Fetch remote. */ @@ -3257,7 +3452,7 @@ int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch) tmr_s, cfg_if->address, cfg_if->port); } - conf_read_unlock(); + rcu_read_unlock(); return KNOTD_EOK; } @@ -3275,7 +3470,10 @@ int zones_cancel_notify(zonedata_t *zd, notify_ev_t *ev) event_t *tmr = ev->timer; ev->timer = 0; pthread_mutex_unlock(&zd->lock); - evsched_cancel(tmr->parent, tmr); + if (evsched_cancel(tmr->parent, tmr) == 0) { + dbg_notify("notify: NOTIFY event %p designated for cancellation " + "not found\n", tmr); + } /* Re-lock and find again (if not deleted). */ pthread_mutex_lock(&zd->lock); diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h index bb95b93..65e5a61 100644..100755 --- a/src/knot/server/zones.h +++ b/src/knot/server/zones.h @@ -59,7 +59,7 @@ typedef struct zonedata_t /*! \brief Zone data lock for exclusive access. */ pthread_mutex_t lock; - + /*! \brief Access control lists. */ acl_t *xfr_out; /*!< ACL for xfr-out.*/ acl_t *notify_in; /*!< ACL for notify-in.*/ @@ -272,6 +272,23 @@ int zones_xfr_load_changesets(knot_ns_xfr_t *xfr, uint32_t serial_from, uint32_t serial_to); /*! + * \brief Creates changesets from zones difference. + * + * Also saves changesets to journal, which is taken from old zone. + * + * \param old_zone Old zone, previously served by server. + * \param new_zone New zone, to be served by server, after creating changesets. + * + * \retval KNOTD_EOK on success. + * \retval KNOTD_EINVAL on invalid arguments. + * \retval KNOTD_ERANGE when new serial is lower than the old one. + * \retval KNOTD_ENODIFF when new zone's serial are equal. + * \retval KNOTD_ERROR when there was error creating changesets. + */ +int zones_create_and_save_changesets(const knot_zone_t *old_zone, + const knot_zone_t *new_zone); + +/*! * \brief Update zone timers. * * REFRESH/RETRY/EXPIRE timers are updated according to SOA. diff --git a/src/knot/stat/gatherer.c b/src/knot/stat/gatherer.c index 5b8eab6..5b8eab6 100644..100755 --- a/src/knot/stat/gatherer.c +++ b/src/knot/stat/gatherer.c diff --git a/src/knot/stat/gatherer.h b/src/knot/stat/gatherer.h index 62b3939..62b3939 100644..100755 --- a/src/knot/stat/gatherer.h +++ b/src/knot/stat/gatherer.h diff --git a/src/knot/stat/stat-common.h b/src/knot/stat/stat-common.h index 032e32b..032e32b 100644..100755 --- a/src/knot/stat/stat-common.h +++ b/src/knot/stat/stat-common.h diff --git a/src/knot/stat/stat.c b/src/knot/stat/stat.c index a473085..a473085 100644..100755 --- a/src/knot/stat/stat.c +++ b/src/knot/stat/stat.c diff --git a/src/knot/stat/stat.h b/src/knot/stat/stat.h index 0cf1454..0cf1454 100644..100755 --- a/src/knot/stat/stat.h +++ b/src/knot/stat/stat.h diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c index fc20c29..20043df 100644..100755 --- a/src/knot/zone/semantic-check.c +++ b/src/knot/zone/semantic-check.c @@ -16,8 +16,7 @@ static char *error_messages[(-ZC_ERR_ALLOC) + 1] = { [-ZC_ERR_ALLOC] = "Memory allocation error!\n", [-ZC_ERR_MISSING_SOA] = "SOA record missing in zone!\n", - [-ZC_ERR_MISSING_NS_DEL_POINT] = "NS record missing in zone apex or in " - "delegation point!\n", + [-ZC_ERR_MISSING_NS_DEL_POINT] = "NS record missing in zone apex!\n", [-ZC_ERR_RRSIG_RDATA_TYPE_COVERED] = "RRSIG: Type covered rdata field is wrong!\n", @@ -169,7 +168,7 @@ int err_handler_handle_error(err_handler_t *handler, return KNOT_EBADARG; } - /*!< \todo this is so wrong! This should not even return anything. */ + /*!< \todo #1886 this is so wrong! Should not even return anything. */ if (error == ZC_ERR_ALLOC || error == 0) { return KNOT_EBADARG; } @@ -262,9 +261,11 @@ static int check_cname_cycles_in_zone(knot_zone_contents_t *zone, const knot_node_t *next_node = NULL; uint i = 0; - - assert(tmp_rdata); - + + if (tmp_rdata == NULL) { + return KNOT_EOK; + } + const knot_dname_t *next_dname = knot_rdata_cname_name(tmp_rdata); /* (cname_name == dname_target) */ @@ -330,6 +331,7 @@ static int check_cname_cycles_in_zone(knot_zone_contents_t *zone, } else if ((knot_dname_is_fqdn(next_dname_copy) && knot_dname_label_count(next_dname_copy) == 0)) { knot_dname_free(&next_dname_copy); + knot_dname_free(&tmp_chopped); /* Root domain, end of search. */ break; } @@ -367,7 +369,7 @@ static int check_cname_cycles_in_zone(knot_zone_contents_t *zone, next_dname); } -/*!< \todo this might replace some of the code above. */ +/*!< \todo #1887 this might replace some of the code above. */ // /* Still NULL, try wildcards. */ // if (next_node == NULL && knot_dname_is_wildcard(next_dname)) { // /* We can only use the wildcard so many times. */ @@ -390,7 +392,6 @@ static int check_cname_cycles_in_zone(knot_zone_contents_t *zone, knot_dname_t *chopped_next = knot_dname_left_chop(next_dname); if (chopped_next == NULL) { - /*!< \todo check. */ return KNOT_ERROR; } while (next_node == NULL && chopped_next != NULL) { @@ -422,7 +423,7 @@ static int check_cname_cycles_in_zone(knot_zone_contents_t *zone, if (next_node != NULL) { next_rrset = knot_node_rrset(next_node, rrset->type); - if (next_rrset != NULL) { + if (next_rrset != NULL && next_rrset->rdata != NULL) { next_dname = knot_rdata_cname_name(next_rrset->rdata); } else { @@ -477,7 +478,6 @@ uint16_t type_covered_from_rdata(const knot_rdata_t *rdata) static int check_dnskey_rdata(const knot_rdata_t *rdata) { /* check that Zone key bit it set - position 7 in net order */ - /*! \todo FIXME: endian? I swear I've fixed this already, it was 7 i guesss*/ uint16_t mask = 1 << 8; //0b0000000100000000; uint16_t flags = @@ -570,7 +570,6 @@ static int dnskey_to_wire(const knot_rdata_t *rdata, uint8_t **wire, /* copy the wire octet by octet */ - /* TODO check if we really have that many items */ if (rdata->count < 4) { free(*wire); *wire = NULL; @@ -754,28 +753,34 @@ static int check_rrsig_in_rrset(const knot_rrset_t *rrset, return ZC_ERR_RRSIG_TTL; } - /* Check whether all rrsets have their rrsigs */ const knot_rdata_t *tmp_rdata = knot_rrset_rdata(rrset); const knot_rdata_t *tmp_rrsig_rdata = knot_rrset_rdata(rrsigs); - - assert(tmp_rdata); - assert(tmp_rrsig_rdata); + + assert(tmp_rrsig_rdata != NULL); + if (tmp_rdata == NULL) { + /* Only RRSIG, valid, but we can't check anything. */ + return KNOT_EOK; + } + int ret = 0; - char all_signed = tmp_rdata && tmp_rrsig_rdata; do { if ((ret = check_rrsig_rdata(tmp_rrsig_rdata, rrset, dnskey_rrset)) != 0) { + /*!< \todo This should go to handler. */ return ret; } - - all_signed = tmp_rdata && tmp_rrsig_rdata; - } while (((tmp_rdata = knot_rrset_rdata_next(rrset, tmp_rdata)) - != NULL) && - ((tmp_rrsig_rdata = - knot_rrset_rdata_next(rrsigs, tmp_rrsig_rdata)) - != NULL)); - + tmp_rdata = knot_rrset_rdata_next(rrset, tmp_rdata); + tmp_rrsig_rdata = knot_rrset_rdata_next(rrsigs, + tmp_rrsig_rdata); + } while (tmp_rdata != NULL && tmp_rrsig_rdata != NULL); + + /*!< \todo JK 03-08-2012 #1972 + * This check is not very informative, its output + * does not contain any info about which RRSet is not completely signed. + * A rewrite is needed. + */ + char all_signed = tmp_rdata == NULL && tmp_rrsig_rdata == NULL; if (!all_signed) { return ZC_ERR_RRSIG_NOT_ALL; } @@ -992,10 +997,6 @@ static int check_nsec3_node_in_zone(knot_zone_contents_t *zone, knot_node_t *nod /* Directly discard. */ knot_dname_free(&next_dname); - /*!< \todo These comments are not accurate anymore. */ - /* This is probably not sufficient, but again, it is covered in - * zone load time */ - uint count; uint16_t *array = NULL; if (rdata_nsec_to_type_array( @@ -1104,8 +1105,9 @@ static int semantic_checks_plain(knot_zone_contents_t *zone, } } - if (knot_rrset_rdata(cname_rrset)->next != - knot_rrset_rdata(cname_rrset)) { + if (knot_rrset_rdata(cname_rrset) && + knot_rrset_rdata(cname_rrset)->next != + knot_rrset_rdata(cname_rrset)) { *fatal_error = 1; err_handler_handle_error(handler, node, ZC_ERR_CNAME_MULTIPLE); @@ -1169,7 +1171,7 @@ static int semantic_checks_plain(knot_zone_contents_t *zone, return KNOT_EOK; } - /*!< \todo Good Lord, move this to ist own function. */ + /*!< \todo #1887 Good Lord, move this to ist own function. */ /* check for glue records at zone cuts and in apex. */ if (knot_node_is_deleg_point(node) || knot_zone_contents_apex(zone) == @@ -1184,10 +1186,12 @@ static int semantic_checks_plain(knot_zone_contents_t *zone, //FIXME this should be an error as well ! (i guess) knot_dname_t *ns_dname = + knot_dname_deep_copy( knot_rdata_get_item(knot_rrset_rdata - (ns_rrset), 0)->dname; - - assert(ns_dname); + (ns_rrset), 0)->dname); + if (ns_dname == NULL) { + return KNOT_ENOMEM; + } const knot_node_t *glue_node = knot_zone_contents_find_node(zone, ns_dname); @@ -1199,6 +1203,7 @@ static int semantic_checks_plain(knot_zone_contents_t *zone, knot_dname_t *wildcard = knot_dname_new_from_str("*", 1, NULL); if (wildcard == NULL) { + knot_dname_free(&ns_dname); return KNOT_ENOMEM; } @@ -1206,6 +1211,7 @@ static int semantic_checks_plain(knot_zone_contents_t *zone, if (knot_dname_cat(wildcard, ns_dname) == NULL) { + knot_dname_free(&ns_dname); knot_dname_free(&wildcard); return KNOT_ENOMEM; } @@ -1236,6 +1242,7 @@ static int semantic_checks_plain(knot_zone_contents_t *zone, } } } + knot_dname_free(&ns_dname); } return KNOT_EOK; } @@ -1512,7 +1519,7 @@ void log_cyclic_errors_in_zone(err_handler_t *handler, free(next_dname_decoded); - /*! \todo Free result and dname! */ + /*! \todo #1887 Free result and dname! */ if (knot_dname_cat(next_dname, knot_node_owner(knot_zone_contents_apex(zone))) == NULL) { diff --git a/src/knot/zone/semantic-check.h b/src/knot/zone/semantic-check.h index 17b774f..2f6dad7 100644..100755 --- a/src/knot/zone/semantic-check.h +++ b/src/knot/zone/semantic-check.h @@ -20,7 +20,7 @@ * * \brief DNS zone semantic checks. * - * \addtogroup dnslib + * \addtogroup zoneparser * @{ */ @@ -200,3 +200,5 @@ int zone_do_sem_checks(knot_zone_contents_t *zone, char do_checks, knot_node_t **last_node); #endif // _KNOT_SEMANTIC_CHECK_H_ + +/*! @} */ diff --git a/src/knot/zone/zone-dump-text.c b/src/knot/zone/zone-dump-text.c index bc606d3..8f428a5 100644..100755 --- a/src/knot/zone/zone-dump-text.c +++ b/src/knot/zone/zone-dump-text.c @@ -305,12 +305,12 @@ static inline uint16_t rdata_item_size(knot_rdata_item_t item) return item.raw_data[0]; } -char *rdata_dname_to_string(knot_rdata_item_t item) +static char *rdata_dname_to_string(knot_rdata_item_t item) { return knot_dname_to_str(item.dname); } -char *rdata_binary_dname_to_string(knot_rdata_item_t item) +static char *rdata_binary_dname_to_string(knot_rdata_item_t item) { if (item.raw_data == NULL) { return NULL; @@ -334,7 +334,7 @@ char *rdata_binary_dname_to_string(knot_rdata_item_t item) return str; } -char *rdata_dns_name_to_string(knot_rdata_item_t item) +static char *rdata_dns_name_to_string(knot_rdata_item_t item) { return knot_dname_to_str(item.dname); } @@ -342,8 +342,7 @@ char *rdata_dns_name_to_string(knot_rdata_item_t item) static char *rdata_txt_data_to_string(const uint8_t *data) { uint8_t length = data[0]; - size_t i; - + size_t i = 0; if (length == 0) { return NULL; } @@ -360,38 +359,41 @@ static char *rdata_txt_data_to_string(const uint8_t *data) } memset(ret, 0, current_length); - strncat(ret, "\"", 3); + strncat(ret, "\"", 2); for (i = 1; i <= length; i++) { char ch = (char) data[i]; if (isprint((int)ch)) { if (ch == '"' || ch == '\\') { - strncat(ret, "\"", 3); + strncat(ret, "\"", 2); } - /* for the love of god, how to this better, - but w/o obscure self-made functions */ char tmp_str[2]; tmp_str[0] = ch; - tmp_str[1] = 0; + tmp_str[1] = '\0'; strncat(ret, tmp_str, 2); } else { - strncat(ret, "\\", 3); + strncat(ret, "\\", 2); char tmp_str[2]; tmp_str[0] = ch - '0'; - tmp_str[1] = 0; - + tmp_str[1] = '\0'; strncat(ret, tmp_str, 2); } } - strncat(ret, "\"", 3); + strncat(ret, "\"", 2); return ret; } -char *rdata_text_to_string(knot_rdata_item_t item) +static char *rdata_text_to_string(knot_rdata_item_t item) { uint16_t size = item.raw_data[0]; - char *ret = malloc(sizeof(char) * size * 2 + 1) ; + /* + * Times two because they can all be one char long + * and then it would be as much chars as spaces (and one final space). + */ + size_t txt_size = size * 2 + 1; + /* + 1 ... space for (hypothetical) last \0. */ + char *ret = malloc(txt_size + 1); if (ret == NULL) { ERR_ALLOC_FAILED; return NULL; @@ -399,6 +401,7 @@ char *rdata_text_to_string(knot_rdata_item_t item) memset(ret, 0, sizeof(char) * size); const uint8_t *data = (uint8_t *)(item.raw_data + 1); size_t read_count = 0; + size_t tmp_str_current_length = 0; // Will be used with strncat. while (read_count < size) { assert(read_count <= size); char *txt = rdata_txt_data_to_string(data + read_count); @@ -406,20 +409,30 @@ char *rdata_text_to_string(knot_rdata_item_t item) free(ret); return NULL; } + /* + * We can trust this strlen, as + * it is created in internal function. + */ read_count += strlen(txt) - 1; /* Create delimiter. */ char del[2]; del[0] = ' '; del[1] = '\0'; - strncat(ret, txt, strlen(txt)); - strncat(ret, del, 2); + + /* We can only write to the remainder of string. */ + strncat(ret, txt, txt_size - tmp_str_current_length); + /* Increase length of tmp string. */ + tmp_str_current_length += strlen(txt); + strncat(ret, del, txt_size - tmp_str_current_length); + /* Increase length of tmp string by 1 ... space. */ + tmp_str_current_length += + 1; free(txt); } return ret; } -char *rdata_byte_to_string(knot_rdata_item_t item) +static char *rdata_byte_to_string(knot_rdata_item_t item) { assert(item.raw_data[0] == 1); uint8_t data = *((uint8_t *)(item.raw_data + 1)); @@ -428,7 +441,7 @@ char *rdata_byte_to_string(knot_rdata_item_t item) return ret; } -char *rdata_short_to_string(knot_rdata_item_t item) +static char *rdata_short_to_string(knot_rdata_item_t item) { uint16_t data = knot_wire_read_u16(rdata_item_data(item)); char *ret = malloc(sizeof(char) * U16_MAX_STR_LEN); @@ -438,7 +451,7 @@ char *rdata_short_to_string(knot_rdata_item_t item) return ret; } -char *rdata_long_to_string(knot_rdata_item_t item) +static char *rdata_long_to_string(knot_rdata_item_t item) { uint32_t data = knot_wire_read_u32(rdata_item_data(item)); char *ret = malloc(sizeof(char) * U32_MAX_STR_LEN); @@ -447,7 +460,7 @@ char *rdata_long_to_string(knot_rdata_item_t item) return ret; } -char *rdata_a_to_string(knot_rdata_item_t item) +static char *rdata_a_to_string(knot_rdata_item_t item) { /* 200 seems like a little too much */ char *ret = malloc(sizeof(char) * 200); @@ -458,7 +471,7 @@ char *rdata_a_to_string(knot_rdata_item_t item) } } -char *rdata_aaaa_to_string(knot_rdata_item_t item) +static char *rdata_aaaa_to_string(knot_rdata_item_t item) { char *ret = malloc(sizeof(char) * 200); if (inet_ntop(AF_INET6, rdata_item_data(item), ret, 200)) { @@ -468,7 +481,7 @@ char *rdata_aaaa_to_string(knot_rdata_item_t item) } } -char *rdata_rrtype_to_string(knot_rdata_item_t item) +static char *rdata_rrtype_to_string(knot_rdata_item_t item) { uint16_t type = knot_wire_read_u16(rdata_item_data(item)); const char *tmp = knot_rrtype_to_string(type); @@ -477,7 +490,7 @@ char *rdata_rrtype_to_string(knot_rdata_item_t item) return ret; } -char *rdata_algorithm_to_string(knot_rdata_item_t item) +static char *rdata_algorithm_to_string(knot_rdata_item_t item) { uint8_t id = *rdata_item_data(item); char *ret = malloc(sizeof(char) * MAX_RR_TYPE_LEN); @@ -492,7 +505,7 @@ char *rdata_algorithm_to_string(knot_rdata_item_t item) return ret; } -char *rdata_certificate_type_to_string(knot_rdata_item_t item) +static char *rdata_certificate_type_to_string(knot_rdata_item_t item) { uint16_t id = knot_wire_read_u16(rdata_item_data(item)); char *ret = malloc(sizeof(char) * MAX_RR_TYPE_LEN); @@ -507,7 +520,7 @@ char *rdata_certificate_type_to_string(knot_rdata_item_t item) return ret; } -char *rdata_period_to_string(knot_rdata_item_t item) +static char *rdata_period_to_string(knot_rdata_item_t item) { /* uint32 but read 16 XXX */ uint32_t period = knot_wire_read_u32(rdata_item_data(item)); @@ -516,7 +529,7 @@ char *rdata_period_to_string(knot_rdata_item_t item) return ret; } -char *rdata_time_to_string(knot_rdata_item_t item) +static char *rdata_time_to_string(knot_rdata_item_t item) { time_t time = (time_t) knot_wire_read_u32(rdata_item_data(item)); struct tm tm_conv; @@ -532,7 +545,7 @@ char *rdata_time_to_string(knot_rdata_item_t item) } } -char *rdata_base32_to_string(knot_rdata_item_t item) +static char *rdata_base32_to_string(knot_rdata_item_t item) { int length; size_t size = rdata_item_size(item); @@ -555,7 +568,8 @@ char *rdata_base32_to_string(knot_rdata_item_t item) } } -char *rdata_base64_to_string(knot_rdata_item_t item) +/*!< \todo Replace with function from .../common after release. */ +static char *rdata_base64_to_string(knot_rdata_item_t item) { int length; size_t size = rdata_item_size(item); @@ -570,7 +584,7 @@ char *rdata_base64_to_string(knot_rdata_item_t item) } } -char *hex_to_string(const uint8_t *data, size_t size) +static char *knot_hex_to_string(const uint8_t *data, size_t size) { static const char hexdigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', @@ -593,7 +607,7 @@ char *hex_to_string(const uint8_t *data, size_t size) char *rdata_hex_to_string(knot_rdata_item_t item) { - return hex_to_string(rdata_item_data(item), rdata_item_size(item)); + return knot_hex_to_string(rdata_item_data(item), rdata_item_size(item)); } char *rdata_hexlen_to_string(knot_rdata_item_t item) @@ -605,8 +619,8 @@ char *rdata_hexlen_to_string(knot_rdata_item_t item) ret[1] = '\0'; return ret; } else { - return hex_to_string(rdata_item_data(item) + 1, - rdata_item_size(item) - 1); + return knot_hex_to_string(rdata_item_data(item) + 1, + rdata_item_size(item) - 1); } } @@ -622,8 +636,8 @@ char *rdata_nsap_to_string(knot_rdata_item_t item) /* String is already terminated. */ memcpy(ret, "0x", strlen("0x")); - char *converted = hex_to_string(rdata_item_data(item), - rdata_item_size(item)); + char *converted = knot_hex_to_string(rdata_item_data(item), + rdata_item_size(item)); if (converted == NULL) { return NULL; } @@ -707,9 +721,7 @@ char *rdata_services_to_string(knot_rdata_item_t item) if (proto) { int i; - /*!< \todo #1863 see below, but we can trust getprotobynumber... */ strncpy(ret, proto->p_name, strlen(proto->p_name)); - strncat(ret, " ", 2); for (i = 0; i < bitmap_size * 8; ++i) { @@ -718,12 +730,6 @@ char *rdata_services_to_string(knot_rdata_item_t item) getservbyport((int)htons(i), proto->p_name); if (service) { - /*!< \todo #1863 - * using strncat with strlen - * does not make a whole lot of sense. - * At least it will crash wil - * Use max length of service name! - */ strncat(ret, service->s_name, strlen(service->s_name)); strncat(ret, " ", 2); @@ -738,37 +744,6 @@ char *rdata_services_to_string(knot_rdata_item_t item) } return ret; - - /* - int result = 0; - uint8_t protocol_number = buffer_read_u8(&packet); - ssize_t bitmap_size = buffer_remaining(&packet); - uint8_t *bitmap = buffer_current(&packet); - struct protoent *proto = getprotobynumber(protocol_number); - - - if (proto) { - int i; - - strcpy(ret, proto->p_name); - - for (i = 0; i < bitmap_size * 8; ++i) { - if (get_bit(bitmap, i)) { - struct servent *service = - getservbyport((int)htons(i), - proto->p_name); - if (service) { - buffer_printf(output, " %s", - service->s_name); - } else { - buffer_printf(output, " %d", i); - } - } - } - result = 1; - } - return ret; - */ } char *rdata_ipsecgateway_to_string(knot_rdata_item_t item, @@ -830,17 +805,15 @@ char *rdata_nxt_to_string(knot_rdata_item_t item) char *rdata_nsec_to_string(knot_rdata_item_t item) { - /* CLEANUP */ -// int insert_space = 0; - char *ret = malloc(sizeof(char) * MAX_NSEC_BIT_STR_LEN); - + if (ret == NULL) { + ERR_ALLOC_FAILED; + return NULL; + } memset(ret, 0, MAX_NSEC_BIT_STR_LEN); - uint8_t *data = rdata_item_data(item); int increment = 0; - for (int i = 0; i < rdata_item_size(item); i += increment) { increment = 0; uint8_t window = data[i]; @@ -870,33 +843,6 @@ char *rdata_nsec_to_string(knot_rdata_item_t item) } return ret; - - /* CLEANUP */ -/* while (buffer_available(&packet, 2)) { - uint8_t window = buffer_read_u8(&packet); - uint8_t bitmap_size = buffer_read_u8(&packet); - uint8_t *bitmap = buffer_current(&packet); - int i; - - if (!buffer_available(&packet, bitmap_size)) { - buffer_set_position(output, saved_position); - return 0; - } - - for (i = 0; i < bitmap_size * 8; ++i) { - if (get_bit(bitmap, i)) { - buffer_printf(output, - "%s%s", - insert_space ? " " : "", - rrtype_to_string( - window * 256 + i)); - insert_space = 1; - } - } - buffer_skip(&packet, bitmap_size); - } - - return 1; */ } char *rdata_unknown_to_string(knot_rdata_item_t item) @@ -912,7 +858,7 @@ char *rdata_unknown_to_string(knot_rdata_item_t item) snprintf(ret + strlen("\\# "), strlen("\\# ") + U16_MAX_STR_LEN + 1, "%lu ", (unsigned long) size); - char *converted = hex_to_string(rdata_item_data(item), size); + char *converted = knot_hex_to_string(rdata_item_data(item), size); strncat(ret, converted, size * 2 + 1); free(converted); return ret; @@ -959,14 +905,13 @@ char *rdata_item_to_string(knot_rdata_zoneformat_t type, return item_to_string_table[type](item); } -/* CLEANUP */ -/*void knot_zone_tree_apply_inorder(knot_zone_t *zone, - void (*function)(knot_node_t *node, void *data), - void *data); */ - int rdata_dump_text(const knot_rdata_t *rdata, uint16_t type, FILE *f, const knot_rrset_t *rrset) { + if (rdata == NULL || rrset == NULL) { + return KNOT_EBADARG; + } + knot_rrtype_descriptor_t *desc = knot_rrtype_descriptor_by_type(type); char *item_str = NULL; @@ -1040,19 +985,23 @@ int rrsig_set_dump_text(knot_rrset_t *rrsig, FILE *f) int rrset_dump_text(const knot_rrset_t *rrset, FILE *f) { - dump_rrset_header(rrset, f); - knot_rdata_t *tmp = rrset->rdata; + if (rrset->rdata != NULL) { // No sense in dumping empty RR + dump_rrset_header(rrset, f); - while (tmp->next != rrset->rdata) { - int ret = rdata_dump_text(tmp, rrset->type, f, rrset); - if (ret != KNOTD_EOK) { - return ret; + knot_rdata_t *tmp = rrset->rdata; + + while (tmp->next != rrset->rdata) { + int ret = rdata_dump_text(tmp, rrset->type, f, rrset); + if (ret != KNOTD_EOK) { + return ret; + } + dump_rrset_header(rrset, f); + tmp = tmp->next; } - dump_rrset_header(rrset, f); - tmp = tmp->next; + + rdata_dump_text(tmp, rrset->type, f, rrset); } - rdata_dump_text(tmp, rrset->type, f, rrset); knot_rrset_t *rrsig_set = rrset->rrsigs; if (rrsig_set != NULL) { rrsig_set_dump_text(rrsig_set, f); diff --git a/src/knot/zone/zone-dump-text.h b/src/knot/zone/zone-dump-text.h index 9f9c8dd..c55f712 100644..100755 --- a/src/knot/zone/zone-dump-text.h +++ b/src/knot/zone/zone-dump-text.h @@ -20,7 +20,7 @@ * * \brief Functions for dumping zone to text file. * - * \addtogroup dnslib + * \addtogroup zone-load-dump * @{ */ diff --git a/src/knot/zone/zone-dump.c b/src/knot/zone/zone-dump.c index aaf0165..708cd2b 100644..100755 --- a/src/knot/zone/zone-dump.c +++ b/src/knot/zone/zone-dump.c @@ -360,6 +360,11 @@ static int knot_rdata_dump_binary(knot_rdata_t *rdata, rdata->items[i].dname) { wildcard = rdata->items[i].dname->node->owner; } + + dbg_zdump_detail("zdump: dump_rdata: " + "Writing dname: %s.\n", + knot_dname_to_str( + rdata->items[i].dname)); if (use_ids) { /* Write ID. */ @@ -392,6 +397,8 @@ static int knot_rdata_dump_binary(knot_rdata_t *rdata, /*! \todo Does not have to be so complex. * Create extra variable. */ if (rdata->items[i].dname->node != NULL && !wildcard) { + dbg_zdump("zdump: dump_rdata: " + "This dname is in the zone.\n"); if (!write_wrapper((uint8_t *)"\1", sizeof(uint8_t), 1, fd, stream, max_size, @@ -401,6 +408,8 @@ static int knot_rdata_dump_binary(knot_rdata_t *rdata, return KNOT_ERROR; } } else { + dbg_zdump("zdump: dump_rdata: " + "This dname is not in the zone.\n"); if (!write_wrapper((uint8_t *)"\0", sizeof(uint8_t), 1, fd, @@ -423,6 +432,9 @@ static int knot_rdata_dump_binary(knot_rdata_t *rdata, } uint32_t wildcard_id = wildcard->id; + dbg_zdump("zdump: dump_rdata: " + "This dname is covered by wc (%s).\n", + knot_dname_to_str(wildcard)); if (!write_wrapper(&wildcard_id, sizeof(wildcard_id), 1, fd, stream, max_size, @@ -567,7 +579,11 @@ static int knot_rrset_dump_binary(const knot_rrset_t *rrset, int fd, "Dumping RRSet \\w owner: %s.\n", name); free(name); - ); + ); + + if (!use_ids) { + assert(rrset->rrsigs == NULL); + } if (!use_ids) { /*!< \todo IDs in changeset do no good. Change loading too. */ @@ -602,10 +618,14 @@ static int knot_rrset_dump_binary(const knot_rrset_t *rrset, int fd, /* Calculate rrset rdata count. */ knot_rdata_t *tmp_rdata = rrset->rdata; - while(tmp_rdata->next != rrset->rdata) { + while(tmp_rdata && (tmp_rdata->next != rrset->rdata)) { tmp_rdata = tmp_rdata->next; rdata_count++; } + + if (rrset->rdata == NULL) { + rdata_count = 0; + } if (!write_wrapper(&rdata_count, sizeof(rdata_count), 1, fd, stream, max_size, written_bytes, crc)) { @@ -619,29 +639,35 @@ static int knot_rrset_dump_binary(const knot_rrset_t *rrset, int fd, } dbg_zdump_verb("zdump: rrset_dump_binary: Static data dumped.\n"); + + if (rdata_count != 0) { + + tmp_rdata = rrset->rdata; - tmp_rdata = rrset->rdata; - - while (tmp_rdata->next != rrset->rdata) { + while (tmp_rdata->next != rrset->rdata) { + int ret = knot_rdata_dump_binary(tmp_rdata, rrset->type, + fd, use_ids, + stream, max_size, + written_bytes, crc); + if (ret != KNOT_EOK) { + dbg_zdump("zdump: rrset_to_binary: Could not " + "dump " + "rdata. Reason: %s.\n", + knot_strerror(ret)); + return ret; + } + tmp_rdata = tmp_rdata->next; + } + int ret = knot_rdata_dump_binary(tmp_rdata, rrset->type, fd, use_ids, - stream, max_size, - written_bytes, crc); + stream, + max_size, written_bytes, crc); if (ret != KNOT_EOK) { dbg_zdump("zdump: rrset_to_binary: Could not dump " "rdata. Reason: %s.\n", knot_strerror(ret)); return ret; } - tmp_rdata = tmp_rdata->next; - } - - int ret = knot_rdata_dump_binary(tmp_rdata, rrset->type, fd, use_ids, - stream, - max_size, written_bytes, crc); - if (ret != KNOT_EOK) { - dbg_zdump("zdump: rrset_to_binary: Could not dump " - "rdata. Reason: %s.\n", knot_strerror(ret)); - return ret; } dbg_zdump_verb("zdump: rrset_dump_binary: Rdata dumped.\n"); @@ -894,6 +920,7 @@ int knot_zdump_binary(knot_zone_contents_t *zone, int fd, int do_checks, const char *sfilename, crc_t *crc) { + if (fd < 0 || sfilename == NULL) { dbg_zdump("zdump: Bad arguments.\n"); return KNOT_EBADARG; diff --git a/src/knot/zone/zone-dump.h b/src/knot/zone/zone-dump.h index fbebae9..02d0298 100644..100755 --- a/src/knot/zone/zone-dump.h +++ b/src/knot/zone/zone-dump.h @@ -20,7 +20,7 @@ * * \brief Functions for dumping zone to binary file. * - * \addtogroup dnslib + * \addtogroup zone-load-dump * @{ */ @@ -38,7 +38,7 @@ enum { }; /*! \brief Magic identifier: { "knot", maj_ver, min_ver, revision } */ -#define MAGIC_BYTES {'k', 'n', 'o', 't', '1', '0', '4'} +#define MAGIC_BYTES {'k', 'n', 'o', 't', '1', '1', '0'} /*! * \brief Dumps given zone to binary file. diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c index 3a7134e..fedd38b 100644..100755 --- a/src/knot/zone/zone-load.c +++ b/src/knot/zone/zone-load.c @@ -62,39 +62,57 @@ static int timet_cmp(time_t x, time_t y) * \retval 0 if failed. */ static inline int fread_safe_from_file(void *dst, - size_t size, size_t n, FILE *fp) + size_t size, size_t n, void *source) { + if (dst == NULL || source == NULL) { + dbg_zload("zload: fread_safe_from_file: NULL arguments.\n"); + return 0; + } + FILE *fp = (FILE *)source; int rc = fread(dst, size, n, fp); if (rc != n) { - fprintf(stderr, "fread: invalid read %d (expected %zu)\n", rc, - n); + dbg_zload("zload: fread_safe_from_file: " + "invalid read %d (exp. %zu)\n", + rc, n); } return rc == n; } -static uint8_t *knot_zload_stream = NULL; -static size_t knot_zload_stream_remaining = 0; -static size_t knot_zload_stream_size = 0; +struct load_stream { + uint8_t *stream; + size_t stream_remaining; + size_t stream_size; +}; + +typedef struct load_stream load_stream_t; static inline int read_from_stream(void *dst, - size_t size, size_t n, FILE *fp) + size_t size, size_t n, void *source) { - if (knot_zload_stream_remaining < (size * n)) { + if (dst == NULL || source == NULL) { + dbg_zload("zload: read_from_stream: NULL arguments.\n"); + return 0; + } + + /* Extract information from source data. */ + load_stream_t *data = (load_stream_t *)source; + + + if (data->stream_remaining < (size * n)) { + dbg_zload("zload: read_from_stream: Buffer depleted.\n"); return 0; } memcpy(dst, - knot_zload_stream + - (knot_zload_stream_size - knot_zload_stream_remaining), + data->stream + + (data->stream_size - data->stream_remaining), size * n); - knot_zload_stream_remaining -= size * n; + data->stream_remaining -= size * n; return 1; } -static int (*fread_wrapper)(void *dst, size_t size, size_t n, FILE *fp); - /*! \note Contents of dump file: * MAGIC(knotxx) NUMBER_OF_NORMAL_NODES NUMBER_OF_NSEC3_NODES * [normal_nodes] [nsec3_nodes] @@ -136,23 +154,31 @@ static void load_rdata_purge(knot_rdata_t *rdata, knot_dname_retain(items[i].dname); break; default: + /*!< \todo This would leak wire data! */ break; } } /* Copy items to rdata and free the temporary rdata. */ knot_rdata_set_items(rdata, items, count); - knot_rdata_deep_free(&rdata, type, 1); + knot_rdata_deep_free(&rdata, type, 0); free(items); } -static knot_dname_t *read_dname_with_id(FILE *f) +static knot_dname_t *read_dname_with_id(FILE *f, int use_ids) { if (f == NULL) { dbg_zload("zload: read_dname_id: NULL file.\n"); } knot_dname_t *ret = knot_dname_new(); CHECK_ALLOC_LOG(ret, NULL); + + int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source); + if (use_ids) { + fread_wrapper = fread_safe_from_file; + } else { + fread_wrapper = read_from_stream; + } /* Read ID. */ uint32_t dname_id = 0; @@ -173,9 +199,12 @@ static knot_dname_t *read_dname_with_id(FILE *f) return NULL; } ret->size = dname_size; - dbg_zload("loaded: dname length: %u\n", ret->size); - - assert(ret->size <= DNAME_MAX_WIRE_LENGTH); + dbg_zload_detail("loaded: dname length: %u\n", ret->size); + if (ret->size > DNAME_MAX_WIRE_LENGTH) { + dbg_zload("zload: read_dname_id: Name too long.\n"); + knot_dname_release(ret); + return NULL; + } /* Read wireformat of dname. */ ret->name = malloc(sizeof(uint8_t) * ret->size); @@ -238,6 +267,18 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f, knot_dname_t **id_array, int use_ids) { + if (f == NULL) { + dbg_zload("zload: load_rdata: NULL arguments.\n"); + return NULL; + } + + int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source); + if (use_ids) { + fread_wrapper = fread_safe_from_file; + } else { + fread_wrapper = read_from_stream; + } + knot_rdata_t *rdata = knot_rdata_new(); if (rdata == NULL) { dbg_zload("zload: load_rdata: Cannot create new rdata.\n"); @@ -262,12 +303,14 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f, malloc(sizeof(knot_rdata_item_t) * rdata_count); if (items == NULL) { ERR_ALLOC_FAILED; - free(items); + free(rdata); return NULL; } if (rdata_count > desc->length) { dbg_zload("zload: load_rdata: Read wrong count of RDATA.\n"); + free(items); + free(rdata); return NULL; } @@ -285,6 +328,7 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f, /*!< \todo #1686 * Refactor these variables, some might be too big. */ + uint32_t dname_id = 0; uint8_t has_wildcard = 0; @@ -304,8 +348,13 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f, knot_dname_retain(id_array[dname_id]); items[i].dname = id_array[dname_id]; } else { - items[i].dname = read_dname_with_id(f); + items[i].dname = read_dname_with_id(f, use_ids); } + + dbg_zload_detail("zload: load_rdata: " + "Loading dname: %s.\n", + knot_dname_to_str(items[i].dname)); + if(!fread_wrapper(&in_the_zone, sizeof(in_the_zone), 1, f)) { @@ -322,6 +371,27 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f, "Cannot read wildcard bit.\n"); return NULL; } + + dbg_zload_detail("zload: load_rdata: Has wildcard: " + "%d\n", has_wildcard); + + if (use_ids && !in_the_zone) { + dbg_zload_detail("zload: load_rdata: " + "Freeing node owned by: %s.\n", + knot_dname_to_str(items[i].dname)); + /* Destroy the node */ + assert(!in_the_zone); + if (items[i].dname->node != NULL && + /* + * This check is here to prevent freeing + * of previously set wildcard node. + */ + (items[i].dname->node->owner == + items[i].dname)) { + knot_node_free(&items[i].dname->node); + assert(items[i].dname->node == NULL); + } + } if (use_ids && has_wildcard) { if(!fread_wrapper(&dname_id, sizeof(dname_id), @@ -332,16 +402,14 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f, "Cannot read wc ID.\n"); return NULL; } + dbg_zload_detail("zload: load_rdata: " + "Wildcard: %s\n", + knot_dname_to_str( + id_array[dname_id])); items[i].dname->node = id_array[dname_id]->node; - } else if (use_ids && !in_the_zone) { - /* destroy the node */ - if (id_array[dname_id]->node != NULL) { - knot_node_free(&id_array[dname_id]-> - node, 0); - } - /* Also sets node to NULL! */ } + assert(items[i].dname); } else { if (!fread_wrapper(&raw_data_length, @@ -379,10 +447,12 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f, } /* Each item has refcount already incremented for saving in rdata. */ - if (knot_rdata_set_items(rdata, items, rdata_count) != 0) { - fprintf(stderr, "zload: read_rdata: Could not set items " - "when loading rdata.\n"); - knot_rdata_deep_free(&rdata, type, 0); + int ret = knot_rdata_set_items(rdata, items, rdata_count); + if (ret != KNOT_EOK) { + dbg_zload("zload: read_rdata: Could not set items " + "when loading rdata. Reason: %\n.", + knot_strerror(ret)); + load_rdata_purge(rdata, items, desc->length, desc, type); return NULL; } @@ -403,7 +473,7 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f, * * \return pointer to created and read RRSIG on success, NULL otherwise. */ -static knot_rrset_t *knot_load_rrsig(FILE *f, knot_dname_t **id_array, +static knot_rrset_t *knot_load_rrsig(void *f, knot_dname_t **id_array, int use_ids) { if (f == NULL || id_array == NULL) { @@ -411,6 +481,13 @@ static knot_rrset_t *knot_load_rrsig(FILE *f, knot_dname_t **id_array, return NULL; } + int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source); + if (use_ids) { + fread_wrapper = fread_safe_from_file; + } else { + fread_wrapper = read_from_stream; + } + knot_rrset_t *rrsig = NULL; uint16_t rrset_type = 0; @@ -487,9 +564,21 @@ static knot_rrset_t *knot_load_rrsig(FILE *f, knot_dname_t **id_array, * * \return pointer to created and read RRSet on success, NULL otherwise. */ -static knot_rrset_t *knot_load_rrset(FILE *f, knot_dname_t **id_array, +static knot_rrset_t *knot_load_rrset(void *f, knot_dname_t **id_array, int use_ids) { + if (f == NULL) { + dbg_zload("zload: load_rrset: NULL arguments.\n"); + return NULL; + } + + int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source); + if (use_ids) { + fread_wrapper = fread_safe_from_file; + } else { + fread_wrapper = read_from_stream; + } + knot_rrset_t *rrset = NULL; uint16_t rrset_type = 0; @@ -504,7 +593,7 @@ static knot_rrset_t *knot_load_rrset(FILE *f, knot_dname_t **id_array, if (!use_ids) { dbg_zload_detail("zload: load_rrset: " "Loading owner of new RRSet from wire.\n"); - owner = read_dname_with_id(f); + owner = read_dname_with_id(f, use_ids); if (owner == NULL) { dbg_zload("zload: load_rrset: Cannot load owner.\n"); return NULL; @@ -587,6 +676,7 @@ dbg_zload_exec_detail( return NULL; } } else { + dbg_zload("zload: load_rrset: Cannot load rdata.\n"); knot_rrset_deep_free(&rrset, 0, 1, 1); return NULL; } @@ -628,6 +718,10 @@ static knot_node_t *knot_load_node(FILE *f, knot_dname_t **id_array) dbg_zload("zload: load_node: Wrong parameters.\n"); return NULL; } + + int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source); + fread_wrapper = fread_safe_from_file; + uint8_t flags = 0; knot_node_t *node = NULL; uint32_t parent_id = 0; @@ -661,6 +755,10 @@ static knot_node_t *knot_load_node(FILE *f, knot_dname_t **id_array) return NULL; } knot_dname_t *owner = id_array[dname_id]; + if (owner == NULL) { + dbg_zload("zload: load_node: Wrong dname ID, cannot load.\n"); + return NULL; + } dbg_zload_detail("zload: load_node: Node owner id: %d.\n", dname_id); dbg_zload_exec_detail( @@ -701,7 +799,7 @@ dbg_zload_exec_detail( for (int i = 0; i < rrset_count; i++) { if ((tmp_rrset = knot_load_rrset(f, id_array, 1)) == NULL) { - knot_node_free(&node, 0); + knot_node_free(&node); /*!< \todo #1686 * Refactor freeing, might not be enough. */ @@ -770,6 +868,9 @@ static void find_and_set_wildcard_child(knot_zone_contents_t *zone, static int knot_check_magic(FILE *f, const uint8_t* MAGIC, uint MAGIC_LENGTH) { uint8_t tmp_magic[MAGIC_LENGTH]; + + int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source); + fread_wrapper = fread_safe_from_file; if (!fread_wrapper(&tmp_magic, sizeof(uint8_t), MAGIC_LENGTH, f)) { return 0; @@ -791,6 +892,9 @@ static unsigned long calculate_crc(FILE *f) fseek(f, 0L, SEEK_END); size_t file_size = ftell(f); fseek(f, 0L, SEEK_SET); + + int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source); + fread_wrapper = fread_safe_from_file; const size_t chunk_size = 4096; /* read chunks of 4 kB */ @@ -836,11 +940,12 @@ int knot_zload_open(zloader_t **dst, const char *filename) dbg_zload("zload: open: Bad arguments.\n"); return KNOT_EBADARG; } + + int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source); + fread_wrapper = fread_safe_from_file; *dst = 0; - fread_wrapper = fread_safe_from_file; - /* Open file for binary read. */ FILE *f = fopen(filename, "rb"); if (unlikely(!f)) { @@ -856,6 +961,15 @@ int knot_zload_open(zloader_t **dst, const char *filename) return KNOT_EFEWDATA; // No such file or directory (POSIX.1) } + + /* Calculate file size. */ + fseek(f, 0L, SEEK_END); + size_t file_size = ftell(f); + fseek(f, 0L, SEEK_SET); + if (file_size < MAGIC_LENGTH) { + fclose(f); + return KNOT_EFEWDATA; + } /* Calculate CRC and compare with filename.crc file */ unsigned long crc_calculated = calculate_crc(f); @@ -983,8 +1097,14 @@ int knot_zload_open(zloader_t **dst, const char *filename) static void cleanup_id_array(knot_dname_t **id_array, const uint from, const uint to) { + if (id_array == NULL) { + dbg_zload("zload: cleanup_id_array: NULL arguments.\n"); + } + for (uint i = from; i < to; i++) { - knot_dname_release(id_array[i]); + if (id_array[i] != NULL) { + knot_dname_release(id_array[i]); + } } free(id_array); @@ -1044,7 +1164,7 @@ static knot_dname_t **create_dname_array(FILE *f, uint max_id) memset(array, 0, sizeof(knot_dname_t *) * (max_id + 1)); for (uint i = 0; i < max_id - 1; i++) { - knot_dname_t *read_dname = read_dname_with_id(f); + knot_dname_t *read_dname = read_dname_with_id(f, 1); if (read_dname == NULL) { dbg_zload("zload: create_dname_array: " "Cannot read dname.\n" ); @@ -1090,8 +1210,6 @@ knot_zone_t *knot_zload_load(zloader_t *loader) return NULL; } - fread_wrapper = fread_safe_from_file; - FILE *f = loader->fp; knot_node_t *tmp_node; @@ -1099,7 +1217,10 @@ knot_zone_t *knot_zload_load(zloader_t *loader) uint32_t node_count; uint32_t nsec3_node_count; uint32_t auth_node_count; - + + int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source); + fread_wrapper = fread_safe_from_file; + if (!fread_wrapper(&node_count, sizeof(node_count), 1, f)) { dbg_zload("zload: load: Cannot read node count!\n"); return NULL; @@ -1147,7 +1268,7 @@ knot_zone_t *knot_zload_load(zloader_t *loader) knot_node_t *apex = knot_load_node(f, id_array); if (!apex) { - fprintf(stderr, "zone: Could not load apex node (in %s)\n", + dbg_zload("zone: Could not load apex node (in %s)\n", loader->filename); cleanup_id_array(id_array, 1, node_count + nsec3_node_count + 1); @@ -1163,7 +1284,7 @@ knot_zone_t *knot_zload_load(zloader_t *loader) node_count + nsec3_node_count + 1); dbg_zload("zload: load: Failed to create new " "zone from apex!\n"); - knot_node_free(&apex, 0); + knot_node_free(&apex); free(dname_table); return NULL; } @@ -1178,19 +1299,26 @@ knot_zone_t *knot_zload_load(zloader_t *loader) knot_node_t *last_node = 0; last_node = apex; + int ret = 0; + for (uint i = 1; i < node_count; i++) { tmp_node = knot_load_node(f, id_array); if (tmp_node != NULL) { - if (knot_zone_contents_add_node(contents, tmp_node, - 0, 0, 0) != 0) { + dbg_zload_detail("zload: load: Adding node owned by: " + "%s\n.", + knot_dname_to_str(tmp_node->owner)); + if ((ret = knot_zone_contents_add_node(contents, + tmp_node, + 0, 0, 0)) != 0) { cleanup_id_array(id_array, 1, node_count + nsec3_node_count + 1); knot_zone_deep_free(&zone, 0); dbg_zload("zload: load: Failed to add node " - "to zone.\n"); + "to zone: %s.\n", knot_strerror(ret)); return NULL; } + if (knot_dname_is_wildcard(tmp_node->owner)) { find_and_set_wildcard_child(contents, tmp_node, 0); @@ -1229,12 +1357,12 @@ knot_zone_t *knot_zload_load(zloader_t *loader) assert(nsec3_first != NULL); - if (knot_zone_contents_add_nsec3_node(contents, nsec3_first, - 0, 0, 0) - != 0) { + if ((ret = knot_zone_contents_add_nsec3_node(contents, + nsec3_first, + 0, 0, 0)) != 0) { dbg_zload("zload: load: " "cannot add first nsec3 node, " - "exiting.\n"); + "exiting: %s.\n", knot_strerror(ret)); knot_zone_deep_free(&zone, 0); cleanup_id_array(id_array, node_count + 1, nsec3_node_count + 1); @@ -1249,10 +1377,11 @@ knot_zone_t *knot_zload_load(zloader_t *loader) tmp_node = knot_load_node(f, id_array); if (tmp_node != NULL) { - if (knot_zone_contents_add_nsec3_node(contents, - tmp_node, 0, 0, 0) != 0) { + if ((ret = knot_zone_contents_add_nsec3_node(contents, + tmp_node, 0, 0, 0)) != 0) { dbg_zload("zload: load: Cannot add " - "NSEC3 node.\n"); + "NSEC3 node: %s.\n", + knot_strerror(ret)); knot_zone_deep_free(&zone, 0); cleanup_id_array(id_array, node_count + 1, nsec3_node_count + 1); @@ -1265,6 +1394,10 @@ knot_zone_t *knot_zload_load(zloader_t *loader) } else { fprintf(stderr, "zone: Node error (in %s).\n", loader->filename); + knot_zone_deep_free(&zone, 0); + cleanup_id_array(id_array, node_count + 1, + nsec3_node_count + 1); + return NULL; } } @@ -1332,27 +1465,20 @@ int knot_zload_rrset_deserialize(knot_rrset_t **rrset, return KNOT_EBADARG; } - fread_wrapper = read_from_stream; + load_stream_t data; + data.stream = stream; + data.stream_remaining = *size; + data.stream_size = *size; - knot_zload_stream = stream; - knot_zload_stream_remaining = knot_zload_stream_size = *size; - - knot_rrset_t *ret = knot_load_rrset(NULL, NULL, 0); + knot_rrset_t *ret = knot_load_rrset(&data, NULL, 0); if (ret == NULL) { dbg_zload("zload: rrset_deserialize: Cannot load RRSet.\n"); - knot_zload_stream = NULL; - knot_zload_stream_remaining = 0; - knot_zload_stream_size = 0; return KNOT_EMALF; } - *size = knot_zload_stream_remaining; + *size = data.stream_remaining; *rrset = ret; - knot_zload_stream = NULL; - knot_zload_stream_remaining = 0; - knot_zload_stream_size = 0; - dbg_zload_detail("zload: rrset_deserialize: RRSet deserialized " "successfully.\n"); return KNOT_EOK; diff --git a/src/knot/zone/zone-load.h b/src/knot/zone/zone-load.h index 2fe318f..837d5f2 100644..100755 --- a/src/knot/zone/zone-load.h +++ b/src/knot/zone/zone-load.h @@ -20,7 +20,7 @@ * * \brief Loader of previously parsed zone * - * \addtogroup dnslib + * \addtogroup zone-load-dump * @{ */ diff --git a/src/knotc.8 b/src/knotc.8 index b61bfd3..9cd3f08 100644..100755 --- a/src/knotc.8 +++ b/src/knotc.8 @@ -1,6 +1,6 @@ -.TH knotc "8" "November 2011" "CZ.NIC Labs" "Knot DNS, version 0.8" +.TH knotc "8" "August 2012" "CZ.NIC Labs" "Knot DNS, version 1.1" .SH NAME -.B knot +.B knotc \- Knot DNS control utility .SH SYNOPSIS .B knotc @@ -28,27 +28,39 @@ Wait for the server to finish start/stop operations. \fB\-i\fR, \fB\-\-interactive\fR Interactive mode (do not daemonize). .TP +\fB\-a\fR, \fB\-\-auto\fR +Enable automatic recompilation (start or reload). +.TP \fB\-h\fR, \fB\-\-help\fR Print help and usage. .SS "Actions:" .TP start -Start knot server zone (no\-op if running). +Start knot server daemon (no\-op if running). .TP stop -Stop knot server (no\-op if not running). +Stop knot server daemon (no\-op if not running). .TP restart -Stops and then starts knot server. +Stops and then starts knot server daemon. .TP reload Reload knot configuration and compiled zones. .TP running -check if server is running. +Check if server is running. .TP compile Compile zone file. +.TP +refresh +Refresh all slave zones. +.TP +checkconf +Check server configuration. +.TP +checkzone +Check zones before compiling (accepts specific zones, f.e. 'knotc checkzone example1.com example2.com'). .SH "SEE ALSO" The full documentation for .B Knot diff --git a/src/knotd.8 b/src/knotd.8 index df4b264..cdc450a 100644..100755 --- a/src/knotd.8 +++ b/src/knotd.8 @@ -1,4 +1,4 @@ -.TH "knotd" "8" "November 2011" "CZ.NIC Labs" "Knot DNS, version 0.8" +.TH "knotd" "8" "August 2012" "CZ.NIC Labs" "Knot DNS, version 1.1" .SH NAME .B knotd \- Knot DNS daemon diff --git a/src/libknot/common.h b/src/libknot/common.h index 9b2d8ae..9b2d8ae 100644..100755 --- a/src/libknot/common.h +++ b/src/libknot/common.h diff --git a/src/libknot/consts.h b/src/libknot/consts.h index 4249763..4249763 100644..100755 --- a/src/libknot/consts.h +++ b/src/libknot/consts.h diff --git a/src/libknot/dname.c b/src/libknot/dname.c index 80de030..7d59b6b 100644..100755 --- a/src/libknot/dname.c +++ b/src/libknot/dname.c @@ -163,7 +163,7 @@ static int knot_dname_str_to_wire(const char *name, uint size, return -1; } - dbg_dname("Allocated space for wire format of dname: %p\n", wire); + dbg_dname_verb("Allocated space for wire format of dname: %p\n", wire); if (root) { *wire = '\0'; @@ -181,18 +181,18 @@ static int knot_dname_str_to_wire(const char *name, uint size, assert(w - wire - 1 == ch - (const uint8_t *)name); if (*ch == '.') { - dbg_dname("Position %zd (%p): " - "label length: %u\n", - label_start - wire, - label_start, label_length); + dbg_dname_detail("Position %zd (%p): " + "label length: %u\n", + label_start - wire, + label_start, label_length); *label_start = label_length; labels[label_count++] = label_start - wire; label_start = w; label_length = 0; } else { assert(w - wire < wire_size); - dbg_dname("Position %zd (%p): character: %c\n", - w - wire, w, *ch); + dbg_dname_detail("Position %zd (%p): character: %c\n", + w - wire, w, *ch); *w = *ch; ++label_length; } @@ -205,14 +205,13 @@ static int knot_dname_str_to_wire(const char *name, uint size, --ch; if (*ch == '.') { // put 0 for root label if the name ended with . --w; - dbg_dname("Position %zd (%p): character: (null)\n", - w - wire, w); + dbg_dname_detail("Position %zd (%p): character: (null)\n", + w - wire, w); *w = 0; } else { // otherwise we did not save the last label length - dbg_dname("Position %zd (%p): " - "label length: %u\n", - label_start - wire, - label_start, label_length); + dbg_dname_detail("Position %zd (%p): label length: %u\n", + label_start - wire, + label_start, label_length); *label_start = label_length; labels[label_count++] = label_start - wire; } @@ -277,7 +276,8 @@ static int knot_dname_find_labels(knot_dname_t *dname, int alloc) if (pos - name > size || *pos != '\0' ) { dbg_dname("Wrong wire format of domain name!\n"); - dbg_dname("Position: %d, character: %d, expected size: %d\n", pos - name, *pos, size); + dbg_dname("Position: %d, character: %d, expected size: %d\n", + pos - name, *pos, size); return -1; } @@ -298,12 +298,11 @@ static int knot_dname_find_labels(knot_dname_t *dname, int alloc) static int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2, int cs) { -dbg_dname_exec( +dbg_dname_exec_verb( char *name1 = knot_dname_to_str(d1); char *name2 = knot_dname_to_str(d2); - dbg_dname("Comparing dnames %s and %s\n", - name1, name2); + dbg_dname_verb("Comparing dnames %s and %s\n", name1, name2); for (int i = 0; i < strlen(name1); ++i) { name1[i] = knot_tolower(name1[i]); @@ -312,8 +311,7 @@ dbg_dname_exec( name2[i] = knot_tolower(name2[i]); } - dbg_dname("After to lower: %s and %s\n", - name1, name2); + dbg_dname_detail("After to lower: %s and %s\n", name1, name2); free(name1); free(name2); @@ -325,16 +323,16 @@ dbg_dname_exec( int l1 = d1->label_count; int l2 = d2->label_count; - dbg_dname("Label counts: %d and %d\n", l1, l2); + dbg_dname_detail("Label counts: %d and %d\n", l1, l2); assert(l1 >= 0); assert(l2 >= 0); // compare labels from last to first while (l1 > 0 && l2 > 0) { - dbg_dname("Comparing labels %d and %d\n", - l1 - 1, l2 - 1); - dbg_dname(" at offsets: %d and %d\n", - d1->labels[l1 - 1], d2->labels[l2 - 1]); + dbg_dname_detail("Comparing labels %d and %d\n", + l1 - 1, l2 - 1); + dbg_dname_detail(" at offsets: %d and %d\n", + d1->labels[l1 - 1], d2->labels[l2 - 1]); int res = knot_dname_compare_labels( &d1->name[d1->labels[--l1]], &d2->name[d2->labels[--l2]], @@ -434,26 +432,6 @@ dbg_dname_exec_verb( /*----------------------------------------------------------------------------*/ -//int knot_dname_from_wire(knot_dname_t *dname, const uint8_t *name, -// uint size) -//{ -// int i = 0; -// uint8_t labels[KNOT_MAX_DNAME_LABELS]; -// int label_i = 0; - -// while (name[i] != 0) { -// labels[label_i++] = i; -// uint8_t label_length = name[i]; -// if (i + label_length >= size) { -// return -2; -// } -// for (int j = 1; j <= label_length; ++j) { -// } -// } -//} - -/*----------------------------------------------------------------------------*/ - knot_dname_t *knot_dname_new_from_wire(const uint8_t *name, uint size, struct knot_node *node) { @@ -510,12 +488,10 @@ knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire, return NULL; } labels[l] = i; - dbg_dname("Next label (%d.) position: %zu\n", l, i); + dbg_dname_detail("Next label (%d.) position: %zu\n", l, i); if (knot_wire_is_pointer(wire + p)) { // pointer. - -// printf("Pointer.\n"); size_t ptr = knot_wire_get_pointer(wire + p); /* Check that the pointer points backwards @@ -622,6 +598,10 @@ knot_dname_t *knot_dname_deep_copy(const knot_dname_t *dname) /* dname_new_from_wire() does not accept non-FQDN dnames, so we * do the copy by hand. It's faster anyway */ + if (dname == NULL) { + return NULL; + } + knot_dname_t *copy = knot_dname_new(); CHECK_ALLOC(copy, NULL); @@ -771,7 +751,44 @@ struct knot_node *knot_dname_get_node(const knot_dname_t *dname) if (dname == NULL) { return NULL; } - return dname->node; + + knot_node_t *node = dname->node; + + /* + * If the zone contains new zone contents (during an update), we should + * return new node. Check if the node has the new node set. If it does + * not, it means this is already the new node. If it has, return the + * new node. If the new node is empty, return NULL, as the node will be + * deleted later. + */ +dbg_dname_exec_detail( + dbg_dname_detail("Getting node from dname: node: %p, zone: %p\n", node, + knot_node_zone(node)); + if (node != NULL && knot_node_zone(node) != NULL + && knot_zone_contents(knot_node_zone(node)) != NULL) { + dbg_dname_detail("zone contents gen: %d, new node of the node: " + "%p, is empty: %d\n", + knot_zone_contents_gen_is_new(knot_zone_contents( + knot_node_zone(node))), + knot_node_new_node(node), + knot_node_new_node(node) + ? knot_node_is_empty(knot_node_new_node(node)) + : -1); + } +); + + if (node && knot_node_zone(node) + && knot_zone_contents(knot_node_zone(node)) + && knot_zone_contents_gen_is_new(knot_zone_contents( + knot_node_zone(node))) + && knot_node_new_node(node) != NULL) { + node = knot_node_get_new_node(node); + if (knot_node_is_empty(node)) { + node = NULL; + } + } + + return node; } /*----------------------------------------------------------------------------*/ @@ -785,7 +802,20 @@ void knot_dname_set_node(knot_dname_t *dname, knot_node_t *node) void knot_dname_update_node(knot_dname_t *dname) { +dbg_dname_exec_detail( + char *name = knot_dname_to_str(dname); + dbg_dname_detail("Updating node pointer in dname %p: %s. Before: %p\n", + dname, name, dname->node); + free(name); +); + knot_node_update_ref(&dname->node); + dbg_dname_detail("After: %p\n", dname->node); + + if (knot_node_is_empty(dname->node)) { + dbg_dname_detail("Node is empty, setting to NULL.\n"); + dname->node = NULL; + } } /*----------------------------------------------------------------------------*/ @@ -829,12 +859,6 @@ knot_dname_t *knot_dname_left_chop(const knot_dname_t *dname) return parent; } - -// if (dname->label_count <= 1) { -// /* Nothing to chop. */ -// return NULL; -// } - parent->size = dname->size - dname->name[0] - 1; parent->name = (uint8_t *)malloc(parent->size); @@ -894,12 +918,11 @@ void knot_dname_left_chop_no_copy(knot_dname_t *dname) int knot_dname_is_subdomain(const knot_dname_t *sub, const knot_dname_t *domain) { -dbg_dname_exec( +dbg_dname_exec_verb( char *name1 = knot_dname_to_str(sub); char *name2 = knot_dname_to_str(domain); - dbg_dname("Checking if %s is subdomain of %s\n", - name1, name2); + dbg_dname_verb("Checking if %s is subdomain of %s\n", name1, name2); free(name1); free(name2); ); @@ -919,7 +942,7 @@ dbg_dname_exec( int l1 = sub->label_count; int l2 = domain->label_count; - dbg_dname("Label counts: %d and %d\n", l1, l2); + dbg_dname_detail("Label counts: %d and %d\n", l1, l2); if (l1 <= l2) { // if sub does not have more labes than domain return 0; // it is not its subdomain @@ -927,10 +950,10 @@ dbg_dname_exec( // compare labels from last to first while (l1 > 0 && l2 > 0) { - dbg_dname("Comparing labels %d and %d\n", - l1 - 1, l2 - 1); - dbg_dname(" at offsets: %d and %d\n", - sub->labels[l1 - 1], domain->labels[l2 - 1]); + dbg_dname_detail("Comparing labels %d and %d\n", + l1 - 1, l2 - 1); + dbg_dname_detail(" at offsets: %d and %d\n", + sub->labels[l1 - 1], domain->labels[l2 - 1]); // if some labels do not match if (knot_dname_compare_labels(&sub->name[sub->labels[--l1]], &domain->name[domain->labels[--l2]], 0) @@ -989,15 +1012,6 @@ int knot_dname_label_count(const knot_dname_t *dname) uint8_t knot_dname_label_size(const knot_dname_t *dname, int i) { -// printf("Returning size of %d. label starting on %d\n", -// i, dname->labels[i]); -// printf("Label count: %d, size of %d. label: %d, size of %d.label: %d\n", -// dname->label_count, i, dname->labels[i], i + 1, -// dname->labels[i+1]); -// printf("Size from the name: %u\n", dname->name[dname->labels[i]]); -// printf("Size from label offsets: %u\n", -// dname->labels[i + 1] - dname->labels[i]); - assert(i >= 0); assert(dname->size == 1 || i + 1 == dname->label_count || dname->labels[i + 1] - dname->labels[i] - 1 @@ -1007,17 +1021,16 @@ uint8_t knot_dname_label_size(const knot_dname_t *dname, int i) /*----------------------------------------------------------------------------*/ -knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *dname, - int size, - const knot_dname_t *suffix) +knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *dname, int size, + const knot_dname_t *suffix) { -dbg_dname_exec( +dbg_dname_exec_verb( char *name = knot_dname_to_str(dname); - dbg_dname("Replacing suffix of name %s, size %d with ", name, - size); + dbg_dname_verb("Replacing suffix of name %s, size %d with ", name, + size); free(name); name = knot_dname_to_str(suffix); - dbg_dname("%s (size %d)\n", name, suffix->size); + dbg_dname_verb("%s (size %d)\n", name, suffix->size); free(name); ); knot_dname_t *res = knot_dname_new(); @@ -1025,7 +1038,7 @@ dbg_dname_exec( res->size = dname->size - size + suffix->size; - dbg_dname("Allocating %d bytes...\n", res->size); + dbg_dname_detail("Allocating %d bytes...\n", res->size); res->name = (uint8_t *)malloc(res->size); if (res->name == NULL) { knot_dname_free(&res); @@ -1034,12 +1047,12 @@ dbg_dname_exec( dbg_dname_hex((char *)res->name, res->size); - dbg_dname("Copying %d bytes from the original name.\n", - dname->size - size); + dbg_dname_detail("Copying %d bytes from the original name.\n", + dname->size - size); memcpy(res->name, dname->name, dname->size - size); dbg_dname_hex((char *)res->name, res->size); - dbg_dname("Copying %d bytes from the suffix.\n", suffix->size); + dbg_dname_detail("Copying %d bytes from the suffix.\n", suffix->size); memcpy(res->name + dname->size - size, suffix->name, suffix->size); dbg_dname_hex((char *)res->name, res->size); @@ -1057,13 +1070,6 @@ void knot_dname_free(knot_dname_t **dname) return; } -// char *name = knot_dname_to_str((*dname)); - -// printf("freeing in dname: %s %p\n", name, *dname); - -// free(name); - - if ((*dname)->name != NULL) { free((*dname)->name); } @@ -1116,13 +1122,13 @@ knot_dname_t *knot_dname_cat(knot_dname_t *d1, const knot_dname_t *d2) return NULL; } - dbg_dname("1: copying %d bytes from adress %p to %p\n", - d1->size, d1->name, new_dname); + dbg_dname_detail("1: copying %d bytes from adress %p to %p\n", + d1->size, d1->name, new_dname); memcpy(new_dname, d1->name, d1->size); - dbg_dname("2: copying %d bytes from adress %p to %p\n", - d2->size, d2->name, new_dname + d1->size); + dbg_dname_detail("2: copying %d bytes from adress %p to %p\n", + d2->size, d2->name, new_dname + d1->size); memcpy(new_dname + d1->size, d2->name, d2->size); diff --git a/src/libknot/dname.h b/src/libknot/dname.h index 473bca7..43bc4d2 100644..100755 --- a/src/libknot/dname.h +++ b/src/libknot/dname.h @@ -224,8 +224,6 @@ const struct knot_node *knot_dname_node(const knot_dname_t *dname); struct knot_node *knot_dname_get_node(const knot_dname_t *dname); -void knot_dname_set_node(knot_dname_t *dname, struct knot_node *node); - void knot_dname_update_node(knot_dname_t *dname); void knot_dname_set_node(knot_dname_t *dname, struct knot_node *node); diff --git a/src/libknot/edns.c b/src/libknot/edns.c index ea630dd..8e1efcc 100644..100755 --- a/src/libknot/edns.c +++ b/src/libknot/edns.c @@ -71,21 +71,20 @@ int knot_edns_new_from_wire(knot_opt_rr_t *opt_rr, const uint8_t *wire, // owner of EDNS OPT RR must be root (0) if (*pos != 0) { dbg_edns("EDNS packet malformed (expected root " - "domain as owner).\n"); + "domain as owner).\n"); return KNOT_EMALF; } pos += 1; // check the type of the record (must be OPT) if (knot_wire_read_u16(pos) != KNOT_RRTYPE_OPT) { - dbg_edns("EDNS packet malformed (expected OPT type" - ".\n"); + dbg_edns("EDNS packet malformed (expected OPT type.\n"); return KNOT_EMALF; } pos += 2; opt_rr->payload = knot_wire_read_u16(pos); - dbg_edns("Parsed payload: %u\n", opt_rr->payload); + dbg_edns_verb("Parsed payload: %u\n", opt_rr->payload); pos += 2; opt_rr->ext_rcode = *(pos++); @@ -107,18 +106,18 @@ int knot_edns_new_from_wire(knot_opt_rr_t *opt_rr, const uint8_t *wire, while (parsed < rdlength + KNOT_EDNS_MIN_SIZE) { if (max_size - parsed < 4) { dbg_edns("Not enough data to parse OPT RR" - " OPTION header.\n"); + " OPTION header.\n"); return KNOT_EFEWDATA; } uint16_t code = knot_wire_read_u16(pos); pos += 2; uint16_t length = knot_wire_read_u16(pos); pos += 2; - dbg_edns("EDNS OPTION: Code: %u, Length: %u\n", - code, length); + dbg_edns_verb("EDNS OPTION: Code: %u, Length: %u\n", + code, length); if (max_size - parsed - 4 < length) { dbg_edns("Not enough data to parse OPT RR" - " OPTION data.\n"); + " OPTION data.\n"); return KNOT_EFEWDATA; } int ret; @@ -144,28 +143,28 @@ int knot_edns_new_from_rr(knot_opt_rr_t *opt_rr, return KNOT_EBADARG; } - dbg_edns("Parsing payload.\n"); + dbg_edns_verb("Parsing payload.\n"); opt_rr->payload = knot_rrset_class(rrset); // the TTL has switched bytes uint32_t ttl; - dbg_edns("TTL: %u\n", knot_rrset_ttl(rrset)); + dbg_edns_detail("TTL: %u\n", knot_rrset_ttl(rrset)); knot_wire_write_u32((uint8_t *)&ttl, knot_rrset_ttl(rrset)); // first byte of TTL is extended RCODE - dbg_edns("TTL: %u\n", ttl); + dbg_edns_detail("TTL: %u\n", ttl); memcpy(&opt_rr->ext_rcode, &ttl, 1); - dbg_edns("Parsed extended RCODE: %u.\n", opt_rr->ext_rcode); + dbg_edns_detail("Parsed extended RCODE: %u.\n", opt_rr->ext_rcode); // second is the version memcpy(&opt_rr->version, (const uint8_t *)(&ttl) + 1, 1); - dbg_edns("Parsed version: %u.\n", opt_rr->version); + dbg_edns_detail("Parsed version: %u.\n", opt_rr->version); // third and fourth are flags opt_rr->flags = knot_wire_read_u16((const uint8_t *)(&ttl) + 2); - dbg_edns("Parsed flags: %u.\n", opt_rr->flags); + dbg_edns_detail("Parsed flags: %u.\n", opt_rr->flags); // size of the header, options are counted elsewhere opt_rr->size = 11; int rc = 0; - dbg_edns("Parsing options.\n"); + dbg_edns_verb("Parsing options.\n"); const knot_rdata_t *rdata = knot_rrset_rdata(rrset); // in OPT RR, all RDATA are in one RDATA item stored as BINARY data, @@ -206,7 +205,7 @@ int knot_edns_new_from_rr(knot_opt_rr_t *opt_rr, } - dbg_edns("EDNS created.\n"); + dbg_edns_verb("EDNS created.\n"); return KNOT_EOK; } @@ -316,10 +315,10 @@ int knot_edns_add_option(knot_opt_rr_t *opt_rr, uint16_t code, free(old_options); } - dbg_edns("Adding option.\n"); - dbg_edns("Code: %u.\n", code); - dbg_edns("Length: %u.\n", length); - dbg_edns("Data: %p.\n", data); + dbg_edns_verb("Adding option.\n"); + dbg_edns_verb("Code: %u.\n", code); + dbg_edns_verb("Length: %u.\n", length); + dbg_edns_verb("Data: %p.\n", data); opt_rr->options[opt_rr->option_count].data = (uint8_t *)malloc(length); CHECK_ALLOC_LOG(opt_rr->options[opt_rr->option_count].data, KNOT_ENOMEM); @@ -370,9 +369,9 @@ short knot_edns_to_wire(const knot_opt_rr_t *opt_rr, uint8_t *wire, uint8_t *pos = wire; - dbg_edns_detail("Putting OPT RR to the wire format. Size: %d, " - "position: %zu\n", - opt_rr->size, (size_t)(pos - wire)); + dbg_edns_verb("Putting OPT RR to the wire format. Size: %d, " + "position: %zu\n", + opt_rr->size, (size_t)(pos - wire)); *(pos++) = 0; knot_wire_write_u16(pos, KNOT_RRTYPE_OPT); diff --git a/src/libknot/edns.h b/src/libknot/edns.h index 022ac36..022ac36 100644..100755 --- a/src/libknot/edns.h +++ b/src/libknot/edns.h diff --git a/src/libknot/hash/cuckoo-hash-table.c b/src/libknot/hash/cuckoo-hash-table.c index 9db32bf..7358e14 100644..100755 --- a/src/libknot/hash/cuckoo-hash-table.c +++ b/src/libknot/hash/cuckoo-hash-table.c @@ -30,7 +30,6 @@ #include "util/debug.h" #include "hash/cuckoo-hash-table.h" #include "hash/hash-functions.h" -#include "common/dynamic-array.h" /*----------------------------------------------------------------------------*/ /* Macros and inline functions */ @@ -283,6 +282,8 @@ static int ck_stash_is_full(const ck_hash_table_t *table) */ static inline void ck_clear_item(ck_hash_table_item_t **item) { + dbg_stash("[EMPTY STASH] [CREATE] setting item %p (%p) to NULL.\n", + item, *item); *item = NULL; } @@ -320,49 +321,11 @@ static inline void ck_swap_items(ck_hash_table_item_t **item1, static inline void ck_put_item(ck_hash_table_item_t **to, ck_hash_table_item_t *item) { - *to = item; -} - -/*----------------------------------------------------------------------------*/ -/*! - * \brief Checks if the hash was already used twice. - * - * If yes, it means we entered a loop in the hashing process, so we must stop. - * Otherwise it remembers that we used the hash. - * - * \note According to Kirsch, et al. a check that at most one hash was used - * twice should be sufficient. We will retain our version for now. - * - * \param used Array of used table indices (hashes). - * \param hash Hash to check. - * - * \retval -1 if the hash was already used twice. - * \retval -2 if an error occured. - * \retval 0 if the hash was not used twice yet. - */ -static uint ck_check_used_twice(da_array_t *used, uint32_t hash) -{ - uint i = 0, found = 0; - while (i < da_get_count(used) && found < 2) { - if (((uint *)(da_get_items(used)))[i] == hash) { - ++found; - } - ++i; - } - - if (found == 2) { - dbg_ck_hash("Hashing entered infinite loop.\n"); - return -1; - } else { - if (da_reserve(used, 1) < 0) { - ERR_ALLOC_FAILED; - return -2; - } - ((uint *)da_get_items(used))[da_get_count(used)] = hash; - da_occupy(used, 1); - assert(da_get_count(used) < RELOCATIONS_MAX); - return 0; + if (item == NULL) { + dbg_stash("[EMPTY STASH] [CREATE] Putting NULL to item %p.\n", + to); } + *to = item; } /*----------------------------------------------------------------------------*/ @@ -419,12 +382,14 @@ static ck_hash_table_item_t **ck_find_in_stash(const ck_hash_table_t *table, * non-NULL. */ if (item->item && ck_items_match(item->item, key, length)) { - dbg_ck("Comparing item in stash (key: %.*s (size %zu))" - "with searched item (key %.*s (size %u)).\n", - (int)item->item->key_length, item->item->key, - item->item->key_length, (int)length, key, - length); + dbg_ck_detail("Comparing item in stash (key: %.*s (size" + " %zu)) with searched item (key %.*s (size %u)).\n", + (int)item->item->key_length, item->item->key, + item->item->key_length, (int)length, key, length); return &item->item; + } else if (item->item == NULL) { + dbg_stash("[EMPTY STASH] [FIND] STASH ITEM IS EMPTY: " + "%p (%p)\n", item, item->item); } item = item->next; } @@ -448,18 +413,18 @@ static ck_hash_table_item_t **ck_find_gen(const ck_hash_table_t *table, size_t length, uint8_t generation) { uint32_t hash; - dbg_ck("Finding item in generation: %u\n", generation); + dbg_ck_verb("Finding item in generation: %u\n", generation); // check hash tables for (uint t = 0; t < table->table_count; ++t) { hash = HASH(&table->hash_system, key, length, table->table_size_exp, generation, t); - dbg_ck("Hash: %u, key: %.*s\n", hash, (int)length, key); - dbg_ck("Table %d, hash: %u, item: %p\n", t + 1, hash, - table->tables[t][hash]); + dbg_ck_detail("Hash: %u, key: %.*s\n", hash, (int)length, key); + dbg_ck_detail("Table %d, hash: %u, item: %p\n", t + 1, hash, + table->tables[t][hash]); if (table->tables[t][hash] != NULL) { - dbg_ck("Table %u, key: %.*s, value: %p, key " + dbg_ck_detail("Table %u, key: %.*s, value: %p, key " "length: %zu\n", t + 1, (int)table->tables[t][hash]->key_length, table->tables[t][hash]->key, @@ -475,16 +440,16 @@ static ck_hash_table_item_t **ck_find_gen(const ck_hash_table_t *table, } // try to find in stash - dbg_ck("Searching in stash...\n"); + dbg_ck_verb("Searching in stash...\n"); ck_hash_table_item_t **found = ck_find_in_stash(table, key, length); - dbg_ck("Found pointer: %p\n", found); + dbg_ck_verb("Found pointer: %p\n", found); if (found != NULL) { - dbg_ck("Stash, key: %.*s, value: %p, key length: %zu\n", - (int)(*found)->key_length, (*found)->key, - (*found)->value, (*found)->key_length); + dbg_ck_verb("Stash, key: %.*s, value: %p, key length: %zu\n", + (int)(*found)->key_length, (*found)->key, + (*found)->value, (*found)->key_length); } // ck_find_in_buffer returns NULL if not found, otherwise pointer to @@ -520,6 +485,136 @@ static ck_hash_table_item_t **ck_find_item_nc(const ck_hash_table_t *table, } /*----------------------------------------------------------------------------*/ +/* Lightweight dynamic array for keeping track of used items. */ +/*----------------------------------------------------------------------------*/ + +typedef struct ck_used { + uint32_t *items; + uint array_count; + size_t *counts; + size_t allocated; +} ck_used_t; + +/*----------------------------------------------------------------------------*/ + +static int ck_used_create(ck_used_t *used, uint table_count) +{ + // all tables in one array + used->items = malloc(table_count * RELOCATIONS_DEFAULT + * sizeof(uint32_t)); + if (used->items == NULL) { + return -1; + } + + used->counts = malloc(table_count * sizeof(size_t)); + if (used->counts == NULL) { + free(used->items); + return -1; + } + + used->array_count = table_count; + used->allocated = RELOCATIONS_DEFAULT; + + for (int i = 0; i < table_count; ++i) { + used->counts[i] = 0; + } + + memset(used->items, 0, + used->array_count * used->allocated * sizeof(uint32_t)); + + return 0; +} + +/*----------------------------------------------------------------------------*/ + +static void ck_used_free(ck_used_t *used) +{ + free(used->items); + free(used->counts); + used->allocated = 0; +} + +/*----------------------------------------------------------------------------*/ + +static int ck_used_add(ck_used_t *used, uint table_nr, uint32_t to_add) +{ + dbg_ck_hash_verb("1) Table nr: %u, count: %zu, allocated: %zu\n", + table_nr, used->counts[table_nr], used->allocated); + + if (used->counts[table_nr] == used->allocated) { + dbg_ck_hash_verb("Reallocating...\n"); + size_t allocated_new = used->allocated * 2; + uint32_t *items_new = malloc(used->array_count * allocated_new + * sizeof(uint32_t)); + if (items_new == NULL) { + return -1; + } + + memcpy(items_new, used->items, + used->allocated * used->array_count); + + uint32_t *old_items = used->items; + + used->items = items_new; + used->allocated = allocated_new; + + free(old_items); + } + + dbg_ck_hash_verb("2) Table nr: %u, count: %zu, allocated: %zu\n", + table_nr, used->counts[table_nr], used->allocated); + + assert(used->counts[table_nr] < used->allocated); + used->items[table_nr * used->allocated + used->counts[table_nr]] + = to_add; + ++used->counts[table_nr]; + + dbg_ck_hash_verb("3)Table nr: %u, count: %zu, allocated: %zu\n", + table_nr, used->counts[table_nr], used->allocated); + + return 0; +} + +/*----------------------------------------------------------------------------*/ +/*! + * \brief Checks if the hash was already used twice. + * + * If yes, it means we entered a loop in the hashing process, so we must stop. + * Otherwise it remembers that we used the hash. + * + * \note According to Kirsch, et al. a check that at most one hash was used + * twice should be sufficient. We will retain our version for now. + * + * \param used Array of used table indices (hashes). + * \param hash Hash to check. + * + * \retval -1 if the hash was already used twice. + * \retval -2 if an error occured. + * \retval 0 if the hash was not used twice yet. + */ +static int ck_check_used_twice(ck_used_t *used, uint table_nr, + uint32_t hash) +{ + uint i = 0, found = 0; + + while (i < used->counts[table_nr] && found < 2) { + if (used->items[table_nr * used->allocated + i] == hash) { + ++found; + } + ++i; + } + + if (found == 2) { + dbg_ck_hash("Hashing entered infinite loop.\n"); + return -1; + } else { + return ck_used_add(used, table_nr, hash); + } +} + +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ /*! * \brief Hashes the given item using the given generation. * @@ -532,20 +627,26 @@ static ck_hash_table_item_t **ck_find_item_nc(const ck_hash_table_t *table, * * \retval 0 if successful and no loop occured. * \retval 1 if a loop occured and the item was inserted to the \a free place. + * \retval < 0 if an error occured. */ static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash, ck_hash_table_item_t **free, uint8_t generation) { - da_array_t used[table->table_count]; - for (uint i = 0; i < table->table_count; ++i) { - da_initialize(&used[i], RELOCATIONS_DEFAULT, sizeof(uint)); +// da_array_t used[table->table_count]; +// for (uint i = 0; i < table->table_count; ++i) { +// da_initialize(&used[i], RELOCATIONS_DEFAULT, sizeof(uint)); +// } + ck_used_t used; + int ret = ck_used_create(&used, table->table_count); + if (ret != 0) { + return -1; } // hash until empty cell is encountered or until loop appears - dbg_ck_hash("Hashing key: %.*s of size %zu.\n", - (int)(*to_hash)->key_length, (*to_hash)->key, - (*to_hash)->key_length); + dbg_ck_hash_verb("Hashing key: %.*s of size %zu.\n", + (int)(*to_hash)->key_length, (*to_hash)->key, + (*to_hash)->key_length); uint next_table = 0; @@ -553,21 +654,24 @@ static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash, (*to_hash)->key_length, table->table_size_exp, generation, next_table); - dbg_ck_hash("New hash: %u.\n", hash); + dbg_ck_hash_detail("New hash: %u.\n", hash); assert(hash < hashsize(table->table_size_exp)); - ((uint *)da_get_items(&used[next_table])) - [da_get_count(&used[next_table])] = hash; + ret = ck_used_add(&used, next_table, hash); + if (ret != 0) { + return -2; + } + ck_hash_table_item_t **next = &table->tables[next_table][hash]; - dbg_ck_hash("Item to be moved: %p, place in table: %p\n", - *next, next); + dbg_ck_hash_detail("Item to be moved: %p, place in table: %p\n", + *next, next); ck_hash_table_item_t **moving = to_hash; int loop = 0; while (*next != NULL) { - dbg_ck_hash("Swapping items to hash: %p and Moving: %p\n", - to_hash, moving); + dbg_ck_hash_detail("Swapping items: To hash: %p, Moving: %p\n", + to_hash, moving); ck_swap_items(to_hash, moving); // first time it's unnecessary // set the generation of the inserted item to the next @@ -575,9 +679,10 @@ static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash, moving = next; - dbg_ck_hash("Moving item from table %u, key: %.*s, hash %u ", - next_table + 1, (int)(*moving)->key_length, - (*moving)->key, hash); + dbg_ck_hash_detail("Moving item from table %u, key: %.*s, hash " + "%u \n", next_table + 1, + (int)(*moving)->key_length, + (*moving)->key, hash); // if rehashing and the 'next' item is from the old generation, // start from table 1 @@ -594,31 +699,31 @@ static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash, next = &table->tables[next_table][hash]; - dbg_ck_hash("to table %u, hash %u, item: %p, place: %p\n", - next_table + 1, hash, *next, next); + dbg_ck_hash_detail("To table %u, hash %u, item: %p, place: %p" + "\n", next_table + 1, hash, *next, next); if ((*next) != NULL) { - dbg_ck_hash("Table %u, hash: %u, key: %.*s\n", - next_table + 1, hash, - (int)(*next)->key_length, (*next)->key); + dbg_ck_hash_detail("Table %u, hash: %u, key: %.*s\n", + next_table + 1, hash, + (int)(*next)->key_length, (*next)->key); } // check if this cell wasn't already used in this item's hashing - if (ck_check_used_twice(&used[next_table], hash) != 0) { + if (ck_check_used_twice(&used, next_table, hash) != 0) { next = free; loop = -1; break; } } - dbg_ck_hash("Putting pointer %p (*moving) to item %p (next).\n", - *moving, next); + dbg_ck_hash_detail("Putting pointer %p (*moving) to item %p (next).\n", + *moving, next); ck_put_item(next, *moving); // set the new generation for the inserted item SET_GENERATION(&(*next)->timestamp, generation); - dbg_ck_hash("Putting pointer %p (*old) to item %p (moving).\n", - *to_hash, moving); + dbg_ck_hash_detail("Putting pointer %p (*old) to item %p (moving).\n", + *to_hash, moving); ck_put_item(moving, *to_hash); @@ -626,9 +731,7 @@ static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash, SET_GENERATION(&(*moving)->timestamp, generation); *to_hash = NULL; - for (uint i = 0; i < table->table_count; ++i) { - da_destroy(&used[i]); - } + ck_used_free(&used); return loop; } @@ -670,6 +773,10 @@ static void ck_rollback_rehash(ck_hash_table_t *table) */ int ck_add_to_stash(ck_hash_table_t *table, ck_hash_table_item_t *item) { + if (item == NULL) { + dbg_stash("[EMPTY STASH] [CREATE] ADDING NULL ITEM TO STASH\n"); + } + ck_stash_item_t *new_item = (ck_stash_item_t *)malloc(sizeof(ck_stash_item_t)); if (new_item == NULL) { @@ -681,8 +788,8 @@ int ck_add_to_stash(ck_hash_table_t *table, ck_hash_table_item_t *item) new_item->next = table->stash; table->stash = new_item; - dbg_ck_hash("First item in stash (now inserted): key: %.*s (size %zu)" - ", value: %p\n", (int)table->stash->item->key_length, + dbg_ck_hash_verb("First item in stash (now inserted): key: %.*s (size" + " %zu), value: %p\n", (int)table->stash->item->key_length, table->stash->item->key, table->stash->item->key_length, table->stash->item->value); @@ -739,12 +846,12 @@ ck_hash_table_t *ck_create_table(uint items) dbg_ck("Creating hash table for %u items.\n", items); dbg_ck("Exponent: %u, number of tables: %u\n ", - table->table_size_exp, table->table_count); + table->table_size_exp, table->table_count); dbg_ck("Table size: %u items, each %zu bytes, total %zu bytes\n", - hashsize(table->table_size_exp), - sizeof(ck_hash_table_item_t *), - hashsize(table->table_size_exp) - * sizeof(ck_hash_table_item_t *)); + hashsize(table->table_size_exp), + sizeof(ck_hash_table_item_t *), + hashsize(table->table_size_exp) + * sizeof(ck_hash_table_item_t *)); // create tables for (uint t = 0; t < table->table_count; ++t) { @@ -805,18 +912,6 @@ void ck_destroy_table(ck_hash_table_t **table, void (*dtor_value)(void *value), } // destroy items in stash -// ck_hash_table_item_t **stash = -// ((ck_hash_table_item_t **)(da_get_items(&(*table)->stash))); -// for (uint i = 0; i < da_get_count(&(*table)->stash); ++i) { -// assert(stash[i] != NULL); -// if (dtor_value) { -// dtor_value(stash[i]->value); -// } -// if (delete_key != 0) { -// free((void *)stash[i]->key); -// } -// free((void *)stash[i]); -// } ck_stash_item_t *item = (*table)->stash; while (item != NULL) { // disconnect the item @@ -842,8 +937,6 @@ void ck_destroy_table(ck_hash_table_t **table, void (*dtor_value)(void *value), for (uint t = 0; t < (*table)->table_count; ++t) { free((*table)->tables[t]); } - // destroy stash -// da_destroy(&(*table)->stash); pthread_mutex_unlock(&(*table)->mtx_table); // destroy mutex, assuming that here noone will lock the mutex again @@ -884,7 +977,7 @@ void ck_table_free(ck_hash_table_t **table) int ck_resize_table(ck_hash_table_t *table) { dbg_ck("Resizing hash table.\n"); - + /* * Easiest is just to increment the exponent, resulting in doubling * the table sizes. This is not very memory-effective, but should do @@ -900,7 +993,7 @@ int ck_resize_table(ck_hash_table_t *table) ck_hash_table_item_t **tables_old[MAX_TABLES]; int exp_new = table->table_size_exp + 1; - dbg_ck("New tables exponent: %d\n", exp_new); + dbg_ck_verb("New tables exponent: %d\n", exp_new); for (int t = 0; t < table->table_count; ++t) { if (ck_new_table(&tables_new[t], exp_new) != 0) { @@ -919,14 +1012,14 @@ int ck_resize_table(ck_hash_table_t *table) * sizeof(ck_hash_table_item_t *); // copy the old table items - dbg_ck("Copying to: %p, from %p, size: %zu\n", - tables_new[t], table->tables[t], old_size); + dbg_ck_verb("Copying to: %p, from %p, size: %zu\n", + tables_new[t], table->tables[t], old_size); memcpy(tables_new[t], table->tables[t], old_size); // set the rest to 0 - dbg_ck("Setting to 0 from %p, size %zu\n", - tables_new[t] + hashsize(table->table_size_exp), - (hashsize(exp_new) * sizeof(ck_hash_table_item_t *)) - - old_size); + dbg_ck_verb("Setting to 0 from %p, size %zu\n", + tables_new[t] + hashsize(table->table_size_exp), + (hashsize(exp_new) * sizeof(ck_hash_table_item_t *)) + - old_size); memset(tables_new[t] + hashsize(table->table_size_exp), 0, (hashsize(exp_new) * sizeof(ck_hash_table_item_t *)) - old_size); @@ -948,7 +1041,6 @@ int ck_resize_table(ck_hash_table_t *table) } return ck_rehash(table); - //return 0; } int ck_insert_item(ck_hash_table_t *table, const char *key, @@ -980,9 +1072,6 @@ int ck_insert_item(ck_hash_table_t *table, const char *key, } } - // there should be at least 2 free places - //assert(da_try_reserve(&table->stash, 2) == 0); - //da_reserve(&table->stash, 1); ck_hash_table_item_t *free_place = NULL; if (ck_hash_item(table, &new_item, &free_place, table->generation) != 0) { @@ -1104,6 +1193,8 @@ ck_hash_table_item_t *ck_remove_item(ck_hash_table_t *table, const char *key, int ck_shallow_copy(const ck_hash_table_t *from, ck_hash_table_t **to) { + dbg_ck("ck_shallow_copy()\n"); + if (from == NULL || to == NULL) { return -1; } @@ -1176,26 +1267,27 @@ int ck_shallow_copy(const ck_hash_table_t *from, ck_hash_table_t **to) return -2; } - dbg_ck("Copying stash item: %p with item %p, ", si, si->item); - dbg_ck("key: %.*s\n", (int)si->item->key_length, si->item->key); + dbg_ck_detail("Copying stash item: %p with item %p, key: %.*s" + "\n", si, si->item, (int)si->item->key_length, + si->item->key); si_new->item = si->item; *pos = si_new; pos = &si_new->next; si = si->next; - - dbg_ck("Old stash item: %p with item %p, ", si, - ((si == NULL) ? NULL : si->item)); +dbg_ck_exec_detail( + dbg_ck_detail("Old stash item: %p with item %p, \n", si, + ((si == NULL) ? NULL : si->item)); if (si != NULL) { - dbg_ck("key: %.*s\n", (int)si->item->key_length, si->item->key); - } else { - dbg_ck("\n"); + dbg_ck_detail("key: %.*s\n", (int)si->item->key_length, + si->item->key); } - dbg_ck("New stash item: %p with item %p, ", si_new, - si_new->item); - dbg_ck("key: %.*s\n", (int)si_new->item->key_length, - si_new->item->key); + dbg_ck_detail("New stash item: %p with item %p, ", si_new, + si_new->item); + dbg_ck_detail("key: %.*s\n", (int)si_new->item->key_length, + si_new->item->key); +); } *pos = NULL; @@ -1273,11 +1365,12 @@ void ck_deep_copy_cleanup(ck_hash_table_t *table, int table_count) int ck_deep_copy(ck_hash_table_t *from, ck_hash_table_t **to) { + dbg_ck("ck_deep_copy()\n"); + if (from == NULL || to == NULL) { return -1; } - dbg_ck("Allocating new table...\n"); *to = (ck_hash_table_t *)malloc(sizeof(ck_hash_table_t)); if (*to == NULL) { @@ -1341,10 +1434,12 @@ int ck_deep_copy(ck_hash_table_t *from, ck_hash_table_t **to) return -2; } - dbg_ck("Copying stash item: %p with item %p, ", si, si->item); -// dbg_ck("key: %.*s\n", (int)si->item->key_length, si->item->key); + dbg_ck_detail("Copying stash item: %p with item %p, ", si, + si->item); if (si->item == NULL) { + dbg_stash("[EMPTY STASH] [FIND] STASH ITEM IS EMPTY: " + "%p (%p)\n", si, si->item); si_new->item = NULL; si_new->next = NULL; } else { @@ -1367,22 +1462,20 @@ int ck_deep_copy(ck_hash_table_t *from, ck_hash_table_t **to) pos = &si_new->next; si = si->next; -dbg_ck_exec( - dbg_ck("Old stash item: %p with item %p, ", si, - ((si == NULL) ? NULL : si->item)); +dbg_ck_exec_detail( + dbg_ck_detail("Old stash item: %p with item %p, \n", si, + ((si == NULL) ? NULL : si->item)); if (si != NULL && si->item != NULL) { - dbg_ck("key: %.*s\n", (int)si->item->key_length, - si->item->key); - } else { - dbg_ck("\n"); + dbg_ck_detail("key: %.*s\n", (int)si->item->key_length, + si->item->key); } - dbg_ck("New stash item: %p with item %p, ", si_new, - (si_new) ? si_new->item : NULL); + dbg_ck_detail("New stash item: %p with item %p, ", si_new, + (si_new) ? si_new->item : NULL); assert(si_new != NULL); assert(si_new->item != NULL); - dbg_ck("key: %.*s\n", (int)si_new->item->key_length, - si_new->item->key); + dbg_ck_detail("key: %.*s\n", (int)si_new->item->key_length, + si_new->item->key); ); } @@ -1446,13 +1539,13 @@ int ck_rehash(ck_hash_table_t *table) do { // 1) Rehash items from stash - dbg_ck_rehash("Rehashing items from stash.\n"); + dbg_ck_hash_verb("Rehashing items from stash.\n"); ck_stash_item_t *item = table->stash; ck_stash_item_t **item_place = &table->stash; // terminate when at the end; this way the newly added items // (added to the beginning) will be properly ignored while (item != NULL) { - dbg_ck_rehash("Rehashing item with " + dbg_ck_hash_detail("Rehashing item with " "key (length %zu): %.*s, generation: %hu, " "table generation: %hu.\n", item->item->key_length, (int)item->item->key_length, item->item->key, @@ -1481,6 +1574,8 @@ int ck_rehash(ck_hash_table_t *table) assert(item->item == NULL); // and the item should be hashed too // assert(table->hashed == NULL); + dbg_stash("[EMPTY STASH] [CREATE] Created empty" + " item: %p (%p)\n", item, item->item); // fix the pointer from the previous hash item *item_place = item->next; @@ -1501,12 +1596,11 @@ int ck_rehash(ck_hash_table_t *table) // which will be put to the stash ck_hash_table_item_t *free = NULL; assert(table->hashed == NULL); -// ck_hash_table_item_t *old = table->hashed; for (uint t = 0; t < table->table_count; ++t) { uint rehashed = 0; - dbg_ck_rehash("Rehashing table %d.\n", t); + dbg_ck_hash_verb("Rehashing table %d.\n", t); while (rehashed < hashsize(table->table_size_exp)) { @@ -1516,13 +1610,13 @@ int ck_rehash(ck_hash_table_t *table) || !(EQUAL_GENERATIONS( table->tables[t][rehashed]->timestamp, table->generation))) { - dbg_ck_rehash("Skipping item.\n"); + dbg_ck_hash_detail("Skipping item.\n"); ++rehashed; continue; } - dbg_ck_rehash("Rehashing item with hash %u, " - "key (length %zu): %.*s, generation: %hu, " + dbg_ck_hash_detail("Rehashing item with hash %u" + ", key (length %zu): %.*s, generation: %hu, " "table generation: %hu.\n", rehashed, table->tables[t][rehashed]->key_length, (int)(table->tables[t][rehashed]->key_length), @@ -1537,8 +1631,8 @@ int ck_rehash(ck_hash_table_t *table) // get rehashed again ck_clear_item(&table->tables[t][rehashed]); - dbg_ck_rehash("Table generation: %hu, next " - "generation: %hu.\n", + dbg_ck_hash_detail("Table generation: %hu, next" + " generation: %hu.\n", GET_GENERATION(table->generation), NEXT_GENERATION(table->generation)); @@ -1546,8 +1640,8 @@ int ck_rehash(ck_hash_table_t *table) NEXT_GENERATION(table->generation)) != 0) { // loop occured dbg_ck_hash("Hashing entered a loop." - "\n"); - dbg_ck_rehash("Item with key %.*s " + "\n"); + dbg_ck_hash_verb("Item with key %.*s " "inserted into the free slot.\n", free->key_length, free->key); @@ -1561,6 +1655,12 @@ int ck_rehash(ck_hash_table_t *table) free_stash_items; free_stash_items = item->next; + if (free == NULL) { + dbg_stash("[EMPTY STASH] " + "[CREATE] STORING NULL" + " in the stash\n"); + } + item->item = free; item->next = table->stash; table->stash = item; @@ -1579,15 +1679,15 @@ int ck_rehash(ck_hash_table_t *table) } } - dbg_ck_rehash("Old table generation: %u\n", - GET_GENERATION(table->generation)); + dbg_ck_hash("Old table generation: %u\n", + GET_GENERATION(table->generation)); // rehashing completed, switch generation of the table SET_NEXT_GENERATION(&table->generation); - dbg_ck_rehash("New table generation: %u\n", - GET_GENERATION(table->generation)); + dbg_ck_hash("New table generation: %u\n", + GET_GENERATION(table->generation)); // generate new hash functions for the old generation - dbg_ck_rehash("Generating coeficients for generation: %u\n", - NEXT_GENERATION(table->generation)); + dbg_ck_hash("Generating coeficients for generation: %u\n", + NEXT_GENERATION(table->generation)); us_next(&table->hash_system, NEXT_GENERATION(table->generation)); @@ -1609,248 +1709,6 @@ int ck_rehash(ck_hash_table_t *table) } /*----------------------------------------------------------------------------*/ -/*! - * \brief Rehashes the whole table. - * - * \param table Hash table to be rehashed. - * - * \note While rehashing no item should be inserted as it will result in a - * deadlock. - * - * \retval 0 No error. - * \retval -1 Rehashing failed. Some items may have been already moved and the - * rehashing flag remains set. - * - * \todo What if the stash is reallocated during ck_hash_item()? We'd be using - * the old stash for saving items! The old stash would not get deallocated - * (due to RCU - maybe put some rcu_read_lock() here), but the item - * would not be saved into the new stash! - * Maybe add a function for getting a pointer to particular item from - * the dynamic array and protect it using rcu_read_lock(). - * Other option: Do not use pointer to an item in stash in the call to - * ck_hash_item(). Use some new place & put the item to the stash - * afterwards, protecting it using rcu_read_lock() and rcu_assign_pointer. - */ -//int ck_rehash(ck_hash_table_t *table) -//{ -// dbg_ck_rehash("Rehashing items in table.\n"); -// SET_REHASHING_ON(&table->generation); - -// // we already have functions for the next generation, begin rehashing -// // we wil use the last item in the buffer as free cell for hashing -// assert(da_try_reserve(&table->stash, 1) == 0); -// ck_hash_table_item_t *old = (ck_hash_table_item_t *) -// (malloc(sizeof(ck_hash_table_item_t))); - -// do { -// dbg_ck_hash("Rehash!\n"); - -// if (da_get_count(&table->stash) > STASH_SIZE) { -// dbg_ck_hash("STASH RESIZED!!! (new stash size: %d)\n", -// da_get_count(&table->stash)); -// } - -// // rehash items from stash, starting from the last old item -// int stash_i = da_get_count(&table->stash) - 1; -// while (stash_i >= 0) { -// // if item's generation is the new generation, skip -// if (STASH_ITEMS(&table->stash)[stash_i] == NULL -// || !(EQUAL_GENERATIONS(STASH_ITEMS(&table->stash) -// [stash_i]->timestamp, -// table->generation))) { -// dbg_ck_rehash("Skipping item.\n"); -// --stash_i; -// continue; -// } - -// dbg_ck_rehash("Rehashing item from buffer position %u" -// ", key (length %u): %.*s, generation: " -// "%hu, table generation: %hu.\n", -// stash_i, -// STASH_ITEMS(&table->stash)[stash_i]->key_length, -// (int)STASH_ITEMS(&table->stash)[stash_i]->key_length, -// STASH_ITEMS(&table->stash)[stash_i]->key, -// GET_GENERATION( -// STASH_ITEMS(&table->stash)[stash_i]->timestamp), -// GET_GENERATION(table->generation)); - -// // otherwise copy the item for rehashing -// ck_put_item(&old, STASH_ITEMS(&table->stash)[stash_i]); -// // clear the place so that this item will not get -// // rehashed again -// ck_clear_item(&STASH_ITEMS(&table->stash)[stash_i]); -// da_release(&table->stash, 1); - -// // there should be at least one place in the stash -// assert(da_try_reserve(&table->stash, 1) == 0); -// da_reserve(&table->stash, 1); - -// assert(STASH_ITEMS(&table->stash)[stash_i] == NULL); - -// // and start rehashing -// if (ck_hash_item(table, &old, -// &STASH_ITEMS(&table->stash)[stash_i], -// NEXT_GENERATION(table->generation)) != 0) { -// // loop occured -// dbg_ck_hash("Hashing entered a loop.\n"); - -// dbg_ck_rehash("Item with key %.*s inserted " -// "into the stash on position %d.\n", -// STASH_ITEMS(&table->stash) -// [stash_i]->key_length, -// STASH_ITEMS(&table->stash) -// [stash_i]->key, -// da_get_count(&table->stash)); - -// // hashing unsuccessful, the item was inserted -// // into the stash -// da_occupy(&table->stash, 1); -// assert(STASH_ITEMS(&table->stash)[stash_i] -// != NULL); - -// // if only one place left, resize the stash -// // TODO: Why??? -// if (da_reserve(&table->stash, 2) < 0) { -// // stash could not be resized => !!! -// dbg_ck_hash("Failed to rehash items " -// "from " -// "table, no other rehash possible!\n"); -// // so rollback -// ck_rollback_rehash(table); -// // clear the 'old' item -// ck_clear_item(&old); -// return -1; -// } -// } - -// // clear the 'old' item -// ck_clear_item(&old); -// // decrement the index -// --stash_i; -// } - -// uint i = 0; -// while (i < da_get_count(&table->stash)) { -// assert(STASH_ITEMS(&table->stash)[i] != NULL); -// ++i; -// } -// dbg_ck_hash("OK\n"); -// assert(da_try_reserve(&table->stash, 1) == 0); -// assert(STASH_ITEMS(&table->stash)[da_get_count(&table->stash)] -// == NULL); - -// // rehash items from hash tables -// for (uint t = TABLE_FIRST; -// t <= TABLE_LAST(table->table_count); ++t) { -// dbg_ck_rehash("Rehashing items from table %d.\n", -// t + 1); -// uint rehashed = 0; - -// while (rehashed < hashsize(table->table_size_exp)) { - -// // if item's generation is the new generation, -// // skip -// if (table->tables[t][rehashed] == NULL -// || !(EQUAL_GENERATIONS( -// table->tables[t][rehashed]->timestamp, -// table->generation))) { -// dbg_ck_rehash("Skipping item.\n"); -// ++rehashed; -// continue; -// } - -// dbg_ck_rehash("Rehashing item with hash %u, " -// "key (length %u): %.*s, generation: %hu, " -// "table generation: %hu.\n", rehashed, -// table->tables[t][rehashed]->key_length, -// (int)(table->tables[t][rehashed]->key_length), -// table->tables[t][rehashed]->key, -// GET_GENERATION( -// table->tables[t][rehashed]->timestamp), -// GET_GENERATION(table->generation)); - -// // otherwise copy the item for rehashing -// ck_put_item(&old, table->tables[t][rehashed]); -// // clear the place so that this item will not -// // get rehashed again -// ck_clear_item(&table->tables[t][rehashed]); - -// dbg_ck_rehash("Table generation: %hu, next " -// "generation: %hu.\n", -// GET_GENERATION(table->generation), -// NEXT_GENERATION(table->generation)); - -// // and start rehashing -// assert(&old != &STASH_ITEMS(&table->stash)[ -// da_get_count(&table->stash)]); -// assert(da_try_reserve(&table->stash, 1) == 0); -// da_reserve(&table->stash, 1); - -// if (ck_hash_item(table, &old, -// &STASH_ITEMS(&table->stash)[ -// da_get_count(&table->stash)], -// NEXT_GENERATION(table->generation)) != 0) { -// // loop occured -// dbg_ck_hash("Hashing entered a loop." -// "\n"); -// dbg_ck_rehash("Item with key %.*s " -// "inserted into the stash on position " -// "%d.\n", STASH_ITEMS(&table->stash)[ -// da_get_count(&table->stash)] -// ->key_length, -// STASH_ITEMS(&table->stash)[ -// da_get_count(&table->stash)]->key, -// da_get_count(&table->stash)); - -// assert(STASH_ITEMS(&table->stash)[ -// da_get_count(&table->stash)] != NULL); -// // loop occured, the item is already at -// // its new place in the buffer, so just -// // increment the index -// da_occupy(&table->stash, 1); - -// // if only one place left, resize the -// // stash TODO: Why? -// if (da_reserve(&table->stash, 2) < 0) { -// // stash could not be resized -// dbg_ck_hash("Failed to rehash" -// " items from table, no other " -// "rehash possible!\n"); -// // so rollback -// ck_rollback_rehash(table); -// // clear the 'old' item -// ck_clear_item(&old); -// return -1; -// } -// } -// ++rehashed; -// } -// } - -// dbg_ck_rehash("Old table generation: %u\n", -// GET_GENERATION(table->generation)); -// // rehashing completed, switch generation of the table -// SET_NEXT_GENERATION(&table->generation); -// dbg_ck_rehash("New table generation: %u\n", -// GET_GENERATION(table->generation)); -// // generate new hash functions for the old generation -// dbg_ck_rehash("Generating coeficients for generation: %u\n", -// NEXT_GENERATION(table->generation)); -// us_next(NEXT_GENERATION(table->generation)); - -// // repeat rehashing while there are more items in the stash than -// // its initial size -// if (da_get_count(&table->stash) > STASH_SIZE) { -// dbg_ck_rehash("Rehashing again!\n"); -// } -// } while (da_get_count(&table->stash) > STASH_SIZE); - -// SET_REHASHING_OFF(&table->generation); - -// return 0; -//} - -/*----------------------------------------------------------------------------*/ void ck_dump_table(const ck_hash_table_t *table) { @@ -1872,15 +1730,6 @@ void ck_dump_table(const ck_hash_table_t *table) } dbg_ck("Stash:\n"); -// for (i = 0; i < da_get_count(&table->stash); ++i) { -// dbg_ck("Index: %u, Key: %.*s Value: %p.\n", i, -// ((ck_hash_table_item_t **) -// da_get_items(&table->stash))[i]->key_length, -// ((ck_hash_table_item_t **) -// da_get_items(&table->stash))[i]->key, -// ((ck_hash_table_item_t **) -// da_get_items(&table->stash))[i]->value); -// } ck_stash_item_t *item = table->stash; while (item != NULL) { dbg_ck("Hash: %u, Key: %.*s, Value: %p.\n", i, diff --git a/src/libknot/hash/cuckoo-hash-table.h b/src/libknot/hash/cuckoo-hash-table.h index eaa6a89..c0fe9cc 100644..100755 --- a/src/libknot/hash/cuckoo-hash-table.h +++ b/src/libknot/hash/cuckoo-hash-table.h @@ -42,7 +42,6 @@ #include <pthread.h> #include "hash/universal-system.h" -#include "common/dynamic-array.h" /*----------------------------------------------------------------------------*/ diff --git a/src/libknot/hash/hash-functions.c b/src/libknot/hash/hash-functions.c index a33dd6b..a33dd6b 100644..100755 --- a/src/libknot/hash/hash-functions.c +++ b/src/libknot/hash/hash-functions.c diff --git a/src/libknot/hash/hash-functions.h b/src/libknot/hash/hash-functions.h index f23730b..f23730b 100644..100755 --- a/src/libknot/hash/hash-functions.h +++ b/src/libknot/hash/hash-functions.h diff --git a/src/libknot/hash/universal-system.c b/src/libknot/hash/universal-system.c index 096974c..096974c 100644..100755 --- a/src/libknot/hash/universal-system.c +++ b/src/libknot/hash/universal-system.c diff --git a/src/libknot/hash/universal-system.h b/src/libknot/hash/universal-system.h index 25330de..25330de 100644..100755 --- a/src/libknot/hash/universal-system.h +++ b/src/libknot/hash/universal-system.h diff --git a/src/libknot/libknot.h b/src/libknot/libknot.h index a401be7..a401be7 100644..100755 --- a/src/libknot/libknot.h +++ b/src/libknot/libknot.h diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c index 7a6bc4d..6924f44 100644..100755 --- a/src/libknot/nameserver/name-server.c +++ b/src/libknot/nameserver/name-server.c @@ -126,10 +126,9 @@ static const knot_zone_t *ns_get_zone_for_qname(knot_zonedb_t *zdb, static knot_rrset_t *ns_synth_from_wildcard( const knot_rrset_t *wildcard_rrset, const knot_dname_t *qname) { - dbg_ns("Synthetizing RRSet from wildcard...\n"); + dbg_ns_verb("Synthetizing RRSet from wildcard...\n"); knot_dname_t *owner = knot_dname_deep_copy(qname); -// printf("Copied owner ptr: %p\n", owner); knot_rrset_t *synth_rrset = knot_rrset_new( owner, knot_rrset_type(wildcard_rrset), @@ -143,7 +142,7 @@ static knot_rrset_t *ns_synth_from_wildcard( return NULL; } - dbg_ns("Created RRSet header:\n"); + dbg_ns_verb("Created RRSet header:\n"); knot_rrset_dump(synth_rrset, 1); // copy all RDATA @@ -159,15 +158,15 @@ static knot_rrset_t *ns_synth_from_wildcard( return NULL; } - dbg_ns("Copied RDATA:\n"); + dbg_ns_verb("Copied RDATA:\n"); knot_rdata_dump(rdata_copy, knot_rrset_type(synth_rrset), 1); - knot_rrset_add_rdata(synth_rrset, rdata_copy); + int ret = knot_rrset_add_rdata(synth_rrset, rdata_copy); + assert(ret == KNOT_EOK); rdata = knot_rrset_rdata_next(wildcard_rrset, rdata); } -// printf("Synthetized RRSet pointer: %p\n", synth_rrset); return synth_rrset; } @@ -182,8 +181,8 @@ static knot_rrset_t *ns_synth_from_wildcard( * temporary RRSet). * \param rrset RRSet to check (and possibly replace). */ -static void ns_check_wildcard(const knot_dname_t *name, knot_packet_t *resp, - knot_rrset_t **rrset) +static int ns_check_wildcard(const knot_dname_t *name, knot_packet_t *resp, + knot_rrset_t **rrset) { assert(name != NULL); assert(resp != NULL); @@ -193,11 +192,26 @@ static void ns_check_wildcard(const knot_dname_t *name, knot_packet_t *resp, if (knot_dname_is_wildcard((*rrset)->owner)) { knot_rrset_t *synth_rrset = ns_synth_from_wildcard(*rrset, name); - dbg_ns("Synthetized RRSet:\n"); + if (synth_rrset == NULL) { + dbg_ns("Failed to synthetize RRSet from wildcard.\n"); + return KNOT_ERROR; + } + +dbg_ns_exec_verb( + dbg_ns_verb("Synthetized RRSet:\n"); knot_rrset_dump(synth_rrset, 1); - knot_packet_add_tmp_rrset(resp, synth_rrset); +); + + int ret = knot_packet_add_tmp_rrset(resp, synth_rrset); + if (ret != KNOT_EOK) { + dbg_ns("Failed to add sythetized RRSet to tmp list.\n"); + knot_rrset_deep_free(&synth_rrset, 1, 1, 1); + return ret; + } *rrset = synth_rrset; } + + return KNOT_EOK; } /*----------------------------------------------------------------------------*/ @@ -230,23 +244,28 @@ static int ns_add_rrsigs(knot_rrset_t *rrset, knot_packet_t *resp, { knot_rrset_t *rrsigs; - dbg_ns("Adding RRSIGs for RRSet, type: %s.\n", - knot_rrtype_to_string(knot_rrset_type(rrset))); + dbg_ns_verb("Adding RRSIGs for RRSet, type: %s.\n", + knot_rrtype_to_string(knot_rrset_type(rrset))); assert(resp != NULL); assert(add_rrset_to_resp != NULL); - dbg_ns("DNSSEC requested: %d\n", - knot_query_dnssec_requested(knot_packet_query(resp))); - dbg_ns("RRSIGS: %p\n", knot_rrset_rrsigs(rrset)); + dbg_ns_detail("DNSSEC requested: %d\n", + knot_query_dnssec_requested(knot_packet_query(resp))); + dbg_ns_detail("RRSIGS: %p\n", knot_rrset_rrsigs(rrset)); if (DNSSEC_ENABLED && knot_query_dnssec_requested(knot_packet_query(resp)) && (rrsigs = knot_rrset_get_rrsigs(rrset)) != NULL) { if (name != NULL) { - ns_check_wildcard(name, resp, &rrsigs); + int ret = ns_check_wildcard(name, resp, &rrsigs); + if (ret != KNOT_EOK) { + dbg_ns("Failed to process wildcard: %s\n", + knot_strerror(ret)); + return ret; + } } - return add_rrset_to_resp(resp, rrsigs, tc, 0, 0, 1); + return add_rrset_to_resp(resp, rrsigs, tc, 1, 0, 1); } return KNOT_EOK; @@ -265,7 +284,7 @@ static int ns_add_rrsigs(knot_rrset_t *rrset, knot_packet_t *resp, * \param tc Set to 1 if omitting the RRSIG RRSet should result in setting the * TC bit in the response. */ -static void ns_follow_cname(const knot_node_t **node, +static int ns_follow_cname(const knot_node_t **node, const knot_dname_t **qname, knot_packet_t *resp, int (*add_rrset_to_resp)(knot_packet_t *, @@ -273,19 +292,22 @@ static void ns_follow_cname(const knot_node_t **node, int, int, int, int), int tc) { - dbg_ns("Resolving CNAME chain...\n"); + dbg_ns_verb("Resolving CNAME chain...\n"); knot_rrset_t *cname_rrset; + int ret = 0; + while (*node != NULL && (cname_rrset = knot_node_get_rrset(*node, KNOT_RRTYPE_CNAME)) - != NULL) { + != NULL + && (knot_rrset_rdata(cname_rrset) != NULL)) { /* put the CNAME record to answer, but replace the possible wildcard name with qname */ assert(cname_rrset != NULL); - dbg_ns("CNAME RRSet: %p, owner: %p\n", cname_rrset, - cname_rrset->owner); + dbg_ns_detail("CNAME RRSet: %p, owner: %p\n", cname_rrset, + cname_rrset->owner); knot_rrset_t *rrset = cname_rrset; @@ -294,27 +316,67 @@ static void ns_follow_cname(const knot_node_t **node, /* if wildcard node, we must copy the RRSet and replace its owner */ rrset = ns_synth_from_wildcard(cname_rrset, *qname); - knot_packet_add_tmp_rrset(resp, rrset); - add_rrset_to_resp(resp, rrset, tc, 0, 0, 1); - ns_add_rrsigs(cname_rrset, resp, *qname, - add_rrset_to_resp, tc); + if (rrset == NULL) { + dbg_ns("Failed to synthetize RRSet from " + "wildcard RRSet followed from CNAME.\n"); + return KNOT_ERROR; /*! \todo Better error. */ + } + + ret = knot_packet_add_tmp_rrset(resp, rrset); + if (ret != KNOT_EOK) { + dbg_ns("Failed to add synthetized RRSet (CNAME " + "follow) to the tmp RRSets in response." + "\n"); + knot_rrset_deep_free(&rrset, 1, 1, 1); + return ret; + } + + ret = add_rrset_to_resp(resp, rrset, tc, 0, 0, 1); + if (ret != KNOT_EOK) { + dbg_ns("Failed to add synthetized RRSet (CNAME " + "follow) to the response.\n"); + return ret; + } + + ret = ns_add_rrsigs(cname_rrset, resp, *qname, + add_rrset_to_resp, tc); + if (ret != KNOT_EOK) { + dbg_ns("Failed to add RRSIG for the synthetized" + "RRSet (CNAME follow) to the response." + "\n"); + return ret; + } int ret = knot_response_add_wildcard_node( resp, *node, *qname); - - /*! \todo Fix when return values are handled! */ if (ret != KNOT_EOK) { - assert(0); + dbg_ns("Failed to add wildcard node for later " + "processing.\n"); + return ret; } } else { - add_rrset_to_resp(resp, rrset, tc, 0, 0, 1); - ns_add_rrsigs(rrset, resp, *qname, add_rrset_to_resp, - tc); + ret = add_rrset_to_resp(resp, rrset, tc, 0, 0, 1); + + if (ret != KNOT_EOK) { + dbg_ns("Failed to add followed RRSet into" + "the response.\n"); + return ret; + } + + ret = ns_add_rrsigs(rrset, resp, *qname, + add_rrset_to_resp, tc); + + if (ret != KNOT_EOK) { + dbg_ns("Failed to add RRSIG for followed RRSet " + "into the response.\n"); + return ret; + } } - dbg_ns("Using RRSet: %p, owner: %p\n", rrset, rrset->owner); + dbg_ns_detail("Using RRSet: %p, owner: %p\n", rrset, + rrset->owner); -dbg_ns_exec( +dbg_ns_exec_verb( char *name = knot_dname_to_str(knot_rrset_owner(rrset)); dbg_ns("CNAME record for owner %s put to response.\n", name); free(name); @@ -323,18 +385,16 @@ dbg_ns_exec( // get the name from the CNAME RDATA const knot_dname_t *cname = knot_rdata_cname_name( knot_rrset_rdata(cname_rrset)); - dbg_ns("CNAME name from RDATA: %p\n", cname); + dbg_ns_detail("CNAME name from RDATA: %p\n", cname); // change the node to the node of that name *node = knot_dname_node(cname); - dbg_ns("This name's node: %p\n", *node); -// // it is not an old node and if yes, skip it -// if (knot_node_is_old(*node)) { -// *node = knot_node_new_node(*node); -// } + dbg_ns_detail("This name's node: %p\n", *node); // save the new name which should be used for replacing wildcard *qname = cname; - }; + } + + return KNOT_EOK; } /*----------------------------------------------------------------------------*/ @@ -350,47 +410,70 @@ dbg_ns_exec( * * \return Number of RRSets added. */ -static int ns_put_answer(const knot_node_t *node, const knot_dname_t *name, - uint16_t type, knot_packet_t *resp) +static int ns_put_answer(const knot_node_t *node, + const knot_zone_contents_t *zone, + const knot_dname_t *name, + uint16_t type, knot_packet_t *resp, int *added, + int check_any) { - int added = 0; -dbg_ns_exec( + *added = 0; +dbg_ns_exec_verb( char *name_str = knot_dname_to_str(node->owner); - dbg_ns("Putting answers from node %s.\n", name_str); + dbg_ns_verb("Putting answers from node %s.\n", name_str); free(name_str); ); + int ret = KNOT_EOK; + switch (type) { case KNOT_RRTYPE_ANY: { - dbg_ns("Returning all RRTYPES.\n"); + dbg_ns_verb("Returning all RRTYPES.\n"); + + // if ANY not allowed, set TC bit + if (check_any && knot_zone_contents_any_disabled(zone)) { + knot_response_set_tc(resp); + break; + } + knot_rrset_t **rrsets = knot_node_get_rrsets(node); if (rrsets == NULL) { break; } int i = 0; - int ret = 0; knot_rrset_t *rrset; while (i < knot_node_rrset_count(node)) { assert(rrsets[i] != NULL); rrset = rrsets[i]; - dbg_ns(" Type: %s\n", + dbg_ns_detail(" Type: %s\n", knot_rrtype_to_string(knot_rrset_type(rrset))); - ns_check_wildcard(name, resp, &rrset); + ret = ns_check_wildcard(name, resp, &rrset); + if (ret != KNOT_EOK) { + dbg_ns("Failed to process wildcard.\n"); + break; + } + ret = knot_response_add_rrset_answer(resp, rrset, 1, 0, 0, 1); - if (ret >= 0 && (added += 1) - && (ret = ns_add_rrsigs(rrset, resp, name, - knot_response_add_rrset_answer, 1)) - >=0 ) { - added += 1; - } else { - free(rrsets); - rrsets = NULL; + if (ret != KNOT_EOK) { + dbg_ns("Failed add Answer RRSet: %s\n", + knot_strerror(ret)); + break; + } + + *added += 1; + + ret = ns_add_rrsigs(rrset, resp, name, + knot_response_add_rrset_answer, 1); + if (ret != KNOT_EOK) { + dbg_ns("Failed add RRSIGs for Answer RRSet: %s" + "\n", knot_strerror(ret)); break; } + *added += 1; + ++i; } if (rrsets != NULL) { @@ -399,7 +482,7 @@ dbg_ns_exec( break; } case KNOT_RRTYPE_RRSIG: { - dbg_ns("Returning all RRSIGs.\n"); + dbg_ns_verb("Returning all RRSIGs.\n"); knot_rrset_t **rrsets = knot_node_get_rrsets(node); if (rrsets == NULL) { break; @@ -416,15 +499,21 @@ dbg_ns_exec( continue; } - ns_check_wildcard(name, resp, &rrset); + ret = ns_check_wildcard(name, resp, &rrset); + if (ret != KNOT_EOK) { + dbg_ns("Failed to process wildcard.\n"); + break; + } + ret = knot_response_add_rrset_answer(resp, rrset, 1, 0, 0, 1); - - if (ret < 0) { + if (ret != KNOT_EOK) { + dbg_ns("Failed add Answer RRSet: %s\n", + knot_strerror(ret)); break; } - added += 1; + *added += 1; ++i; } free(rrsets); @@ -434,23 +523,41 @@ dbg_ns_exec( int ret = 0; knot_rrset_t *rrset = knot_node_get_rrset(node, type); knot_rrset_t *rrset2 = rrset; - if (rrset != NULL) { - dbg_ns("Found RRSet of type %s\n", - knot_rrtype_to_string(type)); - ns_check_wildcard(name, resp, &rrset2); + if (rrset != NULL && knot_rrset_rdata(rrset) != NULL) { + dbg_ns_verb("Found RRSet of type %s\n", + knot_rrtype_to_string(type)); + + ret = ns_check_wildcard(name, resp, &rrset2); + if (ret != KNOT_EOK) { + dbg_ns("Failed to process wildcard.\n"); + break; + } + ret = knot_response_add_rrset_answer(resp, rrset2, 1, 0, 0, 1); - if (ret >= 0 && (added += 1) - && (ret = ns_add_rrsigs(rrset, resp, name, - knot_response_add_rrset_answer, 1)) > 0) { - added += 1; + if (ret != KNOT_EOK) { + dbg_ns("Failed add Answer RRSet: %s\n", + knot_strerror(ret)); + break; } + + *added += 1; + + ret = ns_add_rrsigs(rrset, resp, name, + knot_response_add_rrset_answer, 1); + + if (ret != KNOT_EOK) { + dbg_ns("Failed add RRSIGs for Answer RRSet: %s" + "\n", knot_strerror(ret)); + break; + } + + *added += 1; } } } - knot_response_set_rcode(resp, KNOT_RCODE_NOERROR); - return added; + return ret; } /*----------------------------------------------------------------------------*/ @@ -471,34 +578,36 @@ dbg_ns_exec( * \param resp Response where to add the Additional data. * \param rrset RRSet to get the Additional data for. */ -static void ns_put_additional_for_rrset(knot_packet_t *resp, - const knot_rrset_t *rrset) +static int ns_put_additional_for_rrset(knot_packet_t *resp, + const knot_rrset_t *rrset) { const knot_node_t *node = NULL; const knot_rdata_t *rdata = NULL; const knot_dname_t *dname = NULL; + int ret = 0; + // for all RRs in the RRset rdata = knot_rrset_rdata(rrset); while (rdata != NULL) { - dbg_ns("Getting name from RDATA, type %s..\n", - knot_rrtype_to_string(knot_rrset_type(rrset))); + dbg_ns_verb("Getting name from RDATA, type %s..\n", + knot_rrtype_to_string(knot_rrset_type(rrset))); dname = knot_rdata_get_name(rdata, knot_rrset_type(rrset)); + +dbg_ns_exec_detail( + char *name = knot_dname_to_str(dname); + dbg_ns_detail("Name: %s\n", name); + free(name); +); assert(dname != NULL); node = knot_dname_node(dname); -// dbg_ns_detail("Node saved in RDATA dname: %p\n", node); -// char *name = knot_dname_to_str(dname); -// dbg_ns_detail("Owner of the node: %p, dname: %p (%s)\n", -// node->owner, dname, name); -// free(name); -// knot_node_dump((knot_node_t *)node, (void *)1); - + dbg_ns_detail("Node saved in RDATA dname: %p\n", node); if (node != NULL && node->owner != dname) { - // the stored node should be the closest encloser - assert(knot_dname_is_subdomain(dname, node->owner)); - // try the wildcard child, if any - node = knot_node_wildcard_child(node); + // the stored node should be the wildcard covering the + // name + dbg_ns_detail("Node is wildcard.\n"); + assert(knot_dname_is_wildcard(knot_node_owner(node))); } knot_rrset_t *rrset_add; @@ -506,43 +615,90 @@ static void ns_put_additional_for_rrset(knot_packet_t *resp, if (node != NULL) { dbg_ns_exec( char *name = knot_dname_to_str(node->owner); - dbg_ns("Putting additional from node %s\n", name); + dbg_ns_verb("Putting additional from node %s\n", name); free(name); ); - dbg_ns("Checking CNAMEs...\n"); - if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) - != NULL) { - dbg_ns("Found CNAME in node, following...\n"); + dbg_ns_detail("Checking CNAMEs...\n"); + if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL) { + dbg_ns_detail("Found CNAME in node.\n"); const knot_dname_t *dname = knot_node_owner(node); - ns_follow_cname(&node, &dname, resp, + ret = ns_follow_cname(&node, &dname, resp, knot_response_add_rrset_additional, 0); + if (ret != KNOT_EOK) { + dbg_ns("Failed to follow CNAME.\n"); + return ret; + } } // A RRSet - dbg_ns("A RRSets...\n"); + dbg_ns_detail("A RRSets...\n"); rrset_add = knot_node_get_rrset(node, KNOT_RRTYPE_A); if (rrset_add != NULL) { - dbg_ns("Found A RRsets.\n"); + dbg_ns_detail("Found A RRsets.\n"); knot_rrset_t *rrset_add2 = rrset_add; - ns_check_wildcard(dname, resp, &rrset_add2); - knot_response_add_rrset_additional( + ret = ns_check_wildcard(dname, resp, + &rrset_add2); + if (ret != KNOT_EOK) { + dbg_ns("Failed to process wildcard for" + "Additional section: %s.\n", + knot_strerror(ret)); + return ret; + } + + ret = knot_response_add_rrset_additional( resp, rrset_add2, 0, 1, 0, 1); - ns_add_rrsigs(rrset_add, resp, dname, + + if (ret != KNOT_EOK) { + dbg_ns("Failed to add A RRSet to " + "Additional section: %s.\n", + knot_strerror(ret)); + return ret; + } + + ret = ns_add_rrsigs(rrset_add, resp, dname, knot_response_add_rrset_additional, 0); + + if (ret != KNOT_EOK) { + dbg_ns("Failed to add RRSIGs for A RR" + "Set to Additional section: %s." + "\n", knot_strerror(ret)); + return ret; + } } // AAAA RRSet - dbg_ns("AAAA RRSets...\n"); + dbg_ns_detail("AAAA RRSets...\n"); rrset_add = knot_node_get_rrset(node, KNOT_RRTYPE_AAAA); if (rrset_add != NULL) { - dbg_ns("Found AAAA RRsets.\n"); + dbg_ns_detail("Found AAAA RRsets.\n"); knot_rrset_t *rrset_add2 = rrset_add; - ns_check_wildcard(dname, resp, &rrset_add2); - knot_response_add_rrset_additional( + ret = ns_check_wildcard(dname, resp, + &rrset_add2); + if (ret != KNOT_EOK) { + dbg_ns("Failed to process wildcard for" + "Additional section: %s.\n", + knot_strerror(ret)); + return ret; + } + + ret = knot_response_add_rrset_additional( resp, rrset_add2, 0, 1, 0, 1); - ns_add_rrsigs(rrset_add, resp, dname, + + if (ret != KNOT_EOK) { + dbg_ns("Failed to add AAAA RRSet to " + "Additional section.\n"); + return ret; + } + + ret = ns_add_rrsigs(rrset_add, resp, dname, knot_response_add_rrset_additional, 0); + + if (ret != KNOT_EOK) { + dbg_ns("Failed to add RRSIG for AAAA RR" + "Set to Additional section.\n"); + return ret; + } } } @@ -550,6 +706,8 @@ dbg_ns_exec( assert(rdata != NULL); rdata = knot_rrset_rdata_next(rrset, rdata); } + + return KNOT_EOK; } /*----------------------------------------------------------------------------*/ @@ -580,26 +738,37 @@ static int ns_additional_needed(uint16_t qtype) * * \param resp Response to process. */ -static void ns_put_additional(knot_packet_t *resp) +static int ns_put_additional(knot_packet_t *resp) { - dbg_ns("ADDITIONAL SECTION PROCESSING\n"); + dbg_ns_verb("ADDITIONAL SECTION PROCESSING\n"); const knot_rrset_t *rrset = NULL; + int ret = 0; for (int i = 0; i < knot_packet_answer_rrset_count(resp); ++i) { rrset = knot_packet_answer_rrset(resp, i); assert(rrset != NULL); if (ns_additional_needed(knot_rrset_type(rrset))) { - ns_put_additional_for_rrset(resp, rrset); + ret = ns_put_additional_for_rrset(resp, rrset); + if (ret != KNOT_EOK) { + // if error, do not try to add other RRSets + return ret; + } } } for (int i = 0; i < knot_packet_authority_rrset_count(resp); ++i) { rrset = knot_packet_authority_rrset(resp, i); if (ns_additional_needed(knot_rrset_type(rrset))) { - ns_put_additional_for_rrset(resp, rrset); + ret = ns_put_additional_for_rrset(resp, rrset); + if (ret != KNOT_EOK) { + // if error, do not try to add other RRSets + return ret; + } } } + + return KNOT_EOK; } /*----------------------------------------------------------------------------*/ @@ -609,18 +778,35 @@ static void ns_put_additional(knot_packet_t *resp) * \param zone Zone to take the authority NS RRSet from. * \param resp Response where to add the RRSet. */ -static void ns_put_authority_ns(const knot_zone_contents_t *zone, +static int ns_put_authority_ns(const knot_zone_contents_t *zone, knot_packet_t *resp) { + dbg_ns_verb("PUTTING AUTHORITY NS\n"); + knot_rrset_t *ns_rrset = knot_node_get_rrset( knot_zone_contents_apex(zone), KNOT_RRTYPE_NS); if (ns_rrset != NULL) { - knot_response_add_rrset_authority(resp, ns_rrset, 0, 1, 0, 1); - ns_add_rrsigs(ns_rrset, resp, knot_node_owner( + int ret = knot_response_add_rrset_authority(resp, ns_rrset, 0, + 1, 0, 1); + + if (ret != KNOT_EOK) { + dbg_ns("Failed to add Authority NSs to response.\n"); + return ret; + } + + ret = ns_add_rrsigs(ns_rrset, resp, knot_node_owner( knot_zone_contents_apex(zone)), - knot_response_add_rrset_authority, 1); + knot_response_add_rrset_authority, 1); + + if (ret != KNOT_EOK) { + dbg_ns("Failed to add RRSIGs for Authority NSs to " + "response.\n"); + return ret; + } } + + return KNOT_EOK; } /*----------------------------------------------------------------------------*/ @@ -633,6 +819,8 @@ static void ns_put_authority_ns(const knot_zone_contents_t *zone, static int ns_put_authority_soa(const knot_zone_contents_t *zone, knot_packet_t *resp) { + dbg_ns_verb("PUTTING AUTHORITY SOA\n"); + int ret; knot_rrset_t *soa_rrset = knot_node_get_rrset( @@ -644,7 +832,7 @@ static int ns_put_authority_soa(const knot_zone_contents_t *zone, uint32_t min = knot_rdata_soa_minimum(knot_rrset_rdata(soa_rrset)); if (min < knot_rrset_ttl(soa_rrset)) { knot_rrset_t *soa_copy = NULL; - ret = knot_rrset_deep_copy(soa_rrset, &soa_copy); + ret = knot_rrset_deep_copy(soa_rrset, &soa_copy, 0); if (ret != KNOT_EOK) { return ret; @@ -655,7 +843,11 @@ static int ns_put_authority_soa(const knot_zone_contents_t *zone, knot_rrset_set_ttl(soa_copy, min); soa_rrset = soa_copy; /* Need to add it as temporary, so it get's freed. */ - knot_packet_add_tmp_rrset(resp, soa_copy); + ret = knot_packet_add_tmp_rrset(resp, soa_copy); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&soa_copy, 1, 1, 1); + return ret; + } } assert(soa_rrset != NULL); @@ -717,20 +909,36 @@ static knot_dname_t *ns_next_closer(const knot_dname_t *closest_encloser, * \param node Node to get the NSEC3 RRSet from. * \param resp Response where to add the RRSets. */ -static void ns_put_nsec3_from_node(const knot_node_t *node, +static int ns_put_nsec3_from_node(const knot_node_t *node, knot_packet_t *resp) { assert(DNSSEC_ENABLED && knot_query_dnssec_requested(knot_packet_query(resp))); - knot_rrset_t *rrset = knot_node_get_rrset(node, KNOT_RRTYPE_NSEC3); - assert(rrset != NULL); + knot_rrset_t *rrset = knot_node_get_rrset(node, KNOT_RRTYPE_NSEC3); + //assert(rrset != NULL); - int res = knot_response_add_rrset_authority(resp, rrset, 1, 1, 0, 1); + if (rrset == NULL) { + // bad zone, ignore + return KNOT_EOK; + } + + int res = KNOT_EOK; + if (knot_rrset_rdata(rrset) != NULL) { + res = knot_response_add_rrset_authority(resp, rrset, 1, 1, 0, + 1); + } // add RRSIG for the RRSet - if (res == 0 && (rrset = knot_rrset_get_rrsigs(rrset)) != NULL) { - knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); + if (res == KNOT_EOK && (rrset = knot_rrset_get_rrsigs(rrset)) != NULL + && knot_rrset_rdata(rrset) != NULL) { + res = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, + 1); } + + /*! \note TC bit is already set, if something went wrong. */ + + // return the error code, so that other code may be skipped + return res; } /*----------------------------------------------------------------------------*/ @@ -753,31 +961,25 @@ static int ns_put_covering_nsec3(const knot_zone_contents_t *zone, const knot_node_t *prev, *node; /*! \todo Check version. */ int match = knot_zone_contents_find_nsec3_for_name(zone, name, - &node, &prev); - assert(match >= 0); -// node = knot_node_current(node); -// prev = knot_node_current(prev); + &node, &prev); + //assert(match >= 0); + if (match < 0) { + // ignoring, what can we do anyway? + return KNOT_EOK; + } - if (match == KNOT_ZONE_NAME_FOUND){ - // run-time collision => SERVFAIL + if (match == KNOT_ZONE_NAME_FOUND || prev == NULL){ + // if run-time collision => SERVFAIL return KNOT_EOK; } -// // check if the prev node is not old and if yes, take the new one -// if (knot_node_is_old(prev)) { -// prev = knot_node_new_node(prev); -// assert(prev != NULL); -// } - -dbg_ns_exec( +dbg_ns_exec_verb( char *name = knot_dname_to_str(prev->owner); - dbg_ns("Covering NSEC3 node: %s\n", name); + dbg_ns_verb("Covering NSEC3 node: %s\n", name); free(name); ); - ns_put_nsec3_from_node(prev, resp); - - return KNOT_EOK; + return ns_put_nsec3_from_node(prev, resp); } /*----------------------------------------------------------------------------*/ @@ -812,19 +1014,24 @@ static int ns_put_nsec3_closest_encloser_proof( assert(qname != NULL); assert(resp != NULL); + // this function should be called only if NSEC3 is enabled in the zone + assert(knot_zone_contents_nsec3params(zone) != NULL); + + dbg_ns_verb("Adding closest encloser proof\n"); + if (knot_zone_contents_nsec3params(zone) == NULL) { -dbg_ns_exec( +dbg_ns_exec_verb( char *name = knot_dname_to_str(knot_node_owner( knot_zone_contents_apex(zone))); - dbg_ns("No NSEC3PARAM found in zone %s.\n", name); + dbg_ns_verb("No NSEC3PARAM found in zone %s.\n", name); free(name); ); return KNOT_EOK; } -dbg_ns_exec( +dbg_ns_exec_detail( char *name = knot_dname_to_str(knot_node_owner(*closest_encloser)); - dbg_ns("Closest encloser: %s\n", name); + dbg_ns_detail("Closest encloser: %s\n", name); free(name); ); @@ -845,28 +1052,30 @@ dbg_ns_exec( assert(nsec3_node != NULL); -dbg_ns_exec( +dbg_ns_exec_verb( char *name = knot_dname_to_str(nsec3_node->owner); - dbg_ns("NSEC3 node: %s\n", name); + dbg_ns_verb("NSEC3 node: %s\n", name); free(name); name = knot_dname_to_str((*closest_encloser)->owner); - dbg_ns("Closest provable encloser: %s\n", name); + dbg_ns_verb("Closest provable encloser: %s\n", name); free(name); if (next_closer != NULL) { name = knot_dname_to_str(next_closer); - dbg_ns("Next closer name: %s\n", name); + dbg_ns_verb("Next closer name: %s\n", name); free(name); } else { - dbg_ns("Next closer name: none\n"); + dbg_ns_verb("Next closer name: none\n"); } ); - ns_put_nsec3_from_node(nsec3_node, resp); + int ret = ns_put_nsec3_from_node(nsec3_node, resp); + if (ret != KNOT_EOK) { + return ret; + } /* * 2) NSEC3 that covers the "next closer" name. */ - int ret = 0; if (next_closer == NULL) { // create the "next closer" name by appending from qname next_closer = ns_next_closer( @@ -875,9 +1084,9 @@ dbg_ns_exec( if (next_closer == NULL) { return NS_ERR_SERVFAIL; } -dbg_ns_exec( +dbg_ns_exec_verb( char *name = knot_dname_to_str(next_closer); - dbg_ns("Next closer name: %s\n", name); + dbg_ns_verb("Next closer name: %s\n", name); free(name); ); ret = ns_put_covering_nsec3(zone, next_closer, resp); @@ -914,9 +1123,9 @@ static knot_dname_t *ns_wildcard_child_name(const knot_dname_t *name) return NULL; } -dbg_ns_exec( +dbg_ns_exec_verb( char *name = knot_dname_to_str(wildcard); - dbg_ns("Wildcard: %s\n", name); + dbg_ns_verb("Wildcard: %s\n", name); free(name); ); return wildcard; @@ -971,26 +1180,55 @@ static int ns_put_nsec3_no_wildcard_child(const knot_zone_contents_t *zone, * RRSets of the requested type). * \param resp Response where to add the NSECs or NSEC3s. */ -static void ns_put_nsec_nsec3_nodata(const knot_node_t *node, - knot_packet_t *resp) +static int ns_put_nsec_nsec3_nodata(const knot_zone_contents_t *zone, + const knot_node_t *node, + knot_packet_t *resp) { if (!DNSSEC_ENABLED || !knot_query_dnssec_requested(knot_packet_query(resp))) { - return; + return KNOT_EOK; } - knot_node_t *nsec3_node = knot_node_get_nsec3_node(node); + /*! \todo Maybe distinguish different errors. */ + int ret = KNOT_ERROR; + knot_rrset_t *rrset = NULL; - if ((rrset = knot_node_get_rrset(node, KNOT_RRTYPE_NSEC)) != NULL - || (nsec3_node != NULL && (rrset = - knot_node_get_rrset(nsec3_node, KNOT_RRTYPE_NSEC3)) != NULL)) { - knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); - // add RRSIG for the RRSet - if ((rrset = knot_rrset_get_rrsigs(rrset)) != NULL) { - knot_response_add_rrset_authority(resp, rrset, 1, - 0, 0, 1); + + if (knot_zone_contents_nsec3_enabled(zone)) { + knot_node_t *nsec3_node = knot_node_get_nsec3_node(node); + dbg_ns_verb("Adding NSEC3 for NODATA, NSEC3 node: %p\n", + nsec3_node); + + if (nsec3_node != NULL + && (rrset = knot_node_get_rrset(nsec3_node, + KNOT_RRTYPE_NSEC3)) != NULL + && knot_rrset_rdata(rrset) != NULL) { + dbg_ns_detail("Putting the RRSet to Authority\n"); + ret = knot_response_add_rrset_authority(resp, rrset, 1, + 0, 0, 1); } + } else { + dbg_ns_verb("Adding NSEC for NODATA\n"); + if ((rrset = knot_node_get_rrset(node, KNOT_RRTYPE_NSEC)) + != NULL + && knot_rrset_rdata(rrset) != NULL) { + dbg_ns_detail("Putting the RRSet to Authority\n"); + ret = knot_response_add_rrset_authority(resp, rrset, 1, + 0, 0, 1); + } + } + + if (ret != KNOT_EOK) { + return ret; } + + dbg_ns_detail("Putting RRSet's RRSIGs to Authority\n"); + if (rrset != NULL && (rrset = knot_rrset_get_rrsigs(rrset)) != NULL) { + ret = knot_response_add_rrset_authority(resp, rrset, 1, + 0, 0, 1); + } + + return ret; } /*----------------------------------------------------------------------------*/ @@ -1030,9 +1268,11 @@ static int ns_put_nsec_nxdomain(const knot_dname_t *qname, } } +dbg_ns_exec_verb( char *name = knot_dname_to_str(previous->owner); - dbg_ns("Previous node: %s\n", name); + dbg_ns_verb("Previous node: %s\n", name); free(name); +); // 1) NSEC proving that there is no node with the searched name rrset = knot_node_get_rrset(previous, KNOT_RRTYPE_NSEC); @@ -1043,11 +1283,21 @@ static int ns_put_nsec_nxdomain(const knot_dname_t *qname, } - knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); + int ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); + if (ret != KNOT_EOK) { + dbg_ns("Failed to add NSEC for NXDOMAIN to response: %s\n", + knot_strerror(ret)); + return ret; + } + rrset = knot_rrset_get_rrsigs(rrset); assert(rrset != NULL); - knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); - + ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); + if (ret != KNOT_EOK) { + dbg_ns("Failed to add RRSIGs for NSEC for NXDOMAIN to response:" + "%s\n", knot_strerror(ret)); + return ret; + } // 2) NSEC proving that there is no wildcard covering the name // this is only different from 1) if the wildcard would be // before 'previous' in canonical order, i.e. we can @@ -1064,9 +1314,9 @@ static int ns_put_nsec_nxdomain(const knot_dname_t *qname, while (knot_dname_compare(knot_node_owner(prev_new), wildcard) > 0) { -dbg_ns_exec( +dbg_ns_exec_verb( char *name = knot_dname_to_str(knot_node_owner(prev_new)); - dbg_ns("Previous node: %s\n", name); + dbg_ns_verb("Previous node: %s\n", name); free(name); ); assert(prev_new != knot_zone_contents_apex(zone)); @@ -1075,9 +1325,9 @@ dbg_ns_exec( assert(knot_dname_compare(knot_node_owner(prev_new), wildcard) < 0); -dbg_ns_exec( +dbg_ns_exec_verb( char *name = knot_dname_to_str(knot_node_owner(prev_new)); - dbg_ns("Previous node: %s\n", name); + dbg_ns_verb("Previous node: %s\n", name); free(name); ); @@ -1086,11 +1336,27 @@ dbg_ns_exec( if (prev_new != previous) { rrset = knot_node_get_rrset(prev_new, KNOT_RRTYPE_NSEC); - assert(rrset != NULL); - knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); + if (rrset == NULL || knot_rrset_rdata(rrset) == NULL) { + // bad zone, ignore + return KNOT_EOK; + } + ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); + if (ret != KNOT_EOK) { + dbg_ns("Failed to add second NSEC for NXDOMAIN to " + "response: %s\n", knot_strerror(ret)); + return ret; + } rrset = knot_rrset_get_rrsigs(rrset); - assert(rrset != NULL); - knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); + if (rrset == NULL || knot_rrset_rdata(rrset) == NULL) { + // bad zone, ignore + return KNOT_EOK; + } + ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); + if (ret != KNOT_EOK) { + dbg_ns("Failed to add RRSIGs for second NSEC for " + "NXDOMAIN to response: %s\n", knot_strerror(ret)); + return ret; + } } return KNOT_EOK; @@ -1118,13 +1384,12 @@ static int ns_put_nsec3_nxdomain(const knot_zone_contents_t *zone, knot_packet_t *resp) { // 1) Closest encloser proof - dbg_ns("Putting closest encloser proof.\n"); int ret = ns_put_nsec3_closest_encloser_proof(zone, &closest_encloser, qname, resp); // 2) NSEC3 covering non-existent wildcard if (ret == KNOT_EOK && closest_encloser != NULL) { - dbg_ns("Putting NSEC3 for no wildcard child of closest " - "encloser.\n"); + dbg_ns_verb("Putting NSEC3 for no wildcard child of closest " + "encloser.\n"); ret = ns_put_nsec3_no_wildcard_child(zone, closest_encloser, resp); } @@ -1207,16 +1472,16 @@ static int ns_put_nsec3_wildcard(const knot_zone_contents_t *zone, * NSEC3 that covers the "next closer" name. */ // create the "next closer" name by appending from qname - dbg_ns("Finding next closer name for wildcard NSEC3.\n"); + dbg_ns_verb("Finding next closer name for wildcard NSEC3.\n"); knot_dname_t *next_closer = ns_next_closer(closest_encloser->owner, qname); if (next_closer == NULL) { return NS_ERR_SERVFAIL; } -dbg_ns_exec( +dbg_ns_exec_verb( char *name = knot_dname_to_str(next_closer); - dbg_ns("Next closer name: %s\n", name); + dbg_ns_verb("Next closer name: %s\n", name); free(name); ); int ret = ns_put_covering_nsec3(zone, next_closer, resp); @@ -1241,10 +1506,10 @@ dbg_ns_exec( * \param previous Previous node of \a qname in canonical order. * \param resp Response to put the NSEC3s into. */ -static void ns_put_nsec_wildcard(const knot_zone_contents_t *zone, - const knot_dname_t *qname, - const knot_node_t *previous, - knot_packet_t *resp) +static int ns_put_nsec_wildcard(const knot_zone_contents_t *zone, + const knot_dname_t *qname, + const knot_node_t *previous, + knot_packet_t *resp) { assert(DNSSEC_ENABLED && knot_query_dnssec_requested(knot_packet_query(resp))); @@ -1261,13 +1526,22 @@ static void ns_put_nsec_wildcard(const knot_zone_contents_t *zone, knot_rrset_t *rrset = knot_node_get_rrset(previous, KNOT_RRTYPE_NSEC); - if (rrset != NULL) { + + int ret = KNOT_EOK; + + if (rrset != NULL && knot_rrset_rdata(rrset) != NULL) { // NSEC proving that there is no node with the searched name - knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); - rrset = knot_rrset_get_rrsigs(rrset); - assert(rrset != NULL); - knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); + ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, + 0, 1); + if (ret == KNOT_EOK) { + rrset = knot_rrset_get_rrsigs(rrset); + assert(rrset != NULL); + ret = knot_response_add_rrset_authority(resp, rrset, 1, + 0, 0, 1); + } } + + return ret; } /*----------------------------------------------------------------------------*/ @@ -1306,10 +1580,10 @@ static int ns_put_nsec_nsec3_wildcard_nodata(const knot_node_t *node, if (ret == KNOT_EOK && (nsec3_node = knot_node_nsec3_node(node)) != NULL) { - ns_put_nsec3_from_node(nsec3_node, resp); + ret = ns_put_nsec3_from_node(nsec3_node, resp); } } else { - ns_put_nsec_wildcard(zone, qname, previous, resp); + ret = ns_put_nsec_wildcard(zone, qname, previous, resp); } } return ret; @@ -1339,18 +1613,18 @@ static int ns_put_nsec_nsec3_wildcard_answer(const knot_node_t *node, const knot_dname_t *qname, knot_packet_t *resp) { - int r = KNOT_EOK; + int ret = KNOT_EOK; if (DNSSEC_ENABLED && knot_query_dnssec_requested(knot_packet_query(resp)) && knot_dname_is_wildcard(knot_node_owner(node))) { if (knot_zone_contents_nsec3_enabled(zone)) { - r = ns_put_nsec3_wildcard(zone, closest_encloser, qname, - resp); + ret = ns_put_nsec3_wildcard(zone, closest_encloser, + qname, resp); } else { - ns_put_nsec_wildcard(zone, qname, previous, resp); + ret = ns_put_nsec_wildcard(zone, qname, previous, resp); } } - return r; + return ret; } /*----------------------------------------------------------------------------*/ @@ -1403,26 +1677,27 @@ static inline int ns_referral(const knot_node_t *node, knot_packet_t *resp, uint16_t qtype) { - dbg_ns("Referral response.\n"); + dbg_ns_verb("Referral response.\n"); while (!knot_node_is_deleg_point(node)) { assert(knot_node_parent(node) != NULL); node = knot_node_parent(node); } + int ret = KNOT_EOK; + // Special handling of DS queries if (qtype == KNOT_RRTYPE_DS) { knot_rrset_t *ds_rrset = knot_node_get_rrset(node, KNOT_RRTYPE_DS); - int ret = KNOT_EOK; if (ds_rrset) { - knot_response_add_rrset_answer(resp, ds_rrset, 1, 0, - 0, 1); - if (DNSSEC_ENABLED + ret = knot_response_add_rrset_answer(resp, ds_rrset, 1, + 0, 0, 1); + if (ret == KNOT_EOK && DNSSEC_ENABLED && knot_query_dnssec_requested( knot_packet_query(resp))) { - ns_add_rrsigs(ds_rrset, resp, node->owner, + ret = ns_add_rrsigs(ds_rrset, resp, node->owner, knot_response_add_rrset_authority, 1); } @@ -1430,18 +1705,13 @@ static inline int ns_referral(const knot_node_t *node, // normal NODATA response /*! \todo Handle in some generic way. */ - dbg_ns("Adding NSEC/NSEC3 for NODATA.\n"); - ns_put_nsec_nsec3_nodata(node, resp); + dbg_ns_verb("Adding NSEC/NSEC3 for NODATA.\n"); + ret = ns_put_nsec_nsec3_nodata(zone, node, resp); + if (ret != KNOT_EOK) { + return ret; + } - // wildcard delegations not supported! -// if (knot_dname_is_wildcard(node->owner)) { -// dbg_ns("Putting NSEC/NSEC3 for wildcard" -// " NODATA\n"); -// ret = ns_put_nsec_nsec3_wildcard_nodata(node, -// closest_encloser, previous, zone, qname, -// resp); -// } - ns_put_authority_soa(zone, resp); + ret = ns_put_authority_soa(zone, resp); } return ret; @@ -1449,39 +1719,42 @@ static inline int ns_referral(const knot_node_t *node, knot_rrset_t *rrset = knot_node_get_rrset(node, KNOT_RRTYPE_NS); assert(rrset != NULL); - - // TODO: wildcards?? - //ns_check_wildcard(name, resp, &rrset); - knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); - ns_add_rrsigs(rrset, resp, node->owner, - knot_response_add_rrset_authority, 1); + ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1); + if (ret == KNOT_EOK) { + ret = ns_add_rrsigs(rrset, resp, node->owner, + knot_response_add_rrset_authority, 1); + } - int ret = KNOT_EOK; // add DS records - dbg_ns("DNSSEC requested: %d\n", + dbg_ns_verb("DNSSEC requested: %d\n", knot_query_dnssec_requested(knot_packet_query(resp))); - dbg_ns("DS records: %p\n", knot_node_rrset(node, KNOT_RRTYPE_DS)); - if (DNSSEC_ENABLED + dbg_ns_verb("DS records: %p\n", knot_node_rrset(node, KNOT_RRTYPE_DS)); + if (ret == KNOT_EOK && DNSSEC_ENABLED && knot_query_dnssec_requested(knot_packet_query(resp))) { rrset = knot_node_get_rrset(node, KNOT_RRTYPE_DS); if (rrset != NULL) { - knot_response_add_rrset_authority(resp, rrset, 1, 0, - 0, 1); - ns_add_rrsigs(rrset, resp, node->owner, - knot_response_add_rrset_authority, 1); + ret = knot_response_add_rrset_authority(resp, rrset, 1, + 0, 0, 1); + if (ret == KNOT_EOK) { + ret = ns_add_rrsigs(rrset, resp, node->owner, + knot_response_add_rrset_authority, 1); + } } else { // no DS, add NSEC3 or NSEC // if NSEC3 enabled, search for NSEC3 if (knot_zone_contents_nsec3_enabled(zone)) { const knot_node_t *nsec3_node = knot_node_nsec3_node(node); - dbg_ns("There is no DS, putting NSEC3s...\n"); + dbg_ns_detail("There is no DS, putting NSEC3s." + "\n"); if (nsec3_node != NULL) { - dbg_ns("Putting NSEC3s from the node.\n"); - ns_put_nsec3_from_node(nsec3_node, resp); + dbg_ns_detail("Putting NSEC3s from the node.\n"); + ret = ns_put_nsec3_from_node(nsec3_node, + resp); } else { - dbg_ns("Putting Opt-Out NSEC3s.\n"); + dbg_ns_detail("Putting Opt-Out NSEC3s." + "\n"); // no NSEC3 (probably Opt-Out) // TODO: check if the zone is Opt-Out ret = ns_put_nsec3_closest_encloser_proof(zone, @@ -1492,10 +1765,11 @@ static inline int ns_referral(const knot_node_t *node, node, KNOT_RRTYPE_NSEC); if (nsec) { /*! \todo Check return value? */ - knot_response_add_rrset_authority( + ret = knot_response_add_rrset_authority( resp, nsec, 1, 1, 0, 1); - if ((nsec = knot_rrset_get_rrsigs(nsec)) != NULL) { - knot_response_add_rrset_authority( + if (ret == KNOT_EOK && + (nsec = knot_rrset_get_rrsigs(nsec)) != NULL) { + ret = knot_response_add_rrset_authority( resp, nsec, 1, 1, 0, 1); } } @@ -1503,10 +1777,13 @@ static inline int ns_referral(const knot_node_t *node, } } - if (ret == KNOT_EOK) { -// ns_put_additional(resp); + if (ret == KNOT_ESPACE) { + knot_response_set_rcode(resp, KNOT_RCODE_NOERROR); + ret = KNOT_EOK; + } else if (ret == KNOT_EOK) { knot_response_set_rcode(resp, KNOT_RCODE_NOERROR); } + return ret; } @@ -1540,65 +1817,65 @@ static int ns_answer_from_node(const knot_node_t *node, const knot_node_t *previous, const knot_zone_contents_t *zone, const knot_dname_t *qname, uint16_t qtype, - knot_packet_t *resp) + knot_packet_t *resp, int check_any) { - dbg_ns("Putting answers from found node to the response...\n"); - int answers = ns_put_answer(node, qname, qtype, resp); + dbg_ns_verb("Putting answers from found node to the response...\n"); + int answers = 0; + + int ret = ns_put_answer(node, zone, qname, qtype, resp, &answers, + check_any); + if (ret != KNOT_EOK) { + return ret; + } + + assert(ret == KNOT_EOK); - int ret = KNOT_EOK; if (answers == 0) { // if NODATA response, put SOA + ret = ns_put_authority_soa(zone, resp); if (knot_node_rrset_count(node) == 0 && !knot_zone_contents_nsec3_enabled(zone)) { // node is an empty non-terminal => NSEC for NXDOMAIN //assert(knot_node_rrset_count(closest_encloser) > 0); - dbg_ns("Adding NSEC/NSEC3 for NXDOMAIN.\n"); + dbg_ns_verb("Adding NSEC/NSEC3 for NXDOMAIN.\n"); ret = ns_put_nsec_nsec3_nxdomain(zone, knot_node_previous(node), closest_encloser, qname, resp); } else { - dbg_ns("Adding NSEC/NSEC3 for NODATA.\n"); - ns_put_nsec_nsec3_nodata(node, resp); + dbg_ns_verb("Adding NSEC/NSEC3 for NODATA.\n"); + ret = ns_put_nsec_nsec3_nodata(zone, node, resp); + if (ret != KNOT_EOK) { + dbg_ns("Failed adding NSEC/NSEC3 for NODATA: %s" + "\n", knot_strerror(ret)); + return ret; + } + if (knot_dname_is_wildcard(node->owner)) { - dbg_ns("Putting NSEC/NSEC3 for wildcard" - " NODATA\n"); + dbg_ns_verb("Putting NSEC/NSEC3 for wildcard" + " NODATA\n"); ret = ns_put_nsec_nsec3_wildcard_nodata(node, closest_encloser, previous, zone, qname, resp); + if (ret != KNOT_EOK) { + return ret; + } } } - ns_put_authority_soa(zone, resp); } else { // else put authority NS // if wildcard answer, add NSEC / NSEC3 - dbg_ns("Adding NSEC/NSEC3 for wildcard answer.\n"); - -// char *n = knot_dname_to_str(knot_node_owner(node)); -// char *ce = (closest_encloser) -// ? knot_dname_to_str(knot_node_owner(closest_encloser)) -// : "(nil)"; -// char *prev = (previous) -// ? knot_dname_to_str(knot_node_owner(previous)) -// : "(nil)"; -// printf("Node: %s, closest encloser: %s, previous: %s\n", -// n, ce, prev); -// free(n); -// if (closest_encloser) { -// free(ce); -// } -// if (previous) { -// free(prev); -// } + dbg_ns_verb("Adding NSEC/NSEC3 for wildcard answer.\n"); + assert(previous == NULL); assert(closest_encloser == knot_node_parent(node) || !knot_dname_is_wildcard(knot_node_owner(node))); ret = ns_put_nsec_nsec3_wildcard_answer(node, closest_encloser, previous, zone, qname, resp); - ns_put_authority_ns(zone, resp); + + if (ret == KNOT_EOK) { + ret = ns_put_authority_ns(zone, resp); + } } -// if (ret == KNOT_EOK) { -// ns_put_additional(resp); -// } return ret; } @@ -1617,7 +1894,7 @@ static int ns_answer_from_node(const knot_node_t *node, static knot_rrset_t *ns_cname_from_dname(const knot_rrset_t *dname_rrset, const knot_dname_t *qname) { - dbg_ns("Synthetizing CNAME from DNAME...\n"); + dbg_ns_verb("Synthetizing CNAME from DNAME...\n"); // create new CNAME RRSet @@ -1642,15 +1919,25 @@ static knot_rrset_t *ns_cname_from_dname(const knot_rrset_t *dname_rrset, knot_rdata_get_item(knot_rrset_rdata(dname_rrset), 0)->dname); dbg_ns_exec( char *name = knot_dname_to_str(cname); - dbg_ns("CNAME canonical name: %s.\n", name); + dbg_ns_verb("CNAME canonical name: %s.\n", name); free(name); ); knot_rdata_t *cname_rdata = knot_rdata_new(); knot_rdata_item_t cname_rdata_item; cname_rdata_item.dname = cname; - knot_rdata_set_items(cname_rdata, &cname_rdata_item, 1); + int ret = knot_rdata_set_items(cname_rdata, &cname_rdata_item, 1); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&cname_rrset, 1, 1, 1); + knot_rdata_deep_free(&cname_rdata, KNOT_RRTYPE_CNAME, 1); + return NULL; + } - knot_rrset_add_rdata(cname_rrset, cname_rdata); + ret = knot_rrset_add_rdata(cname_rrset, cname_rdata); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&cname_rrset, 1, 1, 1); + knot_rdata_deep_free(&cname_rdata, KNOT_RRTYPE_CNAME, 1); + return NULL; + } return cname_rrset; } @@ -1694,38 +1981,59 @@ static int ns_dname_is_too_long(const knot_rrset_t *dname_rrset, * \param qname Searched name. * \param resp Response. */ -static void ns_process_dname(knot_rrset_t *dname_rrset, - const knot_dname_t *qname, +static int ns_process_dname(knot_rrset_t *dname_rrset, + const knot_dname_t **qname, knot_packet_t *resp) { -dbg_ns_exec( +dbg_ns_exec_verb( char *name = knot_dname_to_str(knot_rrset_owner(dname_rrset)); - dbg_ns("Processing DNAME for owner %s...\n", name); + dbg_ns_verb("Processing DNAME for owner %s...\n", name); free(name); ); // TODO: check the number of RRs in the RRSet?? // put the DNAME RRSet into the answer - knot_response_add_rrset_answer(resp, dname_rrset, 1, 0, 0, 1); - ns_add_rrsigs(dname_rrset, resp, qname, - knot_response_add_rrset_answer, 1); + int ret = knot_response_add_rrset_answer(resp, dname_rrset, 1, 0, 0, 1); + if (ret != KNOT_EOK) { + return ret; + } - if (ns_dname_is_too_long(dname_rrset, qname)) { + ret = ns_add_rrsigs(dname_rrset, resp, *qname, + knot_response_add_rrset_answer, 1); + if (ret != KNOT_EOK) { + return ret; + } + + if (ns_dname_is_too_long(dname_rrset, *qname)) { knot_response_set_rcode(resp, KNOT_RCODE_YXDOMAIN); - return; + return KNOT_EOK; } // synthetize CNAME (no way to tell that client supports DNAME) - knot_rrset_t *synth_cname = ns_cname_from_dname(dname_rrset, qname); + knot_rrset_t *synth_cname = ns_cname_from_dname(dname_rrset, *qname); // add the synthetized RRSet to the Answer - knot_response_add_rrset_answer(resp, synth_cname, 1, 0, 0, 1); + ret = knot_response_add_rrset_answer(resp, synth_cname, 1, 0, 0, 1); + if (ret != KNOT_EOK) { + return ret; + } // no RRSIGs for this RRSet // add the synthetized RRSet into list of temporary RRSets of response - knot_packet_add_tmp_rrset(resp, synth_cname); + ret = knot_packet_add_tmp_rrset(resp, synth_cname); + if (ret != KNOT_EOK) { + return ret; + } + + // get the next SNAME from the CNAME RDATA + const knot_dname_t *cname = knot_rdata_cname_name( + knot_rrset_rdata(synth_cname)); + dbg_ns_verb("CNAME name from RDATA: %p\n", cname); - // do not search for the name in new zone (out-of-bailiwick) + // save the new name which should be used for replacing wildcard + *qname = cname; + + return KNOT_EOK; } /*----------------------------------------------------------------------------*/ @@ -1735,15 +2043,23 @@ dbg_ns_exec( * \param apex Zone apex node. * \param resp Response. */ -static void ns_add_dnskey(const knot_node_t *apex, knot_packet_t *resp) +static int ns_add_dnskey(const knot_node_t *apex, knot_packet_t *resp) { knot_rrset_t *rrset = knot_node_get_rrset(apex, KNOT_RRTYPE_DNSKEY); + + int ret = KNOT_EOK; + if (rrset != NULL) { - knot_response_add_rrset_additional(resp, rrset, 0, 0, 0, 1); - ns_add_rrsigs(rrset, resp, apex->owner, - knot_response_add_rrset_additional, 0); + ret = knot_response_add_rrset_additional(resp, rrset, 0, 0, + 0, 1); + if (ret == KNOT_EOK) { + ret = ns_add_rrsigs(rrset, resp, apex->owner, + knot_response_add_rrset_additional, 0); + } } + + return ret; } /*----------------------------------------------------------------------------*/ @@ -1763,7 +2079,7 @@ static void ns_add_dnskey(const knot_node_t *apex, knot_packet_t *resp) * \todo Describe the answering logic in detail. */ static int ns_answer_from_zone(const knot_zone_contents_t *zone, - knot_packet_t *resp) + knot_packet_t *resp, int check_any) { const knot_node_t *node = NULL, *closest_encloser = NULL, *previous = NULL; @@ -1774,13 +2090,9 @@ static int ns_answer_from_zone(const knot_zone_contents_t *zone, search: #ifdef USE_HASH_TABLE - /*! \todo Check version. */ find_ret = knot_zone_contents_find_dname_hash(zone, qname, &node, &closest_encloser); -// node = knot_node_current(node); -// closest_encloser = knot_node_current(closest_encloser); #else - /*! \todo Check version. */ find_ret = knot_zone_contents_find_dname(zone, qname, &node, &closest_encloser, &previous); node = knot_node_current(node); @@ -1791,33 +2103,33 @@ search: return NS_ERR_SERVFAIL; } -dbg_ns_exec( +dbg_ns_exec_verb( char *name; if (node) { name = knot_dname_to_str(node->owner); - dbg_ns("zone_find_dname() returned node %s \n", name); + dbg_ns_verb("zone_find_dname() returned node %s \n", name); free(name); } else { - dbg_ns("zone_find_dname() returned no node,\n"); + dbg_ns_verb("zone_find_dname() returned no node,\n"); } if (closest_encloser != NULL) { name = knot_dname_to_str(closest_encloser->owner); - dbg_ns(" closest encloser %s.\n", name); + dbg_ns_verb(" closest encloser %s.\n", name); free(name); } else { - dbg_ns(" closest encloser (nil).\n"); + dbg_ns_verb(" closest encloser (nil).\n"); } if (previous != NULL) { name = knot_dname_to_str(previous->owner); - dbg_ns(" and previous node: %s.\n", name); + dbg_ns_verb(" and previous node: %s.\n", name); free(name); } else { - dbg_ns(" and previous node: (nil).\n"); + dbg_ns_verb(" and previous node: (nil).\n"); } ); if (find_ret == KNOT_EBADZONE) { - // possible only if we followed cname + // possible only if we followed CNAME or DNAME assert(cname != 0); knot_response_set_rcode(resp, KNOT_RCODE_NOERROR); auth_soa = 1; @@ -1826,10 +2138,10 @@ dbg_ns_exec( } have_node: - dbg_ns("Closest encloser is deleg. point? %s\n", + dbg_ns_verb("Closest encloser is deleg. point? %s\n", (knot_node_is_deleg_point(closest_encloser)) ? "yes" : "no"); - dbg_ns("Closest encloser is non authoritative? %s\n", + dbg_ns_verb("Closest encloser is non authoritative? %s\n", (knot_node_is_non_auth(closest_encloser)) ? "yes" : "no"); if (knot_node_is_deleg_point(closest_encloser) @@ -1843,21 +2155,31 @@ have_node: knot_rrset_t *dname_rrset = knot_node_get_rrset( closest_encloser, KNOT_RRTYPE_DNAME); if (dname_rrset != NULL) { - ns_process_dname(dname_rrset, qname, resp); - auth_soa = 1; + ret = ns_process_dname(dname_rrset, &qname, resp); + knot_response_set_aa(resp); - goto finalize; + + if (ret != KNOT_EOK) { + goto finalize; + } + + // do not search for the name in new zone + // (out-of-bailiwick), just in the current zone if it + // belongs there + + cname = 1; + goto search; } // else check for a wildcard child const knot_node_t *wildcard_node = knot_node_wildcard_child(closest_encloser); if (wildcard_node == NULL) { - dbg_ns("No wildcard node. (cname: %d)\n", - cname); + dbg_ns_verb("No wildcard node. (cname: %d)\n", + cname); auth_soa = 1; if (cname == 0) { - dbg_ns("Setting NXDOMAIN RCODE.\n"); + dbg_ns_detail("Setting NXDOMAIN RCODE.\n"); // return NXDOMAIN knot_response_set_rcode(resp, KNOT_RCODE_NXDOMAIN); @@ -1884,24 +2206,31 @@ have_node: } if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL - && qtype != KNOT_RRTYPE_CNAME) { + && qtype != KNOT_RRTYPE_CNAME && qtype != KNOT_RRTYPE_RRSIG) { dbg_ns_exec( char *name = knot_dname_to_str(node->owner); - dbg_ns("Node %s has CNAME record, resolving...\n", - name); + dbg_ns_verb("Node %s has CNAME record, resolving...\n", name); free(name); ); const knot_dname_t *act_name = qname; - ns_follow_cname(&node, &act_name, resp, - knot_response_add_rrset_answer, 1); -dbg_ns_exec( + ret = ns_follow_cname(&node, &act_name, resp, + knot_response_add_rrset_answer, 1); + + /*! \todo IS OK??? */ + knot_response_set_aa(resp); + + if (ret != KNOT_EOK) { + // KNOT_ESPACE case is handled there + goto finalize; + } +dbg_ns_exec_verb( char *name = (node != NULL) ? knot_dname_to_str(node->owner) : "(nil)"; char *name2 = knot_dname_to_str(act_name); - dbg_ns("Canonical name: %s (%p), node found: %p\n", - name2, act_name, node); - dbg_ns("The node's owner: %s (%p)\n", name, (node != NULL) - ? node->owner : NULL); + dbg_ns_verb("Canonical name: %s (%p), node found: %p\n", + name2, act_name, node); + dbg_ns_verb("The node's owner: %s (%p)\n", name, (node != NULL) + ? node->owner : NULL); if (node != NULL) { free(name); } @@ -1913,25 +2242,33 @@ dbg_ns_exec( // otherwise search for the new name if (node == NULL) { goto search; - } else if (node->owner != act_name) { - // the stored node is closest encloser - find_ret = KNOT_ZONE_NAME_NOT_FOUND; - closest_encloser = node; - node = NULL; - goto have_node; - } // else do nothing, just continue + } else if (knot_node_owner(node) != act_name) { + if(knot_dname_is_wildcard(knot_node_owner(node))) { + // we must set the closest encloser to the + // parent of the node, to be right + closest_encloser = knot_node_parent(node); + assert(closest_encloser != NULL); + } else { + // the stored node is closest encloser + find_ret = KNOT_ZONE_NAME_NOT_FOUND; + closest_encloser = node; + node = NULL; + goto have_node; + } + } } ret = ns_answer_from_node(node, closest_encloser, previous, zone, qname, - qtype, resp); + qtype, resp, check_any); if (ret == NS_ERR_SERVFAIL) { // in this case we should drop the response and send an error // for now, just send the error code with a non-complete answer -// knot_response_set_rcode(resp, KNOT_RCODE_SERVFAIL); -// goto finalize; return ret; } else if (ret != KNOT_EOK) { /*! \todo Handle RCODE return values!!! */ + // In case ret == KNOT_ESPACE, this is later converted to EOK + // so it does not cause error response + knot_response_set_aa(resp); goto finalize; } knot_response_set_aa(resp); @@ -1940,16 +2277,21 @@ dbg_ns_exec( // this is the only case when the servers answers from // particular node, i.e. the only case when it may return SOA // or NS records in Answer section - if (DNSSEC_ENABLED + if (knot_packet_tc(resp) == 0 && DNSSEC_ENABLED && knot_query_dnssec_requested(knot_packet_query(resp)) && node == knot_zone_contents_apex(zone) && (qtype == KNOT_RRTYPE_SOA || qtype == KNOT_RRTYPE_NS)) { - ns_add_dnskey(node, resp); + ret = ns_add_dnskey(node, resp); } finalize: - if (ret == KNOT_EOK && auth_soa) { - ns_put_authority_soa(zone, resp); + if (ret == KNOT_EOK && knot_packet_tc(resp) == 0 && auth_soa) { + ret = ns_put_authority_soa(zone, resp); + } + + if (ret == KNOT_ESPACE) { + knot_response_set_rcode(resp, KNOT_RCODE_NOERROR); + ret = KNOT_EOK; } // add all missing NSECs/NSEC3s for wildcard nodes @@ -1976,20 +2318,9 @@ finalize: * \retval KNOT_EOK * \retval NS_ERR_SERVFAIL */ -static int ns_answer(const knot_zone_t *zone, knot_packet_t *resp) -{ -// const knot_dname_t *qname = knot_packet_qname(resp); -// assert(qname != NULL); - -// uint16_t qtype = knot_packet_qtype(resp); -//dbg_ns_exec( -// char *name_str = knot_dname_to_str(qname); -// dbg_ns("Trying to find zone for QNAME %s\n", name_str); -// free(name_str); -//); -// // find zone in which to search for the name -// const knot_zone_t *zone = -// ns_get_zone_for_qname(db, qname, qtype); +static int ns_answer(const knot_zone_t *zone, knot_packet_t *resp, + int check_any) +{ const knot_zone_contents_t *contents = knot_zone_contents(zone); // if no zone found, return REFUSED @@ -2012,9 +2343,7 @@ dbg_ns_exec( // take the zone contents and use only them for answering - return ns_answer_from_zone(contents, resp); - - //knot_dname_free(&qname); + return ns_answer_from_zone(contents, resp, check_any); } /*----------------------------------------------------------------------------*/ @@ -2026,10 +2355,9 @@ int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire, size_t rsize = 0; int ret = 0; - if ((ret = knot_packet_to_wire(resp, &rwire, &rsize)) - != KNOT_EOK) { + if ((ret = knot_packet_to_wire(resp, &rwire, &rsize)) != KNOT_EOK) { dbg_ns("Error converting response packet " - "to wire format (error %d).\n", ret); + "to wire format (error %d).\n", ret); return NS_ERR_SERVFAIL; } @@ -2041,14 +2369,13 @@ int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire, if (rwire != wire) { dbg_ns("Wire format reallocated, copying to place for " - "wire.\n"); + "wire.\n"); memcpy(wire, rwire, rsize); } else { dbg_ns("Using the same space or wire format.\n"); } *wire_size = rsize; - //free(rwire); return KNOT_EOK; } @@ -2073,17 +2400,17 @@ static int ns_error_response_to_wire(knot_packet_t *resp, uint8_t *wire, * wire format is assembled, but COUNTs in header are not set. * This is ideal, we just truncate the packet after the question. */ - dbg_ns("Creating error response.\n"); + dbg_ns_verb("Creating error response.\n"); size_t rsize = knot_packet_question_size(knot_packet_query(resp)); - dbg_ns("Error response (~ query) size: %zu\n", rsize); + dbg_ns_detail("Error response (~ query) size: %zu\n", rsize); // take 'qsize' from the current wireformat of the response // it is already assembled - Header and Question section are copied const uint8_t *rwire = knot_packet_wireformat(resp); if (rsize > *wire_size) { dbg_ns("Reponse size (%zu) larger than allowed wire size" - " (%zu).\n", rsize, *wire_size); + " (%zu).\n", rsize, *wire_size); return NS_ERR_SERVFAIL; } @@ -2116,8 +2443,8 @@ typedef struct ns_axfr_params { int knot_ns_tsig_required(int packet_nr) { - dbg_ns_detail("ns_tsig_required(%d): %d\n", packet_nr, - (packet_nr % KNOT_NS_TSIG_FREQ == 0)); + dbg_ns_verb("ns_tsig_required(%d): %d\n", packet_nr, + (packet_nr % KNOT_NS_TSIG_FREQ == 0)); return (packet_nr % KNOT_NS_TSIG_FREQ == 0); } @@ -2132,7 +2459,7 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) assert(xfr->send != NULL); // Transform the packet into wire format - dbg_ns("Converting response to wire format..\n"); + dbg_ns_verb("Converting response to wire format..\n"); size_t real_size = xfr->wire_size; if (ns_response_to_wire(xfr->response, xfr->wire, &real_size) != 0) { return NS_ERR_SERVFAIL; @@ -2154,15 +2481,14 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) xfr->tsig_data_size += real_size; } - /*! \note [TSIG] Generate TSIG if required (during XFR/IN). */ if (xfr->tsig_key && add_tsig) { if (xfr->packet_nr == 0) { /* Add key, digest and digest length. */ dbg_ns_detail("Calling tsig_sign(): %p, %zu, %zu, " - "%p, %zu, %p, %zu, %p\n", - xfr->wire, real_size, xfr->wire_size, - xfr->digest, xfr->digest_size, xfr->digest, - digest_real_size, xfr->tsig_key); + "%p, %zu, %p, %zu, %p\n", + xfr->wire, real_size, xfr->wire_size, + xfr->digest, xfr->digest_size, xfr->digest, + digest_real_size, xfr->tsig_key); res = knot_tsig_sign(xfr->wire, &real_size, xfr->wire_size, xfr->digest, xfr->digest_size, xfr->digest, @@ -2182,8 +2508,7 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) xfr->tsig_data_size); } - dbg_ns_detail("Sign function returned: %s\n", - knot_strerror(res)); + dbg_ns_verb("Sign function returned: %s\n", knot_strerror(res)); dbg_ns_detail("Real size of digest: %zu\n", digest_real_size); if (res != KNOT_EOK) { @@ -2198,8 +2523,8 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) xfr->tsig_data_size = 0; } else if (xfr->tsig_rcode != 0) { - dbg_ns_detail("Adding TSIG without signing, TSIG RCODE: %d.\n", - xfr->tsig_rcode); + dbg_ns_verb("Adding TSIG without signing, TSIG RCODE: %d.\n", + xfr->tsig_rcode); assert(xfr->tsig_rcode != KNOT_TSIG_RCODE_BADTIME); // add TSIG without signing assert(xfr->query != NULL); @@ -2225,12 +2550,11 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) return res; } else if (res != real_size) { dbg_ns("AXFR did not send right amount of bytes." - " Transfer size: %zu, sent: %d\n", - real_size, res); + " Transfer size: %zu, sent: %d\n", real_size, res); } // Clean the response structure - dbg_ns("Clearing response structure..\n"); + dbg_ns_verb("Clearing response structure..\n"); knot_response_clear(xfr->response, 0); // increment the packet number @@ -2243,8 +2567,10 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig) knot_packet_set_tsig_size(xfr->response, 0); } - dbg_ns("Response structure after clearing:\n"); +dbg_ns_exec_verb( + dbg_ns_verb("Response structure after clearing:\n"); knot_packet_dump(xfr->response); +); return KNOT_EOK; } @@ -2260,15 +2586,15 @@ static void ns_axfr_from_node(knot_node_t *node, void *data) if (params->ret != KNOT_EOK) { // just skip (will be called on next node with the same params - dbg_ns("Params contain error: %s, skipping node...\n", + dbg_ns_detail("Params contain error: %s, skipping node...\n", knot_strerror(params->ret)); return; } - dbg_ns("Params OK, answering AXFR from node %p.\n", node); -dbg_ns_exec( + dbg_ns_detail("Params OK, answering AXFR from node %p.\n", node); +dbg_ns_exec_verb( char *name = knot_dname_to_str(knot_node_owner(node)); - dbg_ns("Node ownerr: %s\n", name); + dbg_ns_verb("Node owner: %s\n", name); free(name); ); @@ -2289,8 +2615,8 @@ dbg_ns_exec( assert(rrsets[i] != NULL); rrset = rrsets[i]; rrset: - dbg_ns(" Type: %s\n", - knot_rrtype_to_string(knot_rrset_type(rrset))); + dbg_ns_verb(" Type: %s\n", + knot_rrtype_to_string(knot_rrset_type(rrset))); // do not add SOA if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA) { @@ -2477,9 +2803,7 @@ static int ns_ixfr_put_rrset(knot_ns_xfr_t *xfr, knot_rrset_t *rrset) /*! \todo Probably send back AXFR instead. */ knot_response_set_rcode(xfr->response, KNOT_RCODE_SERVFAIL); - /*! \todo Probably rename the function. */ ns_xfr_send_and_clear(xfr, 1); - //socket_close(xfr->session); /*! \todo Remove for UDP.*/ return res; } @@ -2546,12 +2870,10 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr) 0, 0, 0); if (res != KNOT_EOK) { dbg_ns("IXFR query cannot be answered: %s.\n", - knot_strerror(res)); + knot_strerror(res)); knot_response_set_rcode(xfr->response, KNOT_RCODE_SERVFAIL); - /*! \todo Probably rename the function. */ ns_xfr_send_and_clear(xfr, 1); -// socket_close(xfr->session); /*! \todo Remove for UDP.*/ rcu_read_unlock(); return res; } @@ -2571,10 +2893,7 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr) } if (res == KNOT_EOK) { - /*! \todo Probably rename the function. */ ns_xfr_send_and_clear(xfr, 1); - //socket_close(xfr->session); /*! \todo Remove for UDP.*/ -// return 1; } rcu_read_unlock(); @@ -2596,7 +2915,6 @@ static int ns_ixfr(knot_ns_xfr_t *xfr) // malformed packet dbg_ns("IXFR query does not contain authority record.\n"); knot_response_set_rcode(xfr->response, KNOT_RCODE_FORMERR); - /*! \todo Probably rename the function. */ if (ns_xfr_send_and_clear(xfr, 1) == KNOT_ECONN) { return KNOT_ECONN; } @@ -2614,11 +2932,9 @@ static int ns_ixfr(knot_ns_xfr_t *xfr) // malformed packet dbg_ns("IXFR query is malformed.\n"); knot_response_set_rcode(xfr->response, KNOT_RCODE_FORMERR); - /*! \todo Probably rename the function. */ if (ns_xfr_send_and_clear(xfr, 1) == KNOT_ECONN) { return KNOT_ECONN; } - //socket_close(xfr->session); /*! \todo Remove for UDP. */ return KNOT_EMALF; } @@ -2627,8 +2943,7 @@ static int ns_ixfr(knot_ns_xfr_t *xfr) /*----------------------------------------------------------------------------*/ -static int knot_ns_prepare_response(knot_nameserver_t *nameserver, - knot_packet_t *query, knot_packet_t **resp, +static int knot_ns_prepare_response(knot_packet_t *query, knot_packet_t **resp, size_t max_size) { assert(max_size >= 500); @@ -2641,8 +2956,6 @@ static int knot_ns_prepare_response(knot_nameserver_t *nameserver, } int ret = knot_packet_set_max_size(*resp, max_size); - //(*resp)->wireformat = response_wire;; - //(*resp)->max_size = max_size; if (ret != KNOT_EOK) { dbg_ns("Failed to init response structure.\n"); @@ -2827,14 +3140,12 @@ void knot_ns_set_nsid(knot_nameserver_t *nameserver, const char *nsid, size_t le int ret = knot_ns_replace_nsid(nameserver->opt_rr, nsid, len); -// int ret = knot_edns_add_option(nameserver->opt_rr, EDNS_OPTION_NSID, -// len, (const uint8_t *)nsid); if (ret != KNOT_EOK) { dbg_ns("NS: set_nsid: could not add EDNS option.\n"); return; } - dbg_ns("NS: set_nsid: added successfully.\n"); + dbg_ns_verb("NS: set_nsid: added successfully.\n"); } /*----------------------------------------------------------------------------*/ @@ -2847,28 +3158,21 @@ int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize, return KNOT_EBADARG; } - dbg_ns("ns_parse_packet() called with query size %zu.\n", qsize); - //dbg_ns_hex((char *)query_wire, qsize); - - if (qsize < 2) { - return KNOT_EMALF; - } + dbg_ns_verb("ns_parse_packet() called with query size %zu.\n", qsize); // 1) create empty response - dbg_ns("Parsing packet...\n"); - //parsed = knot_response_new_empty(NULL); + dbg_ns_verb("Parsing packet...\n"); int ret = 0; if ((ret = knot_packet_parse_from_wire(packet, query_wire, qsize, 1)) != 0) { dbg_ns("Error while parsing packet, " - "libknot error '%s'.\n", knot_strerror(ret)); -// knot_response_free(&parsed); + "libknot error '%s'.\n", knot_strerror(ret)); return KNOT_RCODE_FORMERR; } - dbg_ns("Parsed packet header and Question:\n"); + dbg_ns_verb("Parsed packet header and Question:\n"); knot_packet_dump(packet); // 3) determine the query type @@ -2904,17 +3208,15 @@ int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize, return KNOT_RCODE_NOTIMPL; } -// knot_packet_free(&packet); - return KNOT_EOK; } /*----------------------------------------------------------------------------*/ -void knot_ns_error_response(const knot_nameserver_t *nameserver, - uint16_t query_id, uint8_t *flags1_query, - uint8_t rcode, uint8_t *response_wire, - size_t *rsize) +static void knot_ns_error_response(const knot_nameserver_t *nameserver, + uint16_t query_id, uint8_t *flags1_query, + uint8_t rcode, uint8_t *response_wire, + size_t *rsize) { memcpy(response_wire, nameserver->err_response, nameserver->err_resp_size); @@ -2927,7 +3229,7 @@ void knot_ns_error_response(const knot_nameserver_t *nameserver, knot_wire_set_rd(response_wire); } knot_wire_set_opcode(response_wire, - knot_wire_flags_get_opcode(*flags1_query)); + knot_wire_flags_get_opcode(*flags1_query)); } // set the RCODE @@ -2937,10 +3239,10 @@ void knot_ns_error_response(const knot_nameserver_t *nameserver, /*----------------------------------------------------------------------------*/ -int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver, - const uint8_t *query, size_t size, - uint8_t rcode, uint8_t *response_wire, - size_t *rsize) +int knot_ns_error_response_from_query_wire(const knot_nameserver_t *nameserver, + const uint8_t *query, size_t size, + uint8_t rcode, + uint8_t *response_wire, size_t *rsize) { if (size < 2) { // ignore packet @@ -2964,6 +3266,63 @@ int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver, /*----------------------------------------------------------------------------*/ +int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver, + const knot_packet_t *query, + uint8_t rcode, uint8_t *response_wire, + size_t *rsize) +{ + if (query->parsed < 2) { + // ignore packet + return KNOT_EFEWDATA; + } + + if (query->parsed < KNOT_WIRE_HEADER_SIZE) { + return knot_ns_error_response_from_query_wire(nameserver, + query->wireformat, query->size, rcode, response_wire, + rsize); + } + + size_t max_size = *rsize; + uint8_t flags1 = knot_wire_get_flags1(knot_packet_wireformat(query)); + + // prepare the generic error response + knot_ns_error_response(nameserver, knot_packet_id(query), + &flags1, rcode, response_wire, + rsize); + + if (query->parsed > KNOT_WIRE_HEADER_SIZE + + KNOT_WIRE_QUESTION_MIN_SIZE) { + // in this case the whole question was parsed, append it + size_t question_size = 4 + knot_dname_size( + knot_packet_qname(query)); + + if (max_size > KNOT_WIRE_HEADER_SIZE + question_size) { + /* + * At this point, the wireformat of query may be in the + * same place where the response is assembled. This does + * not matter before this point, although the query + * wireformat is rewritten. Now we just need to copy + * the original Question section. So if the pointers are + * the same, we may just leave it and increase the + * response wire size. Otherwise we must copy the data. + */ + if (response_wire != knot_packet_wireformat(query)) { + memcpy(response_wire + KNOT_WIRE_HEADER_SIZE, + knot_packet_wireformat(query) + + KNOT_WIRE_HEADER_SIZE, question_size); + } + *rsize += question_size; + + // adjust QDCOUNT + knot_wire_set_qdcount(response_wire, 1); + } + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + void knot_ns_error_response_full(knot_nameserver_t *nameserver, knot_packet_t *response, uint8_t rcode, uint8_t *response_wire, size_t *rsize) @@ -2971,11 +3330,10 @@ void knot_ns_error_response_full(knot_nameserver_t *nameserver, knot_response_set_rcode(response, rcode); if (ns_error_response_to_wire(response, response_wire, rsize) != 0) { - knot_ns_error_response(nameserver, knot_packet_id( - knot_packet_query(response)), - &response->header.flags1, - KNOT_RCODE_SERVFAIL, response_wire, - rsize); + knot_ns_error_response_from_query(nameserver, + knot_packet_query(response), + KNOT_RCODE_SERVFAIL, + response_wire, rsize); } } @@ -2985,7 +3343,7 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, knot_packet_t *query, knot_packet_t **resp, const knot_zone_t **zone, size_t max_size) { - dbg_ns("knot_ns_prep_normal_response()\n"); + dbg_ns_verb("knot_ns_prep_normal_response()\n"); if (nameserver == NULL || query == NULL || resp == NULL || zone == NULL) { @@ -2994,8 +3352,8 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, // first, parse the rest of the packet assert(knot_packet_is_query(query)); - dbg_ns("Query - parsed: %zu, total wire size: %zu\n", - knot_packet_parsed(query), knot_packet_size(query)); + dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n", + knot_packet_parsed(query), knot_packet_size(query)); int ret; ret = knot_packet_parse_rest(query); @@ -3017,10 +3375,38 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, if (knot_packet_ancount(query) > 0 || knot_packet_nscount(query) > 0 || knot_packet_qdcount(query) != 1) { - dbg_ns("ANCOUNT or NSCOUNT not 0 in query, reply FORMERR.\n"); + dbg_ns("ANCOUNT or NSCOUNT not 0 in query, " + "or QDCOUNT != 1. Reply FORMERR.\n"); return KNOT_EMALF; } + /* + * Check what is in the Additional section. Only OPT and TSIG are + * allowed. TSIG must be the last record if present. + */ + if (knot_packet_arcount(query) > 0) { + int ok = 0; + const knot_rrset_t *add1 = + knot_packet_additional_rrset(query, 0); + if (knot_packet_additional_rrset_count(query) == 1 + && (knot_rrset_type(add1) == KNOT_RRTYPE_OPT + || knot_rrset_type(add1) == KNOT_RRTYPE_TSIG)) { + ok = 1; + } else if (knot_packet_additional_rrset_count(query) == 2) { + const knot_rrset_t *add2 = + knot_packet_additional_rrset(query, 1); + if (knot_rrset_type(add1) == KNOT_RRTYPE_OPT + && knot_rrset_type(add2) == KNOT_RRTYPE_TSIG) { + ok = 1; + } + } + + if (!ok) { + dbg_ns("Additional section malformed. Reply FORMERR\n"); + return KNOT_EMALF; + } + } + size_t resp_max_size = 0; knot_packet_dump(query); @@ -3044,22 +3430,21 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, resp_max_size = MAX_UDP_PAYLOAD; } - ret = knot_ns_prepare_response(nameserver, query, resp, - resp_max_size); + ret = knot_ns_prepare_response(query, resp, resp_max_size); if (ret != KNOT_EOK) { return KNOT_ERROR; } - dbg_ns("Query - parsed: %zu, total wire size: %zu\n", - query->parsed, query->size); - dbg_ns("Opt RR: version: %d, payload: %d\n", + dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n", + query->parsed, query->size); + dbg_ns_detail("Opt RR: version: %d, payload: %d\n", query->opt_rr.version, query->opt_rr.payload); // get the answer for the query knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db); - dbg_ns("EDNS supported in query: %d\n", - knot_query_edns_supported(query)); + dbg_ns_detail("EDNS supported in query: %d\n", + knot_query_edns_supported(query)); // set the OPT RR to the response if (knot_query_edns_supported(query)) { @@ -3067,25 +3452,24 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, knot_query_nsid_requested(query)); if (ret != KNOT_EOK) { dbg_ns("Failed to set OPT RR to the response" - ": %s\n", knot_strerror(ret)); + ": %s\n", knot_strerror(ret)); } else { // copy the DO bit from the query if (knot_query_dnssec_requested(query)) { - /*! \todo API for this. */ knot_edns_set_do(&(*resp)->opt_rr); } } } - dbg_ns("Response max size: %zu\n", (*resp)->max_size); + dbg_ns_verb("Response max size: %zu\n", (*resp)->max_size); const knot_dname_t *qname = knot_packet_qname(*resp); assert(qname != NULL); uint16_t qtype = knot_packet_qtype(*resp); -dbg_ns_exec( +dbg_ns_exec_verb( char *name_str = knot_dname_to_str(qname); - dbg_ns("Trying to find zone for QNAME %s\n", name_str); + dbg_ns_verb("Trying to find zone for QNAME %s\n", name_str); free(name_str); ); // find zone in which to search for the name @@ -3098,11 +3482,11 @@ dbg_ns_exec( int knot_ns_answer_normal(knot_nameserver_t *nameserver, const knot_zone_t *zone, knot_packet_t *resp, - uint8_t *response_wire, size_t *rsize) + uint8_t *response_wire, size_t *rsize, int check_any) { - dbg_ns("ns_answer_normal()\n"); + dbg_ns_verb("ns_answer_normal()\n"); - int ret = ns_answer(zone, resp); + int ret = ns_answer(zone, resp, check_any); if (ret != 0) { // now only one type of error (SERVFAIL), later maybe more @@ -3110,7 +3494,7 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver, KNOT_RCODE_SERVFAIL, response_wire, rsize); } else { - dbg_ns("Created response packet.\n"); + dbg_ns_verb("Created response packet.\n"); //knot_response_dump(resp); knot_packet_dump(resp); @@ -3123,6 +3507,55 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver, } } + dbg_ns_verb("Returning response with wire size %zu\n", *rsize); + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +int knot_ns_answer_ixfr_udp(knot_nameserver_t *nameserver, + const knot_zone_t *zone, knot_packet_t *resp, + uint8_t *response_wire, size_t *rsize) +{ + dbg_ns("ns_answer_ixfr_udp()\n"); + + const knot_zone_contents_t *contents = knot_zone_contents(zone); + + // if no zone found, return REFUSED + if (zone == NULL) { + dbg_ns("No zone found.\n"); + knot_response_set_rcode(resp, KNOT_RCODE_REFUSED); + return KNOT_EOK; + } else if (contents == NULL) { + dbg_ns("Zone expired or not bootstrapped. Reply SERVFAIL.\n"); + knot_response_set_rcode(resp, KNOT_RCODE_SERVFAIL); + return KNOT_EOK; + } + + const knot_node_t *apex = knot_zone_contents_apex(contents); + assert(apex != NULL); + knot_rrset_t *soa = knot_node_get_rrset(apex, KNOT_RRTYPE_SOA); + + // just put the SOA to the Answer section of the response and send back + int ret = knot_response_add_rrset_answer(resp, soa, 1, 0, 0, 0); + if (ret != KNOT_EOK) { + knot_ns_error_response_full(nameserver, resp, + KNOT_RCODE_SERVFAIL, + response_wire, rsize); + } + + dbg_ns("Created response packet.\n"); + knot_packet_dump(resp); + + // Transform the packet into wire format + if (ns_response_to_wire(resp, response_wire, rsize) != 0) { + // send back SERVFAIL (as this is our problem) + knot_ns_error_response_full(nameserver, resp, + KNOT_RCODE_SERVFAIL, + response_wire, rsize); + } + dbg_ns("Returning response with wire size %zu\n", *rsize); return KNOT_EOK; @@ -3134,18 +3567,21 @@ int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) { dbg_ns("knot_ns_init_xfr()\n"); + int ret = 0; + if (nameserver == NULL || xfr == NULL) { - return KNOT_EBADARG; + dbg_ns("Wrong parameters given to function ns_init_xfr()\n"); + /* Sending error was totally wrong. If nameserver or xfr were + * NULL, the ns_error_response() function would crash. + */ + return ret; } - // no need to parse rest of the packet - /*! \todo Parse rest of packet because of EDNS. */ - int ret = knot_packet_parse_rest(xfr->query); + ret = knot_packet_parse_rest(xfr->query); if (ret != KNOT_EOK) { dbg_ns("Failed to parse rest of the query: %s\n", knot_strerror(ret)); - knot_ns_error_response(nameserver, xfr->query->header.id, - &xfr->query->header.flags1, + knot_ns_error_response_from_query(nameserver, xfr->query, (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR : KNOT_RCODE_SERVFAIL, xfr->wire, &xfr->wire_size); @@ -3154,8 +3590,10 @@ int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) return ret; } - dbg_packet("Parsed XFR query:\n"); +dbg_ns_exec_verb( + dbg_ns_verb("Parsed XFR query:\n"); knot_packet_dump(xfr->query); +); // initialize response packet structure knot_packet_t *response = knot_packet_new( @@ -3163,41 +3601,25 @@ int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) if (response == NULL) { dbg_ns("Failed to create packet structure.\n"); /*! \todo xfr->wire is not NULL, will fail on assert! */ - knot_ns_error_response(nameserver, xfr->query->header.id, - &xfr->query->header.flags1, - KNOT_RCODE_SERVFAIL, xfr->wire, - &xfr->wire_size); + knot_ns_error_response_from_query(nameserver, xfr->query, + KNOT_RCODE_SERVFAIL, + xfr->wire, &xfr->wire_size); ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, xfr->wire_size); - knot_packet_free(&response); return ret; } - //int ret = knot_packet_set_max_size(response, xfr->wire_size); response->wireformat = xfr->wire; response->max_size = xfr->wire_size; -// if (ret != KNOT_EOK) { -// dbg_ns("Failed to init response structure.\n"); -// /*! \todo xfr->wire is not NULL, will fail on assert! */ -// knot_ns_error_response(nameserver, xfr->query->header.id, -// KNOT_RCODE_SERVFAIL, xfr->wire, -// &xfr->wire_size); -// int res = xfr->send(xfr->session, &xfr->addr, xfr->wire, -// xfr->wire_size); -// knot_packet_free(&response); -// return res; -// } - ret = knot_response_init_from_query(response, xfr->query); if (ret != KNOT_EOK) { dbg_ns("Failed to init response structure.\n"); /*! \todo xfr->wire is not NULL, will fail on assert! */ - knot_ns_error_response(nameserver, xfr->query->header.id, - &xfr->query->header.flags1, - KNOT_RCODE_SERVFAIL, xfr->wire, - &xfr->wire_size); + knot_ns_error_response_from_query(nameserver, xfr->query, + KNOT_RCODE_SERVFAIL, + xfr->wire, &xfr->wire_size); int res = xfr->send(xfr->session, &xfr->addr, xfr->wire, xfr->wire_size); knot_packet_free(&response); @@ -3213,9 +3635,9 @@ int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) assert(knot_packet_qtype(xfr->response) == KNOT_RRTYPE_AXFR || knot_packet_qtype(xfr->response) == KNOT_RRTYPE_IXFR); -dbg_ns_exec( +dbg_ns_exec_verb( char *name_str = knot_dname_to_str(qname); - dbg_ns("Trying to find zone with name %s\n", name_str); + dbg_ns_verb("Trying to find zone with name %s\n", name_str); free(name_str); ); // find zone in which to search for the name @@ -3258,20 +3680,20 @@ int ns_ixfr_load_serials(const knot_ns_xfr_t *xfr, uint32_t *serial_from, { if (xfr == NULL || xfr->zone == NULL || serial_from == NULL || serial_to == NULL) { - dbg_ns_detail("Wrong parameters: xfr=%p," - " xfr->zone = %p\n", xfr, xfr->zone); + dbg_ns("Wrong parameters: xfr=%p," + " xfr->zone = %p\n", xfr, xfr->zone); return KNOT_EBADARG; } const knot_zone_t *zone = xfr->zone; const knot_zone_contents_t *contents = knot_zone_contents(zone); if (!contents) { - dbg_ns_detail("Missing contents\n"); + dbg_ns("Missing contents\n"); return KNOT_EBADARG; } if (knot_zone_contents_apex(contents) == NULL) { - dbg_ns_detail("No apex.\n"); + dbg_ns("No apex.\n"); return KNOT_EBADARG; } @@ -3279,17 +3701,17 @@ int ns_ixfr_load_serials(const knot_ns_xfr_t *xfr, uint32_t *serial_from, knot_node_rrset(knot_zone_contents_apex(contents), KNOT_RRTYPE_SOA); if (zone_soa == NULL) { - dbg_ns_verb("No SOA.\n"); + dbg_ns("No SOA.\n"); return KNOT_EBADARG; } if (knot_packet_nscount(xfr->query) < 1) { - dbg_ns_verb("No Authority record.\n"); + dbg_ns("No Authority record.\n"); return KNOT_EMALF; } if (knot_packet_authority_rrset(xfr->query, 0) == NULL) { - dbg_ns_verb("Authority record missing.\n"); + dbg_ns("Authority record missing.\n"); return KNOT_ERROR; } @@ -3309,13 +3731,13 @@ int knot_ns_xfr_send_error(const knot_nameserver_t *nameserver, /*! \todo Handle TSIG errors differently. */ knot_response_set_rcode(xfr->response, rcode); - /*! \todo Probably rename the function. */ int ret = 0; - if ((ret = ns_xfr_send_and_clear(xfr, 1)) != KNOT_EOK) { + if ((ret = ns_xfr_send_and_clear(xfr, 1)) != KNOT_EOK + || xfr->response == NULL) { size_t size = 0; - knot_ns_error_response(nameserver, xfr->query->header.id, - &xfr->query->header.flags1, - KNOT_RCODE_SERVFAIL, xfr->wire, &size); + knot_ns_error_response_from_query(nameserver, xfr->query, + KNOT_RCODE_SERVFAIL, + xfr->wire, &size); ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size); } @@ -3337,11 +3759,7 @@ int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) knot_zone_contents_t *contents = knot_zone_get_contents(xfr->zone); if (!contents) { dbg_ns("AXFR failed on stub zone\n"); - /*! \todo replace with knot_ns_xfr_send_error() */ - knot_ns_error_response(nameserver, xfr->query->header.id, - &xfr->query->header.flags1, - KNOT_RCODE_SERVFAIL, xfr->wire, - &xfr->wire_size); + knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL); ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, xfr->wire_size); rcu_read_unlock(); @@ -3349,16 +3767,16 @@ int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) return ret; } - /*! - * \todo [TSIG] The TSIG data should already be stored in 'xfr'. - * Now just count the expected size of the TSIG RR and save it - * to the response structure. + /* + * The TSIG data should already be stored in 'xfr'. + * Now just count the expected size of the TSIG RR and save it + * to the response structure. */ /*! \todo [TSIG] Get the TSIG size from some API function. */ if (xfr->tsig_size > 0) { - dbg_ns_detail("Setting TSIG size in packet: %zu\n", - xfr->tsig_size); + dbg_ns_verb("Setting TSIG size in packet: %zu\n", + xfr->tsig_size); knot_packet_set_tsig_size(xfr->response, xfr->tsig_size); } @@ -3372,11 +3790,7 @@ int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) dbg_ns("AXFR failed, sending SERVFAIL.\n"); // now only one type of error (SERVFAIL), later maybe more /*! \todo xfr->wire is not NULL, will fail on assert! */ - /*! \todo replace with knot_ns_xfr_send_error() */ - knot_ns_error_response(nameserver, xfr->query->header.id, - &xfr->query->header.flags1, - KNOT_RCODE_SERVFAIL, xfr->wire, - &xfr->wire_size); + knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL); ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, xfr->wire_size); } else if (ret > 0) { @@ -3398,14 +3812,12 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) || xfr->response == NULL) { return KNOT_EBADARG; } - - //uint8_t *wire = NULL; - //size_t size = xfr->wire_size; // parse rest of the packet (we need the Authority record) int ret = knot_packet_parse_rest(xfr->query); if (ret != KNOT_EOK) { - dbg_ns("Failed to parse rest of the packet. Reply FORMERR.\n"); + dbg_ns("Failed to parse rest of the packet: %s. " + "Reply FORMERR.\n", knot_strerror(ret)); knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_FORMERR); knot_packet_free(&xfr->response); return ret; @@ -3419,11 +3831,11 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) return ret; } - /*! - * \todo [TSIG] The TSIG data should already be stored in 'xfr'. - * Now just count the expected size of the TSIG RR and save it - * to the response structure. This should be optional, only if - * the request contained TSIG, i.e. if there is the data in 'xfr'. + /* + * The TSIG data should already be stored in 'xfr'. + * Now just count the expected size of the TSIG RR and save it + * to the response structure. This should be optional, only if + * the request contained TSIG, i.e. if there is the data in 'xfr'. */ /*! \todo [TSIG] Get the TSIG size from some API function. */ @@ -3433,16 +3845,6 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) ret = ns_ixfr(xfr); -// /*! \todo Somehow distinguish when it makes sense to send the SERVFAIL -// * and when it does not. E.g. if there was problem in sending -// * packet, it will probably fail when sending the SERVFAIL also. -// */ -// if (ret < 0) { -// dbg_ns("IXFR failed, sending SERVFAIL.\n"); -// // now only one type of error (SERVFAIL), later maybe more -// knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL); -// } - knot_packet_free(&xfr->response); return ret; @@ -3452,18 +3854,16 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) { - /*! - * \todo [TSIG] Here we assume that 'xfr' contains TSIG information - * and the digest of the query sent to the master or the previous - * digest. + /* + * Here we assume that 'xfr' contains TSIG information + * and the digest of the query sent to the master or the previous + * digest. */ dbg_ns("ns_process_axfrin: incoming packet, wire size: %zu\n", - xfr->wire_size); + xfr->wire_size); - int ret = xfrin_process_axfr_packet(/*xfr->wire, xfr->wire_size,*/ - /*(xfrin_constructed_zone_t **)(&xfr->data)*/ - xfr); + int ret = xfrin_process_axfr_packet(xfr); if (ret > 0) { // transfer finished dbg_ns("ns_process_axfrin: AXFR finished, zone created.\n"); @@ -3477,19 +3877,19 @@ int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) assert(zone != NULL); /* Create and fill hash table */ - dbg_ns("ns_process_axfrin: filling hash table.\n"); + dbg_ns_verb("ns_process_axfrin: filling hash table.\n"); int rc = knot_zone_contents_create_and_fill_hash_table(zone); if (rc != KNOT_EOK) { return KNOT_ERROR; // TODO: change error code } - dbg_ns("ns_process_axfrin: adjusting zone.\n"); + dbg_ns_verb("ns_process_axfrin: adjusting zone.\n"); rc = knot_zone_contents_adjust(zone); if (rc != KNOT_EOK) { return rc; } - dbg_ns("ns_process_axfrin: checking loops.\n"); + dbg_ns_verb("ns_process_axfrin: checking loops.\n"); rc = knot_zone_contents_check_loops(zone); if (rc != KNOT_EOK) { return rc; @@ -3508,15 +3908,13 @@ int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) //knot_zone_contents_dump(zone, 0); // check zone integrity -dbg_xfrin_exec( +dbg_ns_exec_verb( int errs = knot_zone_contents_integrity_check(zone); - dbg_xfrin("Zone integrity check: %d errors.\n", errs); + dbg_ns_verb("Zone integrity check: %d errors.\n", errs); ); } - /*! - * \todo In case of error, shouldn't the zone be destroyed here? - */ + /*! \todo In case of error, shouldn't the zone be destroyed here? */ return ret; } @@ -3538,14 +3936,14 @@ int knot_ns_switch_zone(knot_nameserver_t *nameserver, return KNOT_ENOZONE; } - // find the zone in the zone db - knot_zone_t *z = knot_zonedb_find_zone(nameserver->zone_db, - knot_node_owner(knot_zone_contents_apex(zone))); + /* Zone must not be looked-up from server, as it may be a different zone if + * a reload occurs when transfer is pending. */ + knot_zone_t *z = xfr->zone; if (z == NULL) { char *name = knot_dname_to_str(knot_node_owner( knot_zone_contents_apex(zone))); dbg_ns("Failed to replace zone %s, old zone " - "not found\n", name); + "not found\n", name); free(name); return KNOT_ENOZONE; @@ -3555,16 +3953,18 @@ int knot_ns_switch_zone(knot_nameserver_t *nameserver, int ret = xfrin_switch_zone(z, zone, xfr->type); -dbg_ns_exec( - dbg_ns("Zone db contents: (zone count: %zu)\n", - nameserver->zone_db->zone_count); +dbg_ns_exec_verb( + dbg_ns_verb("Zone db contents: (zone count: %zu)\n", + nameserver->zone_db->zone_count); + /* Warning: may not show updated zone if updated zone that is already + * discarded from zone db (reload with pending transfer). */ const knot_zone_t **zones = knot_zonedb_zones(nameserver->zone_db); for (int i = 0; i < knot_zonedb_zone_count (nameserver->zone_db); i++) { - dbg_ns("%d. zone: %p", i, zones[i]); + dbg_ns_verb("%d. zone: %p\n", i, zones[i]); char *name = knot_dname_to_str(zones[i]->name); - dbg_ns(" zone name: %s\n", name); + dbg_ns_verb(" zone name: %s\n", name); free(name); } free(zones); @@ -3574,18 +3974,16 @@ dbg_ns_exec( } /*----------------------------------------------------------------------------*/ -/*! \todo In this function, xfr->zone is properly set. If this is so, we do not - * have to search for the zone after the transfer has finished. - */ + int knot_ns_process_ixfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) { dbg_ns("ns_process_ixfrin: incoming packet\n"); - /*! - * \todo [TSIG] Here we assume that 'xfr' contains TSIG information - * and the digest of the query sent to the master or the previous - * digest. + /* + * [TSIG] Here we assume that 'xfr' contains TSIG information + * and the digest of the query sent to the master or the previous + * digest. */ int ret = xfrin_process_ixfr_packet(xfr); @@ -3608,19 +4006,18 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver, } // find zone associated with the changesets - knot_zone_t *zone = knot_zonedb_find_zone( - nameserver->zone_db, - knot_rrset_owner(chgsets->first_soa)); + /* Must not search for the zone in zonedb as it may fetch a + * different zone than the one the transfer started on. */ + knot_zone_t *zone = xfr->zone; if (zone == NULL) { dbg_ns("No zone found for incoming IXFR!\n"); knot_free_changesets( (knot_changesets_t **)(&xfr->data)); - return KNOT_ENOZONE; /*! \todo Other error code? */ + return KNOT_ENOZONE; } switch (ret) { case XFRIN_RES_COMPLETE: - xfr->zone = zone; break; case XFRIN_RES_SOA_ONLY: { // compare the SERIAL from the changeset with the zone's @@ -3666,9 +4063,7 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver, } } - /*! - * \todo In case of error, shouldn't the zone be destroyed here? - */ + /*! \todo In case of error, shouldn't the zone be destroyed here? */ return ret; } @@ -3684,26 +4079,24 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query, knot_packet_t *response; assert(*rsize >= MAX_UDP_PAYLOAD); - int ret = knot_ns_prepare_response(nameserver, query, &response, - MAX_UDP_PAYLOAD); + int ret = knot_ns_prepare_response(query, &response, MAX_UDP_PAYLOAD); if (ret != KNOT_EOK) { - knot_ns_error_response(nameserver, knot_packet_id(query), - &query->header.flags1, - KNOT_RCODE_SERVFAIL, response_wire, - rsize); + knot_ns_error_response_from_query(nameserver, query, + KNOT_RCODE_SERVFAIL, + response_wire, rsize); return KNOT_EOK; } assert(response != NULL); - dbg_ns("Query - parsed: %zu, total wire size: %zu\n", - query->parsed, query->size); + dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n", + query->parsed, query->size); if (knot_packet_parsed(query) < knot_packet_size(query)) { ret = knot_packet_parse_rest(query); if (ret != KNOT_EOK) { dbg_ns("Failed to parse rest of the query: " - "%s.\n", knot_strerror(ret)); + "%s.\n", knot_strerror(ret)); knot_ns_error_response_full(nameserver, response, (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR @@ -3714,17 +4107,16 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query, } } - dbg_ns("Query - parsed: %zu, total wire size: %zu\n", - knot_packet_parsed(query), knot_packet_size(query)); + dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n", + knot_packet_parsed(query), knot_packet_size(query)); /*! \todo API for EDNS values. */ - dbg_ns("Opt RR: version: %d, payload: %d\n", - query->opt_rr.version, query->opt_rr.payload); + dbg_ns_verb("Opt RR: version: %d, payload: %d\n", + query->opt_rr.version, query->opt_rr.payload); // 2) Find zone for the query // we do not check if there is only one entry in the Question section // because the packet structure does not allow it - /*! \todo Check number of Question entries while parsing. */ if (knot_packet_qtype(query) != KNOT_RRTYPE_SOA) { dbg_ns("Question is not of type SOA.\n"); knot_ns_error_response_full(nameserver, response, @@ -3754,7 +4146,7 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query, return KNOT_EBADZONE; } else if (ret != KNOT_EOK) { dbg_ns("Failed to check zone for update: " - "%s.\n", knot_strerror(ret)); + "%s.\n", knot_strerror(ret)); knot_ns_error_response_full(nameserver, response, rcode, response_wire, rsize); knot_packet_free(&response); @@ -3766,7 +4158,7 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query, ret = knot_ddns_process_prereqs(query, &prereqs, &rcode); if (ret != KNOT_EOK) { dbg_ns("Failed to check zone for update: " - "%s.\n", knot_strerror(ret)); + "%s.\n", knot_strerror(ret)); knot_ns_error_response_full(nameserver, response, rcode, response_wire, rsize); knot_packet_free(&response); @@ -3783,7 +4175,7 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query, &rcode); if (ret != KNOT_EOK) { dbg_ns("Failed to check zone for update: " - "%s.\n", knot_strerror(ret)); + "%s.\n", knot_strerror(ret)); knot_ns_error_response_full(nameserver, response, rcode, response_wire, rsize); knot_ddns_prereqs_free(&prereqs); @@ -3795,7 +4187,7 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query, ret = knot_ddns_process_update(query, changeset, &rcode); if (ret != KNOT_EOK) { dbg_ns("Failed to check zone for update: " - "%s.\n", knot_strerror(ret)); + "%s.\n", knot_strerror(ret)); knot_ns_error_response_full(nameserver, response, rcode, response_wire, rsize); knot_ddns_prereqs_free(&prereqs); diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h index da19f49..3fe1210 100644..100755 --- a/src/libknot/nameserver/name-server.h +++ b/src/libknot/nameserver/name-server.h @@ -115,8 +115,6 @@ typedef struct knot_ns_xfr { */ uint8_t *tsig_data; size_t tsig_data_size; /*!< Size of the message(s) in bytes */ -// const knot_rrset_t *tsig; /*!< Response TSIG. -// \todo [TSIG] Replace with separate data. */ size_t tsig_size; /*!< Size of the TSIG RR wireformat in bytes.*/ knot_key_t *tsig_key; /*!< Associated TSIG key for signing. */ @@ -127,13 +125,6 @@ typedef struct knot_ns_xfr { uint16_t tsig_rcode; uint64_t tsig_prev_time_signed; - /*! \brief Previous digest or request digest. - * - * Should be allocated before the transfer (known size). - */ -// uint8_t *prev_digest; -// size_t prev_digest_size; /*!< Size of previous digest in bytes. */ - /*! * \brief Number of the packet currently assembled. * @@ -154,7 +145,7 @@ static const size_t KNOT_NS_TSIG_DATA_MAX_SIZE = 100 * 64 * 1024; enum knot_ns_xfr_flag_t { XFR_FLAG_TCP = 1 << 0, /*!< XFR request is on TCP. */ XFR_FLAG_UDP = 1 << 1, /*!< XFR request is on UDP. */ - XFR_FLAG_AXFR_FINISHED = 1 << 2 + XFR_FLAG_AXFR_FINISHED = 1 << 2 /*!< Transfer is finished. */ }; typedef enum knot_ns_transport { @@ -218,26 +209,13 @@ void knot_ns_set_nsid(knot_nameserver_t *nameserver, const char *nsid, int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize, knot_packet_t *packet, knot_packet_type_t *type); -/*! - * \brief Prepares wire format of an error response using generic error template - * stored in the nameserver structure. - * - * The error response will not contain the Question section from the query, just - * a header with ID copied from the query and the given RCODE. - * - * \param nameserver Nameserver structure containing the error template. - * \param query_id ID of the query. - * \param rcode RCODE to set in the response. - * \param response_wire Place for wire format of the response. - * \param rsize Size of the error response will be stored here. - */ -void knot_ns_error_response(const knot_nameserver_t *nameserver, - uint16_t query_id, uint8_t *flags1_query, - uint8_t rcode, uint8_t *response_wire, - size_t *rsize); +int knot_ns_error_response_from_query_wire(const knot_nameserver_t *nameserver, + const uint8_t *query, size_t size, + uint8_t rcode, uint8_t *response_wire, + size_t *rsize); int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver, - const uint8_t *query, size_t size, + const knot_packet_t *query, uint8_t rcode, uint8_t *response_wire, size_t *rsize); @@ -264,7 +242,11 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, */ int knot_ns_answer_normal(knot_nameserver_t *nameserver, const knot_zone_t *zone, knot_packet_t *resp, - uint8_t *response_wire, size_t *rsize); + uint8_t *response_wire, size_t *rsize, int check_any); + +int knot_ns_answer_ixfr_udp(knot_nameserver_t *nameserver, + const knot_zone_t *zone, knot_packet_t *resp, + uint8_t *response_wire, size_t *rsize); int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr); @@ -322,11 +304,11 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr); * \param nameserver Name server structure to provide the data for answering. * \param xfr Persistent transfer-specific data. * - * \todo Document me. */ int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr); +/*! \todo Document me. */ int knot_ns_switch_zone(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr); diff --git a/src/libknot/nsec3.c b/src/libknot/nsec3.c index 1414e7e..9cab4be 100644..100755 --- a/src/libknot/nsec3.c +++ b/src/libknot/nsec3.c @@ -74,7 +74,7 @@ int knot_nsec3_params_from_wire(knot_nsec3_params_t *params, dbg_nsec3("Flags: %hu\n", params->flags); dbg_nsec3("Iterations: %hu\n", params->iterations); dbg_nsec3("Salt length: %hu\n", params->salt_length); - dbg_nsec3("Salt: "); + dbg_nsec3("Salt: \n"); if (params->salt != NULL) { dbg_nsec3_hex((char *)params->salt, params->salt_length); @@ -171,8 +171,8 @@ int knot_nsec3_sha1(const knot_nsec3_params_t *params, EVP_MD_CTX_cleanup(&mdctx); - dbg_nsec3("NSEC3 hashing: calls: %lu, avg time per call: %f." - "\n", calls, (double)(total_time) / calls); + dbg_nsec3_verb("NSEC3 hashing: calls: %lu, avg time per call: %f." + "\n", calls, (double)(total_time) / calls); free(data_low); return 0; @@ -194,17 +194,17 @@ int knot_nsec3_sha1(const knot_nsec3_params_t *params, uint8_t salt_length = params->salt_length; uint16_t iterations = params->iterations; - dbg_nsec3("Hashing: \n"); - dbg_nsec3(" Data: %.*s \n", size, data); - dbg_nsec3_hex((const char *)data, size); - dbg_nsec3(" (size %d)\n Iterations: %u\n", (int)size, iterations); - dbg_nsec3(" Salt length: %u\n", salt_length); - dbg_nsec3(" Salt: "); + dbg_nsec3_verb("Hashing: \n"); + dbg_nsec3_verb(" Data: %.*s \n", size, data); + dbg_nsec3_hex_verb((const char *)data, size); + dbg_nsec3_verb(" (size %d)\n Iterations: %u\n", (int)size, iterations); + dbg_nsec3_verb(" Salt length: %u\n", salt_length); + dbg_nsec3_verb(" Salt: \n"); if (salt_length > 0) { - dbg_nsec3_hex((char *)salt, salt_length); - dbg_nsec3("\n"); + dbg_nsec3_hex_verb((char *)salt, salt_length); + dbg_nsec3_verb("\n"); } else { - dbg_nsec3("none\n"); + dbg_nsec3_verb("none\n"); } SHA_CTX ctx; @@ -251,9 +251,9 @@ int knot_nsec3_sha1(const knot_nsec3_params_t *params, *digest_size = SHA_DIGEST_LENGTH; - dbg_nsec3("Hash: %.*s\n", *digest_size, *digest); - dbg_nsec3_hex((const char *)*digest, *digest_size); - dbg_nsec3("\n"); + dbg_nsec3_verb("Hash: %.*s\n", *digest_size, *digest); + dbg_nsec3_hex_verb((const char *)*digest, *digest_size); + dbg_nsec3_verb("\n"); free(data_low); return KNOT_EOK; diff --git a/src/libknot/nsec3.h b/src/libknot/nsec3.h index 0ce6899..0ce6899 100644..100755 --- a/src/libknot/nsec3.h +++ b/src/libknot/nsec3.h diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c index 82b65c6..6c7fd02 100644..100755 --- a/src/libknot/packet/packet.c +++ b/src/libknot/packet/packet.c @@ -22,6 +22,7 @@ #include "common.h" #include "util/descriptor.h" #include "util/wire.h" +#include "tsig.h" /*----------------------------------------------------------------------------*/ @@ -51,7 +52,7 @@ typedef enum { */ static void knot_packet_init_pointers_response(knot_packet_t *pkt) { - dbg_packet("Packet pointer: %p\n", pkt); + dbg_packet_detail("Packet pointer: %p\n", pkt); char *pos = (char *)pkt + PREALLOC_PACKET; @@ -59,7 +60,7 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt) pkt->question.qname = (knot_dname_t *)pos; pos += PREALLOC_QNAME_DNAME; - dbg_packet("QNAME: %p\n", pkt->question.qname); + dbg_packet_detail("QNAME: %p\n", pkt->question.qname); pkt->question.qname->name = (uint8_t *)pos; pos += PREALLOC_QNAME_NAME; @@ -67,7 +68,7 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt) pos += PREALLOC_QNAME_LABELS; pkt->owner_tmp = (uint8_t *)pos; - dbg_packet("Tmp owner: %p\n", pkt->owner_tmp); + dbg_packet_detail("Tmp owner: %p\n", pkt->owner_tmp); pos += PREALLOC_RR_OWNER; // then answer, authority and additional sections @@ -92,9 +93,9 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt) pos += DEFAULT_ARCOUNT * sizeof(const knot_rrset_t *); } - dbg_packet("Answer section: %p\n", pkt->answer); - dbg_packet("Authority section: %p\n", pkt->authority); - dbg_packet("Additional section: %p\n", pkt->additional); + dbg_packet_detail("Answer section: %p\n", pkt->answer); + dbg_packet_detail("Authority section: %p\n", pkt->authority); + dbg_packet_detail("Additional section: %p\n", pkt->additional); pkt->max_an_rrsets = DEFAULT_ANCOUNT; pkt->max_ns_rrsets = DEFAULT_NSCOUNT; @@ -106,8 +107,8 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt) pkt->compression.offsets = (size_t *)pos; pos += DEFAULT_DOMAINS_IN_RESPONSE * sizeof(size_t); - dbg_packet("Compression dnames: %p\n", pkt->compression.dnames); - dbg_packet("Compression offsets: %p\n", pkt->compression.offsets); + dbg_packet_detail("Compression dnames: %p\n", pkt->compression.dnames); + dbg_packet_detail("Compression offsets: %p\n", pkt->compression.offsets); pkt->compression.max = DEFAULT_DOMAINS_IN_RESPONSE; pkt->compression.default_count = DEFAULT_DOMAINS_IN_RESPONSE; @@ -118,8 +119,8 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt) pkt->wildcard_nodes.snames = (const knot_dname_t **)pos; pos += DEFAULT_WILDCARD_NODES * sizeof(knot_dname_t *); - dbg_packet("Wildcard nodes: %p\n", pkt->wildcard_nodes.nodes); - dbg_packet("Wildcard SNAMEs: %p\n", pkt->wildcard_nodes.snames); + dbg_packet_detail("Wildcard nodes: %p\n", pkt->wildcard_nodes.nodes); + dbg_packet_detail("Wildcard SNAMEs: %p\n", pkt->wildcard_nodes.snames); pkt->wildcard_nodes.default_count = DEFAULT_WILDCARD_NODES; pkt->wildcard_nodes.max = DEFAULT_WILDCARD_NODES; @@ -127,7 +128,7 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt) pkt->tmp_rrsets = (const knot_rrset_t **)pos; pos += DEFAULT_TMP_RRSETS * sizeof(const knot_rrset_t *); - dbg_packet("Tmp rrsets: %p\n", pkt->tmp_rrsets); + dbg_packet_detail("Tmp rrsets: %p\n", pkt->tmp_rrsets); pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS; @@ -141,7 +142,7 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt) */ static void knot_packet_init_pointers_query(knot_packet_t *pkt) { - dbg_packet("Packet pointer: %p\n", pkt); + dbg_packet_detail("Packet pointer: %p\n", pkt); char *pos = (char *)pkt + PREALLOC_PACKET; @@ -149,17 +150,15 @@ static void knot_packet_init_pointers_query(knot_packet_t *pkt) pkt->question.qname = (knot_dname_t *)pos; pos += PREALLOC_QNAME_DNAME; - dbg_packet("QNAME: %p (%zu after start of packet)\n", - pkt->question.qname, - (void *)pkt->question.qname - (void *)pkt); + dbg_packet_detail("QNAME: %p (%zu after start of packet)\n", + pkt->question.qname, + (void *)pkt->question.qname - (void *)pkt); pkt->question.qname->name = (uint8_t *)pos; pos += PREALLOC_QNAME_NAME; pkt->question.qname->labels = (uint8_t *)pos; pos += PREALLOC_QNAME_LABELS; -// pkt->owner_tmp = (uint8_t *)((char *)pkt->question.qname->labels -// + PREALLOC_QNAME_LABELS); // then answer, authority and additional sections if (DEFAULT_ANCOUNT_QUERY == 0) { @@ -183,9 +182,9 @@ static void knot_packet_init_pointers_query(knot_packet_t *pkt) pos += DEFAULT_ARCOUNT_QUERY * sizeof(const knot_rrset_t *); } - dbg_packet("Answer section: %p\n", pkt->answer); - dbg_packet("Authority section: %p\n", pkt->authority); - dbg_packet("Additional section: %p\n", pkt->additional); + dbg_packet_detail("Answer section: %p\n", pkt->answer); + dbg_packet_detail("Authority section: %p\n", pkt->authority); + dbg_packet_detail("Additional section: %p\n", pkt->additional); pkt->max_an_rrsets = DEFAULT_ANCOUNT_QUERY; pkt->max_ns_rrsets = DEFAULT_NSCOUNT_QUERY; @@ -194,15 +193,11 @@ static void knot_packet_init_pointers_query(knot_packet_t *pkt) pkt->tmp_rrsets = (const knot_rrset_t **)pos; pos += DEFAULT_TMP_RRSETS_QUERY * sizeof(const knot_rrset_t *); - dbg_packet("Tmp rrsets: %p\n", pkt->tmp_rrsets); + dbg_packet_detail("Tmp rrsets: %p\n", pkt->tmp_rrsets); pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS_QUERY; -// dbg_packet("End of data: %p (%zu after start of packet)\n", -// pkt->tmp_rrsets + DEFAULT_TMP_RRSETS_QUERY, -// (void *)(pkt->tmp_rrsets + DEFAULT_TMP_RRSETS_QUERY) -// - (void *)pkt); - dbg_packet("Allocated total: %u\n", PREALLOC_QUERY); + dbg_packet_detail("Allocated total: %u\n", PREALLOC_QUERY); assert(pos == (char *)pkt + PREALLOC_QUERY); } @@ -230,7 +225,7 @@ static int knot_packet_parse_header(const uint8_t *wire, size_t *pos, assert(header != NULL); if (size - *pos < KNOT_WIRE_HEADER_SIZE) { - dbg_response("Not enough data to parse header.\n"); + dbg_packet("Not enough data to parse header.\n"); return KNOT_EFEWDATA; } @@ -238,10 +233,8 @@ static int knot_packet_parse_header(const uint8_t *wire, size_t *pos, // copy some of the flags: OPCODE and RD // do this by copying flags1 and setting QR to 1, AA to 0 and TC to 0 header->flags1 = knot_wire_get_flags1(wire); -// knot_wire_flags_set_qr(&header->flags1); -// knot_wire_flags_clear_aa(&header->flags1); -// knot_wire_flags_clear_tc(&header->flags1); // do not copy flags2 (all set by server) + header->qdcount = knot_wire_get_qdcount(wire); header->ancount = knot_wire_get_ancount(wire); header->nscount = knot_wire_get_nscount(wire); @@ -276,12 +269,11 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos, assert(question != NULL); if (size - *pos < KNOT_WIRE_QUESTION_MIN_SIZE) { - dbg_response("Not enough data to parse question.\n"); + dbg_packet("Not enough data to parse question.\n"); return KNOT_EFEWDATA; // malformed } - dbg_response("Parsing Question starting on position %zu.\n", - *pos); + dbg_packet("Parsing Question starting on position %zu.\n", *pos); // domain name must end with 0, so just search for 0 int i = *pos; @@ -290,13 +282,13 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos, } if (size - i - 1 < 4) { - dbg_response("Not enough data to parse question.\n"); + dbg_packet("Not enough data to parse question.\n"); return KNOT_EFEWDATA; // no 0 found or not enough data left } - dbg_response("Parsing dname starting on position %zu and " + dbg_packet_verb("Parsing dname starting on position %zu and " "%zu bytes long.\n", *pos, i - *pos + 1); - dbg_response("Alloc: %d\n", alloc); + dbg_packet_verb("Alloc: %d\n", alloc); if (alloc) { question->qname = knot_dname_new_from_wire( wire + *pos, i - *pos + 1, NULL); @@ -314,10 +306,7 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos, *pos = i + 1; question->qtype = knot_wire_read_u16(wire + i + 1); - //*pos += 2; question->qclass = knot_wire_read_u16(wire + i + 3); - //*pos += 2; - *pos += 4; return KNOT_EOK; @@ -340,7 +329,7 @@ static int knot_packet_realloc_rrsets(const knot_rrset_t ***rrsets, short *max_count, short default_max_count, short step) { - dbg_packet("Max count: %d, default max count: %d\n", + dbg_packet_verb("Max count: %d, default max count: %d\n", *max_count, default_max_count); int free_old = (*max_count) != default_max_count; const knot_rrset_t **old = *rrsets; @@ -368,11 +357,6 @@ static knot_rdata_t *knot_packet_parse_rdata(const uint8_t *wire, size_t *pos, size_t total_size, size_t rdlength, const knot_rrtype_descriptor_t *desc) { -// if (desc->type == 0) { -// dbg_packet("Unknown RR type.\n"); -// return NULL; -// } - knot_rdata_t *rdata = knot_rdata_new(); if (rdata == NULL) { return NULL; @@ -383,7 +367,7 @@ static knot_rdata_t *knot_packet_parse_rdata(const uint8_t *wire, if (rc != KNOT_EOK) { dbg_packet("rdata_from_wire() returned: %s\n", - knot_strerror(rc)); + knot_strerror(rc)); knot_rdata_free(&rdata); return NULL; } @@ -396,38 +380,32 @@ static knot_rdata_t *knot_packet_parse_rdata(const uint8_t *wire, static knot_rrset_t *knot_packet_parse_rr(const uint8_t *wire, size_t *pos, size_t size) { -// knot_rrset_t *rrset = -// (knot_rrset_t *)malloc(sizeof(knot_rrset_t)); -// CHECK_ALLOC_LOG(rrset, NULL); - dbg_packet("Parsing RR from position: %zu, total size: %zu\n", - *pos, size); + *pos, size); knot_dname_t *owner = knot_dname_parse_from_wire(wire, pos, size, NULL); - dbg_packet("Created owner: %p, actual position: %zu\n", owner, + dbg_packet_detail("Created owner: %p, actual position: %zu\n", owner, *pos); if (owner == NULL) { return NULL; } -dbg_packet_exec( +dbg_packet_exec_verb( char *name = knot_dname_to_str(owner); - dbg_packet("Parsed name: %s\n", name); + dbg_packet_verb("Parsed name: %s\n", name); free(name); ); - //*remaining -= knot_dname_size(rrset->owner); - /*! @todo Get rid of the numerical constant. */ if (size - *pos < 10) { dbg_packet("Malformed RR: Not enough data to parse RR" - " header.\n"); + " header.\n"); knot_dname_release(owner); return NULL; } - dbg_packet("Reading type from position %zu\n", *pos); + dbg_packet_detail("Reading type from position %zu\n", *pos); uint16_t type = knot_wire_read_u16(wire + *pos); uint16_t rclass = knot_wire_read_u16(wire + *pos + 2); @@ -445,7 +423,7 @@ dbg_packet_exec( uint16_t rdlength = knot_wire_read_u16(wire + *pos + 8); - dbg_packet("Read RR header: type %u, class %u, ttl %u, " + dbg_packet_detail("Read RR header: type %u, class %u, ttl %u, " "rdlength %u\n", rrset->type, rrset->rclass, rrset->ttl, rdlength); @@ -453,10 +431,8 @@ dbg_packet_exec( if (size - *pos < rdlength) { dbg_packet("Malformed RR: Not enough data to parse RR" - " RDATA (size: %zu, position: %zu).\n", - size, *pos); + " RDATA (size: %zu, position: %zu).\n", size, *pos); knot_rrset_deep_free(&rrset, 1, 1, 0); -// free(rrset); return NULL; } @@ -473,16 +449,13 @@ dbg_packet_exec( if (rdata == NULL) { dbg_packet("Malformed RR: Could not parse RDATA.\n"); knot_rrset_deep_free(&rrset, 1, 1, 0); -// free(rrset); return NULL; } if (knot_rrset_add_rdata(rrset, rdata) != KNOT_EOK) { - dbg_packet("Malformed RR: Could not add RDATA to RRSet" - ".\n"); + dbg_packet("Malformed RR: Could not add RDATA to RRSet.\n"); knot_rdata_free(&rdata); knot_rrset_deep_free(&rrset, 1, 1, 0); -// free(rrset); return NULL; } @@ -492,23 +465,22 @@ dbg_packet_exec( /*----------------------------------------------------------------------------*/ static int knot_packet_add_rrset(knot_rrset_t *rrset, - const knot_rrset_t ***rrsets, - short *rrset_count, - short *max_rrsets, - short default_rrsets, - const knot_packet_t *packet, - knot_packet_duplicate_handling_t dupl) + const knot_rrset_t ***rrsets, + short *rrset_count, + short *max_rrsets, + short default_rrsets, + const knot_packet_t *packet, + knot_packet_duplicate_handling_t dupl) { - assert(rrset != NULL); assert(rrsets != NULL); assert(rrset_count != NULL); assert(max_rrsets != NULL); -dbg_packet_exec( +dbg_packet_exec_verb( char *name = knot_dname_to_str(rrset->owner); - dbg_packet("packet_add_rrset(), owner: %s, type: %s\n", - name, knot_rrtype_to_string(rrset->type)); + dbg_packet_verb("packet_add_rrset(), owner: %s, type: %s\n", + name, knot_rrtype_to_string(rrset->type)); free(name); ); @@ -529,19 +501,20 @@ dbg_packet_exec( // try to find the RRSet in this array of RRSets for (int i = 0; i < *rrset_count; ++i) { -dbg_packet_exec( +dbg_packet_exec_detail( char *name = knot_dname_to_str((*rrsets)[i]->owner); - dbg_packet("Comparing to RRSet: owner: %s, " - "type: %s\n", name, - knot_rrtype_to_string( - (*rrsets)[i]->type)); + dbg_packet_detail("Comparing to RRSet: owner: %s, " + "type: %s\n", name, + knot_rrtype_to_string( + (*rrsets)[i]->type)); free(name); ); if (knot_rrset_compare((*rrsets)[i], rrset, KNOT_RRSET_COMPARE_HEADER)) { - //const knot_rrset_t *r = (*rrsets) /*! \todo Test this!!! */ + // no duplicate checking here, the packet should + // look exactly as it came from wire int rc = knot_rrset_merge( (void **)((*rrsets) + i), (void **)&rrset); if (rc != KNOT_EOK) { @@ -561,11 +534,12 @@ dbg_packet_exec( /*----------------------------------------------------------------------------*/ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos, - size_t size, uint16_t rr_count, - const knot_rrset_t ***rrsets, - short *rrset_count, short *max_rrsets, - short default_rrsets, - knot_packet_t *packet) + size_t size, uint16_t rr_count, + uint16_t *parsed_rrs, + const knot_rrset_t ***rrsets, + short *rrset_count, short *max_rrsets, + short default_rrsets, + knot_packet_t *packet) { assert(pos != NULL); assert(wire != NULL); @@ -574,12 +548,7 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos, assert(max_rrsets != NULL); assert(packet != NULL); - dbg_packet("Parsing RRSets starting on position: %zu\n", - *pos); - -// if (*rrsets == NULL) { -// knot_packet_realloc_rrsets(rrsets, max_rrsets, 0, 1); -// } + dbg_packet("Parsing RRSets starting on position: %zu\n", *pos); /* * The RRs from one RRSet may be scattered in the current section. @@ -589,7 +558,8 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos, int err = KNOT_EOK; knot_rrset_t *rrset = NULL; - for (int i = 0; i < rr_count; ++i) { + /* Start parsing from the first RR not parsed. */ + for (int i = *parsed_rrs; i < rr_count; ++i) { rrset = knot_packet_parse_rr(wire, pos, size); if (rrset == NULL) { dbg_packet("Failed to parse RR!\n"); @@ -597,13 +567,15 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos, break; } + ++(*parsed_rrs); + err = knot_packet_add_rrset(rrset, rrsets, rrset_count, - max_rrsets, default_rrsets, packet, - KNOT_PACKET_DUPL_MERGE); + max_rrsets, default_rrsets, packet, + KNOT_PACKET_DUPL_MERGE); if (err < 0) { break; } else if (err > 0) { // merged - dbg_packet("RRSet merged, freeing.\n"); + dbg_packet_detail("RRSet merged, freeing.\n"); knot_rrset_deep_free(&rrset, 1, 0, 0); // TODO: ok?? continue; } @@ -616,6 +588,23 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos, knot_rrset_deep_free(&rrset, 1, 1, 1); break; } + + if (knot_rrset_type(rrset) == KNOT_RRTYPE_TSIG) { + // if there is some TSIG already, treat as malformed + if (knot_packet_tsig(packet) != NULL) { + err = KNOT_EMALF; + break; + } + + // First check the format of the TSIG RR + if (!tsig_rdata_is_ok(rrset)) { + err = KNOT_EMALF; + break; + } + + // store the TSIG into the packet + knot_packet_set_tsig(packet, rrset); + } } return (err < 0) ? err : KNOT_EOK; @@ -630,9 +619,9 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos, */ static void knot_packet_free_allocated_space(knot_packet_t *pkt) { - dbg_packet("Freeing additional space in packet.\n"); + dbg_packet_verb("Freeing additional space in packet.\n"); if (pkt->prealloc_type == KNOT_PACKET_PREALLOC_NONE) { - dbg_packet("Freeing QNAME.\n"); + dbg_packet_detail("Freeing QNAME.\n"); knot_dname_release(pkt->question.qname); } @@ -674,37 +663,55 @@ static int knot_packet_parse_rr_sections(knot_packet_t *packet, int err; - dbg_packet("Parsing Answer RRs...\n"); + assert(packet->tsig_rr == NULL); + + dbg_packet_verb("Parsing Answer RRs...\n"); if ((err = knot_packet_parse_rrs(packet->wireformat, pos, - packet->size, packet->header.ancount, &packet->answer, - &packet->an_rrsets, &packet->max_an_rrsets, + packet->size, packet->header.ancount, &packet->parsed_an, + &packet->answer, &packet->an_rrsets, &packet->max_an_rrsets, DEFAULT_RRSET_COUNT(ANCOUNT, packet), packet)) != KNOT_EOK) { return err; } - dbg_packet("Parsing Authority RRs...\n"); + if (packet->tsig_rr != NULL) { + dbg_packet("TSIG in Answer section.\n"); + return KNOT_EMALF; + } + + dbg_packet_verb("Parsing Authority RRs...\n"); if ((err = knot_packet_parse_rrs(packet->wireformat, pos, - packet->size, packet->header.nscount, &packet->authority, - &packet->ns_rrsets, &packet->max_ns_rrsets, + packet->size, packet->header.nscount, &packet->parsed_ns, + &packet->authority, &packet->ns_rrsets, &packet->max_ns_rrsets, DEFAULT_RRSET_COUNT(NSCOUNT, packet), packet)) != KNOT_EOK) { return err; } - dbg_packet("Parsing Additional RRs...\n"); + if (packet->tsig_rr != NULL) { + dbg_packet("TSIG in Authority section.\n"); + return KNOT_EMALF; + } + + dbg_packet_verb("Parsing Additional RRs...\n"); if ((err = knot_packet_parse_rrs(packet->wireformat, pos, - packet->size, packet->header.arcount, &packet->additional, - &packet->ar_rrsets, &packet->max_ar_rrsets, + packet->size, packet->header.arcount, &packet->parsed_ar, + &packet->additional, &packet->ar_rrsets, &packet->max_ar_rrsets, DEFAULT_RRSET_COUNT(ARCOUNT, packet), packet)) != KNOT_EOK) { return err; } - dbg_packet("Trying to find OPT RR in the packet.\n"); + // If TSIG is not the last record + if (packet->tsig_rr != NULL + && packet->ar_rrsets[packet->additional - 1] != packet->tsig_rr) { + dbg_packet("TSIG in Additonal section but not last.\n"); + return KNOT_EMALF; + } + + dbg_packet_verb("Trying to find OPT RR in the packet.\n"); for (int i = 0; i < packet->ar_rrsets; ++i) { assert(packet->additional[i] != NULL); - if (knot_rrset_type(packet->additional[i]) - == KNOT_RRTYPE_OPT) { - dbg_packet("Found OPT RR, filling.\n"); + if (knot_rrset_type(packet->additional[i]) == KNOT_RRTYPE_OPT) { + dbg_packet_detail("Found OPT RR, filling.\n"); err = knot_edns_new_from_rr(&packet->opt_rr, packet->additional[i]); if (err != KNOT_EOK) { @@ -717,9 +724,10 @@ static int knot_packet_parse_rr_sections(knot_packet_t *packet, packet->parsed = *pos; if (*pos < packet->size) { - // some trailing garbage; ignore, but log - dbg_response("Packet: %zu bytes of trailing garbage " - "in packet.\n", packet->size - (*pos)); + // If there is some trailing garbage, treat the packet as + // malformed + dbg_packet_verb("Packet: %zu bytes of trailing garbage " + "in packet.\n", packet->size - (*pos)); return KNOT_EMALF; } @@ -784,12 +792,14 @@ int knot_packet_parse_from_wire(knot_packet_t *packet, packet->size = size; packet->free_wireformat = 0; - //uint8_t *pos = wireformat; + if (size < 2) { + return KNOT_EMALF; + } + size_t pos = 0; - //size_t remaining = size; - dbg_packet("Parsing wire format of packet (size %zu).\nHeader\n", - size); + dbg_packet_verb("Parsing wire format of packet (size %zu).\nHeader\n", + size); if ((err = knot_packet_parse_header(wireformat, &pos, size, &packet->header)) != KNOT_EOK) { return err; @@ -797,15 +807,14 @@ int knot_packet_parse_from_wire(knot_packet_t *packet, packet->parsed = pos; - dbg_packet("Question (prealloc type: %d)...\n", packet->prealloc_type); + dbg_packet_verb("Question (prealloc type: %d)...\n", + packet->prealloc_type); if (packet->header.qdcount > 1) { dbg_packet("QDCOUNT larger than 1, FORMERR.\n"); return KNOT_EMALF; } - knot_packet_dump(packet); - if (packet->header.qdcount == 1) { if ((err = knot_packet_parse_question(wireformat, &pos, size, &packet->question, packet->prealloc_type @@ -816,18 +825,20 @@ int knot_packet_parse_from_wire(knot_packet_t *packet, packet->parsed = pos; } +dbg_packet_exec_detail( knot_packet_dump(packet); +); if (question_only) { return KNOT_EOK; } /*! \todo Replace by call to parse_rest()? */ - err = knot_packet_parse_rr_sections(packet, &pos); + err = knot_packet_parse_rest(packet); -#ifdef KNOT_PACKET_DEBUG +dbg_packet_exec_detail( knot_packet_dump(packet); -#endif /* KNOT_RESPONSE_DEBUG */ +); return err; } @@ -840,16 +851,21 @@ int knot_packet_parse_rest(knot_packet_t *packet) return KNOT_EBADARG; } -// if (packet->parsed >= packet->size) { -// return KNOT_EOK; -// } - - if (packet->parsed == packet->size) { + if (packet->header.ancount == packet->parsed_an + && packet->header.nscount == packet->parsed_ns + && packet->header.arcount == packet->parsed_ar + && packet->parsed == packet->size) { return KNOT_EOK; } + + // If there is less data then required, the next function will find out. + // If there is more data than required, it also returns EMALF. size_t pos = packet->parsed; + /*! \todo If we already parsed some part of the packet, it is not ok + * to begin parsing from the Answer section. + */ return knot_packet_parse_rr_sections(packet, &pos); } @@ -866,16 +882,16 @@ int knot_packet_parse_next_rr_answer(knot_packet_t *packet, if (packet->parsed >= packet->size) { assert(packet->an_rrsets <= packet->header.ancount); - if (packet->an_rrsets != packet->header.ancount) { + if (packet->parsed_an != packet->header.ancount) { dbg_packet("Parsed less RRs than expected.\n"); return KNOT_EMALF; } else { - dbg_packet("Whole packet parsed\n"); + dbg_packet_detail("Whole packet parsed\n"); return KNOT_EOK; } } - if (packet->an_rrsets == packet->header.ancount) { + if (packet->parsed_an == packet->header.ancount) { assert(packet->parsed < packet->size); //dbg_packet("Trailing garbage, ignoring...\n"); // there may be other data in the packet @@ -885,19 +901,20 @@ int knot_packet_parse_next_rr_answer(knot_packet_t *packet, size_t pos = packet->parsed; - dbg_packet("Parsing next Answer RR (pos: %zu)...\n", pos); + dbg_packet_verb("Parsing next Answer RR (pos: %zu)...\n", pos); *rr = knot_packet_parse_rr(packet->wireformat, &pos, packet->size); if (*rr == NULL) { - dbg_packet("Failed to parse RR!\n"); + dbg_packet_verb("Failed to parse RR!\n"); return KNOT_EMALF; } - dbg_packet("Parsed. Pos: %zu.\n", pos); + dbg_packet_detail("Parsed. Pos: %zu.\n", pos); packet->parsed = pos; // increment the number of answer RRSets, though there are no saved // in the packet; it is OK, because packet->answer is NULL ++packet->an_rrsets; + ++packet->parsed_an; return KNOT_EOK; } @@ -916,37 +933,37 @@ int knot_packet_parse_next_rr_additional(knot_packet_t *packet, if (packet->parsed >= packet->size) { assert(packet->ar_rrsets <= packet->header.arcount); - if (packet->ar_rrsets != packet->header.arcount) { + if (packet->parsed_ar != packet->header.arcount) { dbg_packet("Parsed less RRs than expected.\n"); return KNOT_EMALF; } else { - dbg_packet("Whole packet parsed\n"); + dbg_packet_detail("Whole packet parsed\n"); return KNOT_EOK; } } - if (packet->ar_rrsets == packet->header.arcount) { + if (packet->parsed_ar == packet->header.arcount) { assert(packet->parsed < packet->size); - dbg_packet("Trailing garbage, ignoring...\n"); - /*! \todo Do not ignore. */ - return KNOT_EOK; + dbg_packet_verb("Trailing garbage, treating as malformed...\n"); + return KNOT_EMALF; } size_t pos = packet->parsed; - dbg_packet("Parsing next Additional RR (pos: %zu)...\n", pos); + dbg_packet_verb("Parsing next Additional RR (pos: %zu)...\n", pos); *rr = knot_packet_parse_rr(packet->wireformat, &pos, packet->size); if (*rr == NULL) { - dbg_packet("Failed to parse RR!\n"); + dbg_packet_verb("Failed to parse RR!\n"); return KNOT_EMALF; } - dbg_packet("Parsed. Pos: %zu.\n", pos); + dbg_packet_detail("Parsed. Pos: %zu.\n", pos); packet->parsed = pos; // increment the number of answer RRSets, though there are no saved // in the packet; it is OK, because packet->answer is NULL ++packet->ar_rrsets; + ++packet->parsed_ar; return KNOT_EOK; } @@ -1186,7 +1203,7 @@ const knot_rrset_t *knot_packet_tsig(const knot_packet_t *packet) void knot_packet_set_tsig(knot_packet_t *packet, const knot_rrset_t *tsig_rr) { - packet->tsig_rr = (knot_rrset_t *)tsig_rr; + packet->tsig_rr = (knot_rrset_t *)tsig_rr; } /*----------------------------------------------------------------------------*/ @@ -1268,19 +1285,19 @@ int knot_packet_contains(const knot_packet_t *packet, return KNOT_EBADARG; } - for (int i = 0; i < packet->header.ancount; ++i) { + for (int i = 0; i < packet->an_rrsets; ++i) { if (knot_rrset_compare(packet->answer[i], rrset, cmp)) { return 1; } } - for (int i = 0; i < packet->header.nscount; ++i) { + for (int i = 0; i < packet->ns_rrsets; ++i) { if (knot_rrset_compare(packet->authority[i], rrset, cmp)) { return 1; } } - for (int i = 0; i < packet->header.arcount; ++i) { + for (int i = 0; i < packet->ar_rrsets; ++i) { if (knot_rrset_compare(packet->additional[i], rrset, cmp)) { return 1; } @@ -1307,8 +1324,8 @@ int knot_packet_add_tmp_rrset(knot_packet_t *packet, } packet->tmp_rrsets[packet->tmp_rrsets_count++] = tmp_rrset; - dbg_packet("Current tmp RRSets count: %d, max count: %d\n", - packet->tmp_rrsets_count, packet->tmp_rrsets_max); + dbg_packet_detail("Current tmp RRSets count: %d, max count: %d\n", + packet->tmp_rrsets_count, packet->tmp_rrsets_max); return KNOT_EOK; } @@ -1329,7 +1346,7 @@ void knot_packet_free_tmp_rrsets(knot_packet_t *pkt) dbg_packet_exec( char *name = knot_dname_to_str( (((knot_rrset_t **)(pkt->tmp_rrsets))[i])->owner); - dbg_packet("Freeing tmp RRSet on ptr: %p (ptr to ptr:" + dbg_packet_verb("Freeing tmp RRSet on ptr: %p (ptr to ptr:" " %p, type: %s, owner: %s)\n", (((knot_rrset_t **)(pkt->tmp_rrsets))[i]), &(((knot_rrset_t **)(pkt->tmp_rrsets))[i]), @@ -1394,14 +1411,6 @@ int knot_packet_question_to_wire(knot_packet_t *packet) pos += 2; knot_wire_write_u16(pos, packet->question.qclass); -// int err = 0; - // TODO: put the qname into the compression table -// // TODO: get rid of the numeric constants -// if ((err = knot_response_store_dname_pos(&packet->compression, -// packet->question.qname,0, 12, 12)) != KNOT_EOK) { -// return err; -// } - packet->size += knot_dname_size(packet->question.qname) + 4; return KNOT_EOK; @@ -1486,8 +1495,6 @@ void knot_packet_free(knot_packet_t **packet) knot_packet_free_allocated_space(*packet); // free the space for wireformat -// assert((*packet)->wireformat != NULL); -// free((*packet)->wireformat); if ((*packet)->wireformat != NULL && (*packet)->free_wireformat) { free((*packet)->wireformat); } diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h index 9e37c12..d76209a 100644..100755 --- a/src/libknot/packet/packet.h +++ b/src/libknot/packet/packet.h @@ -138,6 +138,9 @@ struct knot_packet { short free_wireformat; size_t parsed; + uint16_t parsed_an; + uint16_t parsed_ns; + uint16_t parsed_ar; size_t size; /*!< Current wire size of the packet. */ size_t max_size; /*!< Maximum allowed size of the packet. */ diff --git a/src/libknot/packet/query.c b/src/libknot/packet/query.c index b76059b..bc3a4db 100644..100755 --- a/src/libknot/packet/query.c +++ b/src/libknot/packet/query.c @@ -209,7 +209,7 @@ int knot_query_add_rrset_authority(knot_packet_t *query, // reserve space for OPT RR /*! \todo Why here??? */ endp -= query->opt_rr.size; - /*! \note [TSIG] reserve space for TSIG RR */ + /* Reserve space for TSIG RR */ endp -= query->tsig_size; uint8_t *pos = startp; diff --git a/src/libknot/packet/query.h b/src/libknot/packet/query.h index a979641..cda72b9 100644..100755 --- a/src/libknot/packet/query.h +++ b/src/libknot/packet/query.h @@ -61,12 +61,6 @@ int knot_query_nsid_requested(const knot_packet_t *query); int knot_query_edns_supported(const knot_packet_t *query); -//int knot_query_set_qname(knot_packet_t *query, const knot_dname_t *qname); - -//int knot_query_set_qtype(knot_packet_t *query, uint16_t qtype); - -//int knot_query_set_qclass(knot_packet_t *query, uint16_t qclass); - int knot_query_init(knot_packet_t *query); int knot_query_set_question(knot_packet_t *query, diff --git a/src/libknot/packet/response.c b/src/libknot/packet/response.c index 9f6277c..bb4d0f2 100644..100755 --- a/src/libknot/packet/response.c +++ b/src/libknot/packet/response.c @@ -164,15 +164,14 @@ static int knot_response_store_dname_pos(knot_compressed_dnames_t *table, { dbg_response_exec( char *name = knot_dname_to_str(dname); - dbg_response("Putting dname %s into compression table." - " Labels not matched: %d, position: %zu," - ", pointer: %p, unmatched off: %zu\n", name, - not_matched, pos, dname, unmatched_offset); + dbg_response_detail("Putting dname %s into compression table." + " Labels not matched: %d, position: %zu," + ", pointer: %p, unmatched off: %zu\n", name, + not_matched, pos, dname, unmatched_offset); free(name); ); if (pos > KNOT_RESPONSE_MAX_PTR) { - dbg_response("Pointer larger than it can be, not" - " saving\n"); + dbg_response("Pointer larger than it can be, not saving\n"); return KNOT_EDNAMEPTR; } @@ -181,11 +180,6 @@ dbg_response_exec( return KNOT_ENOMEM; } - // store the position of the name -// table->dnames[table->count] = dname; -// table->offsets[table->count] = pos; -// ++table->count; - /* * Store positions of ancestors if more than 1 label was not matched. * @@ -210,11 +204,11 @@ dbg_response_exec( parent_pos = unmatched_offset; } -dbg_response_exec( +dbg_response_exec_detail( char *name = knot_dname_to_str(to_save); - dbg_response("Putting dname %s into compression table." - " Position: %zu, pointer: %p\n", - name, parent_pos, to_save); + dbg_response_detail("Putting dname %s into compression table." + " Position: %zu, pointer: %p\n", + name, parent_pos, to_save); free(name); ); @@ -224,7 +218,6 @@ dbg_response_exec( return KNOT_ENOMEM; } -// dbg_response("Saving..\n"); knot_response_compr_save(table, to_save, parent_pos); /*! \todo Remove '!compr_cs'. */ @@ -234,16 +227,23 @@ dbg_response_exec( // If case-sensitive search is in place, we should not save the // node's parent's positions. - to_save = !compr_cs && (knot_dname_node(to_save) != NULL - && knot_node_parent(knot_dname_node(to_save)) - != NULL) ? knot_node_owner(knot_node_parent( + // Added check to rule out wildcard-covered dnames + // (in such case the offset is not right) + + /*! \todo The whole compression requires a serious refactoring. + * Or better - a rewrite! + */ + to_save = (!compr_cs && knot_dname_node(to_save) != NULL + && knot_node_owner(knot_dname_node(to_save)) + != to_save + && knot_node_parent(knot_dname_node(to_save)) + != NULL) + ? knot_node_owner(knot_node_parent( knot_dname_node(to_save))) - : NULL; + : NULL; dbg_response("i: %d\n", i); parent_pos += knot_dname_label_size(dname, i) + 1; -// parent_pos += (i > 0) -// ? knot_dname_label_size(dname, i - 1) + 1 : 0; ++i; } @@ -267,22 +267,12 @@ static size_t knot_response_find_dname_pos( const knot_dname_t *dname, int compr_cs) { for (int i = 0; i < table->count; ++i) { -// dbg_response("Comparing dnames %p and %p\n", -// dname, table->dnames[i]); -//dbg_response_exec( -// char *name = knot_dname_to_str(dname); -// dbg_response("(%s and ", name); -// name = knot_dname_to_str(table->dnames[i]); -// dbg_response("%s)\n", name); -// free(name); -//); - //if (table->dnames[i] == dname) { int ret = (compr_cs) ? knot_dname_compare_cs(table->dnames[i], dname) : knot_dname_compare(table->dnames[i], dname); if (ret == 0) { - dbg_response("Found offset: %zu\n", - table->offsets[i]); + dbg_response_detail("Found offset: %zu\n", + table->offsets[i]); return table->offsets[i]; } } @@ -319,7 +309,7 @@ static int knot_response_put_dname_ptr(const knot_dname_t *dname, memcpy(wire, knot_dname_name(dname), size); knot_wire_put_pointer(wire + size, offset); - dbg_response("Size of the dname with ptr: %d\n", size + 2); + dbg_response_detail("Size of the dname with ptr: %d\n", size + 2); return size + 2; } @@ -343,15 +333,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; - /*! - * \todo Compress!! - * - * if pos == 0, do not store the position! - */ // try to find the name or one of its ancestors in the compr. table #ifdef COMPRESSION_PEDANTIC - //knot_dname_t *to_find = knot_dname_copy(dname); knot_dname_t *to_find = (knot_dname_t *)dname; int copied = 0; #else @@ -361,11 +345,11 @@ static int knot_response_compress_dname(const knot_dname_t *dname, int not_matched = 0; while (to_find != NULL && knot_dname_label_count(to_find) != 0) { -dbg_response_exec( +dbg_response_exec_detail( char *name = knot_dname_to_str(to_find); - dbg_response("Searching for name %s in the compression" - " table, not matched labels: %d\n", name, - not_matched); + dbg_response_detail("Searching for name %s in the compression" + " table, not matched labels: %d\n", name, + not_matched); free(name); ); offset = knot_response_find_dname_pos(compr->table, to_find, @@ -396,17 +380,17 @@ dbg_response_exec( || knot_node_owner(knot_dname_node(to_find)) != to_find || knot_node_parent(knot_dname_node(to_find)) == NULL) { - dbg_response("compr_cs: %d\n", compr_cs); - dbg_response("knot_dname_node(to_find, 1) == %p" + dbg_response_detail("compr_cs: %d\n", compr_cs); + dbg_response_detail("knot_dname_node(to_find, 1) == %p" "\n", knot_dname_node(to_find)); if (knot_dname_node(to_find) != NULL) { - dbg_response("knot_node_owner(knot_dname_node(" - "to_find, 1)) = %p, to_find = %p\n", - knot_node_owner(knot_dname_node(to_find)), - to_find); - dbg_response("knot_node_parent(knot_dname_node(" - "to_find, 1), 1) = %p\n", + dbg_response_detail("knot_node_owner(knot_dname_node(" + "to_find, 1)) = %p, to_find = %p\n", + knot_node_owner(knot_dname_node(to_find)), + to_find); + dbg_response_detail("knot_node_parent(knot_dname_node(" + "to_find, 1), 1) = %p\n", knot_node_parent(knot_dname_node(to_find))); } break; @@ -417,7 +401,7 @@ dbg_response_exec( knot_node_parent(knot_dname_node(to_find)))); to_find = knot_node_owner( knot_node_parent(knot_dname_node(to_find))); - dbg_response("New to_find: %p\n", to_find); + dbg_response_detail("New to_find: %p\n", to_find); } #endif } @@ -428,10 +412,10 @@ dbg_response_exec( } #endif - dbg_response("Max size available for domain name: %zu\n", max); + dbg_response_detail("Max size available for domain name: %zu\n", max); if (offset > 0) { // found such dname somewhere in the packet - dbg_response("Found name in the compression table.\n"); + dbg_response_detail("Found name in the compression table.\n"); assert(offset >= KNOT_WIRE_HEADER_SIZE); size = knot_response_put_dname_ptr(dname, not_matched, offset, dname_wire, max); @@ -439,7 +423,7 @@ dbg_response_exec( return KNOT_ESPACE; } } else { - dbg_response("Not found, putting whole name.\n"); + dbg_response_detail("Not found, putting whole name.\n"); // now just copy the dname without compressing if (dname->size > max) { return KNOT_ESPACE; @@ -458,8 +442,7 @@ dbg_response_exec( if (knot_response_store_dname_pos(compr->table, dname, not_matched, compr->wire_pos, offset, compr_cs) != 0) { - dbg_response("Compression info could not be stored." - "\n"); + dbg_response_detail("Compression info could not be stored.\n"); } return size; @@ -488,7 +471,7 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset, { int size = 0; - dbg_response("Max size: %zu, owner pos: %zu, owner size: %d\n", + dbg_response_detail("Max size: %zu, owner pos: %zu, owner size: %d\n", max_size, compr->owner.pos, compr->owner.size); if (size + ((compr->owner.pos == 0 @@ -498,7 +481,7 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset, return KNOT_ESPACE; } - dbg_response("Owner position: %zu\n", compr->owner.pos); + dbg_response_detail("Owner position: %zu\n", compr->owner.pos); // put owner if needed (already compressed) if (compr->owner.pos == 0 || compr->owner.pos > KNOT_RESPONSE_MAX_PTR) { @@ -507,28 +490,28 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset, *rrset_wire += compr->owner.size; size += compr->owner.size; } else { - dbg_response("Putting pointer: %zu\n", - compr->owner.pos); + dbg_response_detail("Putting pointer: %zu\n", + compr->owner.pos); knot_wire_put_pointer(*rrset_wire, compr->owner.pos); *rrset_wire += 2; size += 2; } - dbg_response("Max size: %zu, size: %d\n", max_size, size); + dbg_response_detail("Max size: %zu, size: %d\n", max_size, size); - dbg_response("Wire format:\n"); + dbg_response_detail("Wire format:\n"); // put rest of RR 'header' knot_wire_write_u16(*rrset_wire, rrset->type); - dbg_response(" Type: %u\n", rrset->type); + dbg_response_detail(" Type: %u\n", rrset->type); *rrset_wire += 2; knot_wire_write_u16(*rrset_wire, rrset->rclass); - dbg_response(" Class: %u\n", rrset->rclass); + dbg_response_detail(" Class: %u\n", rrset->rclass); *rrset_wire += 2; knot_wire_write_u32(*rrset_wire, rrset->ttl); - dbg_response(" TTL: %u\n", rrset->ttl); + dbg_response_detail(" TTL: %u\n", rrset->ttl); *rrset_wire += 4; // save space for RDLENGTH @@ -538,7 +521,7 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset, size += 10; compr->wire_pos += size; - dbg_response("Max size: %zu, size: %d\n", max_size, size); + dbg_response_detail("Max size: %zu, size: %d\n", max_size, size); knot_rrtype_descriptor_t *desc = knot_rrtype_descriptor_by_type(rrset->type); @@ -561,8 +544,7 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset, return KNOT_ESPACE; } - dbg_response("Compressed dname size: %d\n", - ret); + dbg_response_detail("Compressed dname size: %d\n", ret); *rrset_wire += ret; rdlength += ret; compr->wire_pos += ret; @@ -579,8 +561,8 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset, // save whole domain name memcpy(*rrset_wire, dname->name, dname->size); - dbg_response("Uncompressed dname size: %d\n", - dname->size); + dbg_response_detail("Uncompressed dname size: %d\n", + dname->size); *rrset_wire += dname->size; rdlength += dname->size; compr->wire_pos += dname->size; @@ -596,8 +578,8 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset, // copy just the rdata item data (without size) memcpy(*rrset_wire, raw_data + 1, raw_data[0]); - dbg_response("Raw data size: %d\n", - raw_data[0]); + dbg_response_detail("Raw data size: %d\n", + raw_data[0]); *rrset_wire += raw_data[0]; rdlength += raw_data[0]; compr->wire_pos += raw_data[0]; @@ -606,7 +588,7 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset, } } - dbg_response("Max size: %zu, size: %d\n", max_size, size); + dbg_response_detail("Max size: %zu, size: %d\n", max_size, size); assert(size + rdlength <= max_size); size += rdlength; @@ -639,12 +621,12 @@ static int knot_response_rrset_to_wire(const knot_rrset_t *rrset, knot_compressed_dnames_t *compr, int compr_cs) { -dbg_response_exec( +dbg_response_exec_verb( char *name = knot_dname_to_str(rrset->owner); - dbg_response("Converting RRSet with owner %s, type %s\n", - name, knot_rrtype_to_string(rrset->type)); + dbg_response_verb("Converting RRSet with owner %s, type %s\n", + name, knot_rrtype_to_string(rrset->type)); free(name); - dbg_response(" Size before: %zu\n", *size); + dbg_response_verb(" Size before: %zu\n", *size); ); // if no RDATA in RRSet, return @@ -652,10 +634,6 @@ dbg_response_exec( return KNOT_EOK; } - //uint8_t *rrset_wire = (uint8_t *)malloc(PREALLOC_RRSET_WIRE); - //short rrset_size = 0; - - //uint8_t *owner_wire = (uint8_t *)malloc(rrset->owner->size); /* * We may pass the current position to the compression function * because if the owner will be put somewhere, it will be on the @@ -665,7 +643,6 @@ dbg_response_exec( */ knot_compr_t compr_info; - //compr_info.new_entries = 0; compr_info.table = compr; compr_info.wire_pos = wire_pos; compr_info.owner.pos = 0; @@ -674,8 +651,8 @@ dbg_response_exec( knot_response_compress_dname(rrset->owner, &compr_info, owner_tmp, max_size, compr_cs); - dbg_response(" Owner size: %d, position: %zu\n", - compr_info.owner.size, compr_info.owner.pos); + dbg_response_detail(" Owner size: %d, position: %zu\n", + compr_info.owner.size, compr_info.owner.pos); if (compr_info.owner.size < 0) { return KNOT_ESPACE; } @@ -698,20 +675,17 @@ dbg_response_exec( return KNOT_ESPACE; } - dbg_response("RR of size %d added.\n", ret); + dbg_response_verb("RR of size %d added.\n", ret); rrset_size += ret; ++rrs; } while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL); - //memcpy(*pos, rrset_wire, rrset_size); - //*size += rrset_size; - //*pos += rrset_size; // the whole RRSet did fit in assert (rrset_size <= max_size); *size += rrset_size; - dbg_response(" Size after: %zu\n", *size); + dbg_response_verb(" Size after: %zu\n", *size); return rrs; } @@ -750,8 +724,8 @@ static int knot_response_try_add_rrset(const knot_rrset_t **rrsets, dbg_response_exec( char *name = knot_dname_to_str(rrset->owner); - dbg_response("\nAdding RRSet with owner %s and type %s: \n", - name, knot_rrtype_to_string(rrset->type)); + dbg_response_verb("\nAdding RRSet with owner %s and type %s: \n", + name, knot_rrtype_to_string(rrset->type)); free(name); ); @@ -764,11 +738,11 @@ dbg_response_exec( if (rrs >= 0) { rrsets[(*rrset_count)++] = rrset; resp->size += size; - dbg_response("RRset added, size: %zu, RRs: %d, total " - "size of response: %zu\n\n", size, rrs, - resp->size); + dbg_response_verb("RRset added, size: %zu, RRs: %d, total " + "size of response: %zu\n\n", size, rrs, + resp->size); } else if (tc) { - dbg_response("Setting TC bit.\n"); + dbg_response_verb("Setting TC bit.\n"); knot_wire_flags_set_tc(&resp->header.flags1); knot_wire_set_tc(resp->wireformat); } @@ -825,8 +799,8 @@ static int knot_response_realloc_wc_nodes(const knot_node_t ***nodes, short *max_count, short default_max_count, short step) { - dbg_packet("Max count: %d, default max count: %d\n", - *max_count, default_max_count); + dbg_packet_detail("Max count: %d, default max count: %d\n", + *max_count, default_max_count); int free_old = (*max_count) != default_max_count; const knot_node_t **old_nodes = *nodes; @@ -896,13 +870,10 @@ int knot_response_init_from_query(knot_packet_t *response, // copy the header from the query memcpy(&response->header, &query->header, sizeof(knot_header_t)); -// memmove(&response->header, &query->header, sizeof(knot_header_t)); // copy the Question section (but do not copy the QNAME) memcpy(&response->question, &query->question, sizeof(knot_question_t)); -// memmove(&response->question, &query->question, -// sizeof(knot_question_t)); int err = 0; // put the qname into the compression table @@ -917,18 +888,16 @@ int knot_response_init_from_query(knot_packet_t *response, size_t to_copy = 12 + 4 + knot_dname_size(response->question.qname); assert(response->max_size >= to_copy); -// printf("Resp init from query: Copying from: %p to: %p size: %d\n", -// response->wireformat, query->wireformat, -// to_copy); -// printf("Resp init from query: Question name size: %d Query name size: %d\n", -// knot_dname_size(response->question.qname), -// knot_dname_size(query->question.qname)); memcpy(response->wireformat, query->wireformat, to_copy); response->size = to_copy; // set the qr bit to 1 knot_wire_flags_set_qr(&response->header.flags1); knot_wire_set_qr(response->wireformat); + + // clear TC flag + knot_wire_flags_clear_tc(&response->header.flags1); + knot_wire_clear_tc(response->wireformat); // clear AD flag knot_wire_flags_clear_ad(&response->header.flags2); @@ -940,8 +909,11 @@ int knot_response_init_from_query(knot_packet_t *response, // set counts to 0 response->header.ancount = 0; + knot_wire_set_ancount(response->wireformat, 0); response->header.nscount = 0; + knot_wire_set_nscount(response->wireformat, 0); response->header.arcount = 0; + knot_wire_set_arcount(response->wireformat, 0); response->query = query; @@ -1039,16 +1011,14 @@ int knot_response_add_opt(knot_packet_t *resp, if (override_max_size && resp->max_size > 0 && resp->max_size < opt_rr->payload) { -// return KNOT_EPAYLOAD; return KNOT_EOK; } // set max size (less is OK) if (override_max_size) { dbg_response("Overriding max size to: %u\n", - resp->opt_rr.payload); + resp->opt_rr.payload); return knot_packet_set_max_size(resp, resp->opt_rr.payload); - //resp->max_size = resp->opt_rr.payload; } return KNOT_EOK; @@ -1065,7 +1035,7 @@ int knot_response_add_rrset_answer(knot_packet_t *response, return KNOT_EBADARG; } - dbg_response("add_rrset_answer()\n"); + dbg_response_verb("add_rrset_answer()\n"); assert(response->header.arcount == 0); assert(response->header.nscount == 0); @@ -1081,9 +1051,9 @@ int knot_response_add_rrset_answer(knot_packet_t *response, return KNOT_EOK; } - dbg_response("Trying to add RRSet to Answer section.\n"); - dbg_response("RRset: %p\n", rrset); - dbg_response("Owner: %p\n", rrset->owner); + dbg_response_verb("Trying to add RRSet to Answer section.\n"); + dbg_response_detail("RRset: %p\n", rrset); + dbg_response_detail("Owner: %p\n", rrset->owner); int rrs = knot_response_try_add_rrset(response->answer, &response->an_rrsets, response, @@ -1132,7 +1102,7 @@ int knot_response_add_rrset_authority(knot_packet_t *response, return KNOT_EOK; } - dbg_response("Trying to add RRSet to Authority section.\n"); + dbg_response_verb("Trying to add RRSet to Authority section.\n"); int rrs = knot_response_try_add_rrset(response->authority, &response->ns_rrsets, response, @@ -1188,7 +1158,7 @@ int knot_response_add_rrset_additional(knot_packet_t *response, return KNOT_EOK; } - dbg_response("Trying to add RRSet to Additional section.\n"); + dbg_response_verb("Trying to add RRSet to Additional section.\n"); int rrs = knot_response_try_add_rrset(response->additional, &response->ar_rrsets, response, @@ -1283,7 +1253,7 @@ int knot_response_add_wildcard_node(knot_packet_t *response, response->wildcard_nodes.snames[response->wildcard_nodes.count] = sname; ++response->wildcard_nodes.count; - dbg_response("Current wildcard nodes count: %d, max count: %d\n", + dbg_response_verb("Current wildcard nodes count: %d, max count: %d\n", response->wildcard_nodes.count, response->wildcard_nodes.max); diff --git a/src/libknot/packet/response.h b/src/libknot/packet/response.h index d3e66f3..d3e66f3 100644..100755 --- a/src/libknot/packet/response.h +++ b/src/libknot/packet/response.h diff --git a/src/libknot/rdata.c b/src/libknot/rdata.c index 8e9e8c1..1cdd339 100644..100755 --- a/src/libknot/rdata.c +++ b/src/libknot/rdata.c @@ -210,8 +210,8 @@ knot_rdata_t *knot_rdata_new() /*----------------------------------------------------------------------------*/ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire, - size_t *pos, size_t total_size, size_t rdlength, - const knot_rrtype_descriptor_t *desc) + size_t *pos, size_t total_size, size_t rdlength, + const knot_rrtype_descriptor_t *desc) { int i = 0; uint8_t item_type; @@ -249,7 +249,6 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire, return KNOT_ERROR; } items[i].dname = dname; - //*pos += dname->size; parsed += pos2 - *pos; *pos = pos2; dname = 0; @@ -309,7 +308,6 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire, break; case 3: pos2 = *pos; - //fprintf(stderr, "reading dname from pos: %zu\n", pos2); dname = knot_dname_parse_from_wire( wire, &pos2, total_size, NULL); if (dname == NULL) { @@ -331,12 +329,9 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire, memcpy((uint8_t *)(items[i].raw_data + 1), knot_dname_name(dname), knot_dname_size(dname)); - -// items[i].dname = dname; - //*pos += dname->size; + parsed += pos2 - *pos; - - //fprintf(stderr, "read %zu bytes.\n", parsed); + *pos = pos2; knot_dname_free(&dname); @@ -374,9 +369,7 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire, memcpy(items[i].raw_data + 1, wire + *pos, item_size); *pos += item_size; parsed += item_size; - } else if (item_type == KNOT_RDATA_WF_BINARY/* - || item_type == KNOT_RDATA_WF_IPSECGATEWAY*/) { -// fprintf(stderr, "item_size was 0, creating empty rdata item.\n"); + } else if (item_type == KNOT_RDATA_WF_BINARY) { // in this case we are at the end of the RDATA // and should create an empty RDATA item items[i].raw_data = (uint16_t *)malloc(2); @@ -389,8 +382,6 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire, } else if (item_type != KNOT_RDATA_WF_COMPRESSED_DNAME && item_type != KNOT_RDATA_WF_UNCOMPRESSED_DNAME && item_type != KNOT_RDATA_WF_LITERAL_DNAME) { -// fprintf(stderr, "RDATA item not set (i: %d), type: %u" -// " RDATA item type: %d\n", i, desc->type ,item_type); assert(0); } @@ -519,6 +510,25 @@ int knot_rdata_item_set_raw_data(knot_rdata_t *rdata, uint pos, /*----------------------------------------------------------------------------*/ +int knot_rdata_count(const knot_rdata_t *rdata) +{ + if (rdata == NULL) { + return 0; + } + + int count = 1; + const knot_rdata_t *r = rdata; + + while (r->next != NULL && r->next != rdata) { + r = r->next; + ++count; + } + + return count; +} + +/*----------------------------------------------------------------------------*/ + void knot_rdata_free(knot_rdata_t **rdata) { if (rdata == NULL || *rdata == NULL) { @@ -541,134 +551,16 @@ void knot_rdata_deep_free(knot_rdata_t **rdata, uint type, return; } - knot_rdata_free_items((*rdata)->items, (*rdata)->count, type, - free_all_dnames); + if ((*rdata)->items != NULL) { + knot_rdata_free_items((*rdata)->items, (*rdata)->count, type, + free_all_dnames); + } free(*rdata); *rdata = NULL; } /*----------------------------------------------------------------------------*/ -/* CLEANUP */ -//uint knot_rdata_wire_size(const knot_rdata_t *rdata, -// const uint8_t *format) -//{ -// uint size = 0; - -// for (int i = 0; i < rdata->count; ++i) { -// switch (format[i]) { -// case KNOT_RDATA_WF_COMPRESSED_DNAME: -// case KNOT_RDATA_WF_UNCOMPRESSED_DNAME: -// case KNOT_RDATA_WF_LITERAL_DNAME: -// size += knot_dname_size(rdata->items[i].dname); -// break; -// case KNOT_RDATA_WF_BYTE: -// size += 1; -// break; -// case KNOT_RDATA_WF_SHORT: -// size += 2; -// break; -// case KNOT_RDATA_WF_LONG: -// size += 4; -// break; -// case KNOT_RDATA_WF_A: -// size += 4; -// break; -// case KNOT_RDATA_WF_AAAA: -// size += 16; -// break; -// case KNOT_RDATA_WF_BINARY: -// case KNOT_RDATA_WF_APL: // saved as binary -// case KNOT_RDATA_WF_IPSECGATEWAY: // saved as binary -// size += rdata->items[i].raw_data[0]; -// break; -// case KNOT_RDATA_WF_TEXT: -// case KNOT_RDATA_WF_BINARYWITHLENGTH: -// size += rdata->items[i].raw_data[0] + 1; -// break; -// default: -// assert(0); -// } -// } -// return size; -//} - -/*----------------------------------------------------------------------------*/ - -//int knot_rdata_to_wire(const knot_rdata_t *rdata, const uint8_t *format, -// uint8_t *buffer, uint buf_size) -//{ -// uint copied = 0; -// uint8_t tmp[KNOT_MAX_RDATA_WIRE_SIZE]; -// uint8_t *to = tmp; - -// for (int i = 0; i < rdata->count; ++i) { -// assert(copied < KNOT_MAX_RDATA_WIRE_SIZE); - -// const uint8_t *from = (uint8_t *)rdata->items[i].raw_data; -// uint size = 0; - -// switch (format[i]) { -// case KNOT_RDATA_WF_COMPRESSED_DNAME: -// case KNOT_RDATA_WF_UNCOMPRESSED_DNAME: -// case KNOT_RDATA_WF_LITERAL_DNAME: -// size = knot_dname_size(rdata->items[i].dname); -// from = knot_dname_name(rdata->items[i].dname); - -// break; -// case KNOT_RDATA_WF_BYTE: -// size = 1; -// break; -// case KNOT_RDATA_WF_SHORT: -// size = 2; -// break; -// case KNOT_RDATA_WF_LONG: -// size = 4; -// break; -// case KNOT_RDATA_WF_A: -// size = 4; -// break; -// case KNOT_RDATA_WF_AAAA: -// size = 16; -// break; -// case KNOT_RDATA_WF_TEXT: -// case KNOT_RDATA_WF_BINARYWITHLENGTH: -// // size stored in the first two bytes, but in little -// // endian and we need only the lower byte from it -// *to = *from; // lower byte is the first in little endian -// to += 1; -// case KNOT_RDATA_WF_BINARY: -// case KNOT_RDATA_WF_APL: // saved as binary -// case KNOT_RDATA_WF_IPSECGATEWAY: // saved as binary -// // size stored in the first two bytes, those bytes -// // must not be copied -// size = rdata->items[i].raw_data[0]; -// from += 2; // skip the first two bytes -// break; -// default: -// assert(0); -// } - -// assert(size != 0); -// assert(copied + size < KNOT_MAX_RDATA_WIRE_SIZE); - -// memcpy(to, from, size); -// to += size; -// copied += size; -// } - -// if (copied > buf_size) { -// dbg_rdata("Not enough place allocated for function " -// "knot_rdata_to_wire(). Allocated %u, need %u\n", -// buf_size, copied); -// return -1; -// } - -// memcpy(buffer, tmp, copied); -// return 0; -//} - -/*----------------------------------------------------------------------------*/ knot_rdata_t *knot_rdata_deep_copy(const knot_rdata_t *rdata, uint16_t type, int copy_dnames) @@ -728,33 +620,19 @@ int knot_rdata_compare(const knot_rdata_t *r1, const knot_rdata_t *r2, int cmp = 0; for (int i = 0; i < count; ++i) { - /* CLEANUP */ -// const uint8_t *data1, *data2; -// int size1, size2; - if (format[i] == KNOT_RDATA_WF_COMPRESSED_DNAME || format[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME || format[i] == KNOT_RDATA_WF_LITERAL_DNAME) { cmp = knot_dname_compare(r1->items[i].dname, r2->items[i].dname); -// data1 = knot_dname_name(r1->items[i].dname); -// data2 = knot_dname_name(r2->items[i].dname); -// size1 = knot_dname_size(r2->items[i].dname); -// size2 = knot_dname_size(r2->items[i].dname); } else { cmp = knot_rdata_compare_binary( (uint8_t *)(r1->items[i].raw_data + 1), (uint8_t *)(r2->items[i].raw_data + 1), r1->items[i].raw_data[0], r1->items[i].raw_data[0]); -// data1 = (uint8_t *)(r1->items[i].raw_data + 1); -// data2 = (uint8_t *)(r2->items[i].raw_data + 1); -// size1 = r1->items[i].raw_data[0]; -// size2 = r1->items[i].raw_data[0]; } -// cmp = - if (cmp != 0) { return cmp; } @@ -894,3 +772,48 @@ uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata) return knot_wire_read_u16((uint8_t *)(rdata->items[0].raw_data + 1)); } + +/*---------------------------------------------------------------------------*/ + +uint8_t knot_rdata_nsec3_algorithm(const knot_rdata_t *rdata) +{ + if (rdata->count < 1) { + return 0; + } + + return *((uint8_t *)(rdata->items[0].raw_data + 1)); +} + +/*---------------------------------------------------------------------------*/ + +uint16_t knot_rdata_nsec3_iterations(const knot_rdata_t *rdata) +{ + if (rdata->count < 3) { + // this is actually valid value...what to return?? + return 0; + } + + return knot_wire_read_u16((uint8_t *)(rdata->items[2].raw_data + 1)); +} + +/*---------------------------------------------------------------------------*/ + +uint8_t knot_rdata_nsec3_salt_length(const knot_rdata_t *rdata) +{ + if (rdata->count < 4) { + return 0; + } + + return *((uint8_t *)(rdata->items[3].raw_data + 1)); +} + +/*---------------------------------------------------------------------------*/ + +const uint8_t *knot_rdata_nsec3_salt(const knot_rdata_t *rdata) +{ + if (rdata->count < 4) { + return NULL; + } + + return ((uint8_t *)(rdata->items[3].raw_data + 1)) + 1; +} diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h index bb45f50..acd678f 100644..100755 --- a/src/libknot/rdata.h +++ b/src/libknot/rdata.h @@ -220,6 +220,8 @@ int knot_rdata_item_set_dname(knot_rdata_t *rdata, unsigned int pos, int knot_rdata_item_set_raw_data(knot_rdata_t *rdata, unsigned int pos, uint16_t *raw_data); +int knot_rdata_count(const knot_rdata_t *rdata); + /*! * \brief Copies the given RDATA. * @@ -335,6 +337,11 @@ uint32_t knot_rdata_soa_minimum(const knot_rdata_t *rdata); uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata); +uint8_t knot_rdata_nsec3_algorithm(const knot_rdata_t *rdata); +uint16_t knot_rdata_nsec3_iterations(const knot_rdata_t *rdata); +uint8_t knot_rdata_nsec3_salt_length(const knot_rdata_t *rdata); +const uint8_t *knot_rdata_nsec3_salt(const knot_rdata_t *rdata); + #endif /* _KNOT_RDATA_H */ /*! @} */ diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c index d665c63..ef7fce8 100644..100755 --- a/src/libknot/rrset.c +++ b/src/libknot/rrset.c @@ -32,7 +32,7 @@ /*----------------------------------------------------------------------------*/ static void knot_rrset_disconnect_rdata(knot_rrset_t *rrset, - knot_rdata_t *prev, knot_rdata_t *rdata) + knot_rdata_t *prev, knot_rdata_t *rdata) { if (prev == NULL) { // find the previous RDATA in the series, as its pointer must @@ -113,6 +113,53 @@ int knot_rrset_add_rdata(knot_rrset_t *rrset, knot_rdata_t *rdata) /*----------------------------------------------------------------------------*/ +int knot_rrset_add_rdata_order(knot_rrset_t *rrset, knot_rdata_t *rdata) +{ + if (rrset == NULL || rdata == NULL) { + dbg_rrset("rrset: add_rdata_order: NULL arguments.\n"); + return KNOT_EBADARG; + } + + if (rrset->rdata == NULL) { + /* Easy peasy, just insert the first item. */ + rrset->rdata = rdata; + rrset->rdata->next = rrset->rdata; + } else { + knot_rdata_t *walk = NULL; + char found = 0; + knot_rdata_t *insert_after = rrset->rdata; + while (((walk = knot_rrset_rdata_get_next(rrset, + walk)) != NULL) && + (!found)) { + const knot_rrtype_descriptor_t *desc = + knot_rrtype_descriptor_by_type(rrset->type); + assert(desc); + int cmp = knot_rdata_compare(rdata, walk, + desc->wireformat); + if (cmp < 1) { + /* We've found place for this item. */ + } else if (cmp == 0) { + /* This item will not be inserted. */ + found = 1; + insert_after = NULL; + } + assert(cmp > 1); + /* Continue the search. */ + insert_after = walk; + } + if (insert_after != NULL) { + rdata->next = insert_after->next; + insert_after->next = rdata; + } else { + ; + /* Consider returning something different than EOK. */ + } + } + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + knot_rdata_t *knot_rrset_remove_rdata(knot_rrset_t *rrset, const knot_rdata_t *rdata) { @@ -167,19 +214,30 @@ int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs, int rc; if (rrset->rrsigs != NULL) { if (dupl == KNOT_RRSET_DUPL_MERGE) { - rc = knot_rrset_merge((void **)&rrset->rrsigs, - (void **)&rrsigs); + rc = knot_rrset_merge_no_dupl((void **)&rrset->rrsigs, + (void **)&rrsigs); if (rc != KNOT_EOK) { return rc; } else { return 1; } } else if (dupl == KNOT_RRSET_DUPL_SKIP) { +// rc = knot_rrset_merge_no_dupl((void **)&rrset->rrsigs, +// (void **)&rrsigs); +// if (rc != KNOT_EOK) { +// return rc; +// } else { +// return 1; +// } return 2; } else if (dupl == KNOT_RRSET_DUPL_REPLACE) { rrset->rrsigs = rrsigs; } } else { + if (rrset->ttl != rrsigs->ttl) { + rrsigs->ttl = rrset->ttl; + } + rrset->rrsigs = rrsigs; } @@ -291,6 +349,10 @@ knot_rdata_t *knot_rrset_rdata_get_next(knot_rrset_t *rrset, int knot_rrset_rdata_rr_count(const knot_rrset_t *rrset) { + if (rrset == NULL) { + return 0; + } + int count = 0; const knot_rdata_t *rdata = rrset->rdata; @@ -341,7 +403,7 @@ int knot_rrset_compare_rdata(const knot_rrset_t *r1, const knot_rrset_t *r2) } // compare RDATA sets (order is not significant) - const knot_rdata_t *rdata1= knot_rrset_rdata(r1); + const knot_rdata_t *rdata1 = knot_rrset_rdata(r1); const knot_rdata_t *rdata2; // find all RDATA from r1 in r2 @@ -434,7 +496,6 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset, *pos += 2; size += 10; -// compr->wire_pos += size; dbg_rrset_detail("Max size: %zu, size: %d\n", max_size, size); @@ -465,7 +526,6 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset, knot_dname_size(dname)); *pos += knot_dname_size(dname); rdlength += knot_dname_size(dname); -// compr->wire_pos += dname->size; break; } default: { @@ -481,7 +541,6 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset, dbg_rrset_detail("Raw data size: %d\n", raw_data[0]); *pos += raw_data[0]; rdlength += raw_data[0]; -// compr->wire_pos += raw_data[0]; break; } } @@ -523,11 +582,11 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size, if (ret < 0) { // some RR didn't fit in, so no RRs should be used // TODO: remove last entries from compression table - dbg_rrset_detail("Some RR didn't fit in.\n"); + dbg_rrset_verb("Some RR didn't fit in.\n"); return KNOT_ESPACE; } - dbg_rrset_detail("RR of size %d added.\n", ret); + dbg_rrset_verb("RR of size %d added.\n", ret); rrset_size += ret; ++rrs; } while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL); @@ -556,7 +615,7 @@ int knot_rrset_compare(const knot_rrset_t *r1, int res = ((r1->rclass == r2->rclass) && (r1->type == r2->type) - && (r1->ttl == r2->ttl) +// && (r1->ttl == r2->ttl) && knot_dname_compare(r1->owner, r2->owner) == 0); if (cmp == KNOT_RRSET_COMPARE_WHOLE && res) { @@ -571,7 +630,8 @@ int knot_rrset_compare(const knot_rrset_t *r1, /*----------------------------------------------------------------------------*/ -int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to) +int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to, + int copy_rdata_dnames) { if (from == NULL || to == NULL) { return KNOT_EBADARG; @@ -582,14 +642,14 @@ int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to) *to = (knot_rrset_t *)calloc(1, sizeof(knot_rrset_t)); CHECK_ALLOC_LOG(*to, KNOT_ENOMEM); - //(*to)->owner = knot_dname_deep_copy(from->owner); (*to)->owner = from->owner; knot_dname_retain((*to)->owner); (*to)->rclass = from->rclass; (*to)->ttl = from->ttl; (*to)->type = from->type; if (from->rrsigs != NULL) { - ret = knot_rrset_deep_copy(from->rrsigs, &(*to)->rrsigs); + ret = knot_rrset_deep_copy(from->rrsigs, &(*to)->rrsigs, + copy_rdata_dnames); if (ret != KNOT_EOK) { knot_rrset_deep_free(to, 1, 0, 0); return ret; @@ -601,8 +661,19 @@ int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to) /*! \note Order of RDATA will be reversed. */ while (rdata != NULL) { - ret = knot_rrset_add_rdata(*to, knot_rdata_deep_copy(rdata, - knot_rrset_type(from), 1)); + knot_rdata_t *rdata_copy = knot_rdata_deep_copy(rdata, + knot_rrset_type(from), + copy_rdata_dnames); +dbg_rrset_exec_detail( + char *name = knot_dname_to_str(knot_rrset_owner(from)); + dbg_rrset_detail("Copying RDATA from RRSet with owner: %s, type" + ": %s. Old RDATA ptr: %p, new RDATA ptr: %p\n", + name, + knot_rrtype_to_string(knot_rrset_type(from)), + rdata, rdata_copy); + free(name); +); + ret = knot_rrset_add_rdata(*to, rdata_copy); if (ret != KNOT_EOK) { knot_rrset_deep_free(to, 1, 1, 1); return ret; @@ -632,6 +703,7 @@ int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to) void knot_rrset_rotate(knot_rrset_t *rrset) { + /*! \todo Maybe implement properly one day. */ //rrset->rdata = rrset->rdata->next; } @@ -659,12 +731,6 @@ void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner, return; } -// char *name = knot_dname_to_str(knot_rrset_owner(*rrset)); -// char *type = knot_rrtype_to_string(knot_rrset_type(*rrset)); -// fprintf(stderr, "Deleting RRSet (%p) %s, type %s, rdata: %p\n", -// *rrset, name, type, (*rrset)->rdata); -// free(name); - if (free_rdata) { knot_rdata_t *tmp_rdata; knot_rdata_t *next_rdata; @@ -679,7 +745,6 @@ void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner, tmp_rdata = next_rdata; } -// printf("test: %p\n", tmp_rdata->next->next); assert(tmp_rdata == NULL || tmp_rdata->next == (*rrset)->rdata); @@ -693,10 +758,7 @@ void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner, free_rdata_dnames); } - /*! \todo Release owner every time? */ - //if (free_owner) { - knot_dname_release((*rrset)->owner); - //} + knot_dname_release((*rrset)->owner); free(*rrset); *rrset = NULL; @@ -711,8 +773,7 @@ int knot_rrset_merge(void **r1, void **r2) if ((knot_dname_compare(rrset1->owner, rrset2->owner) != 0) || rrset1->rclass != rrset2->rclass - || rrset1->type != rrset2->type - || rrset1->ttl != rrset2->ttl) { + || rrset1->type != rrset2->type) { return KNOT_EBADARG; } @@ -744,6 +805,155 @@ int knot_rrset_merge(void **r1, void **r2) } tmp_rdata->next = rrset1->rdata; + rrset2->rdata = rrset1->rdata; + + return KNOT_EOK; +} + +int knot_rrset_merge_no_dupl(void **r1, void **r2) +{ + if (r1 == NULL || r2 == NULL) { + dbg_rrset("rrset: merge_no_dupl: NULL arguments."); + return KNOT_EBADARG; + } + + knot_rrset_t *rrset1 = (knot_rrset_t *)(*r1); + knot_rrset_t *rrset2 = (knot_rrset_t *)(*r2); + if (rrset1 == NULL || rrset2 == NULL) { + dbg_rrset("rrset: merge_no_dupl: NULL arguments."); + return KNOT_EBADARG; + } + +dbg_rrset_exec_detail( + char *name = knot_dname_to_str(rrset1->owner); + dbg_rrset_detail("rrset: merge_no_dupl: Merging %s.\n", name); + free(name); +); + + if ((knot_dname_compare(rrset1->owner, rrset2->owner) != 0) + || rrset1->rclass != rrset2->rclass + || rrset1->type != rrset2->type) { + dbg_rrset("rrset: merge_no_dupl: Trying to merge " + "different RRs.\n"); + return KNOT_EBADARG; + } + + knot_rdata_t *walk2 = rrset2->rdata; + + // no RDATA in RRSet 1 + if (rrset1->rdata == NULL && rrset2->rdata != NULL) { + /* + * This function has to assure that there are no duplicates in + * second RRSet's list. This can be done by putting a first + * item from the second list as a first item of the first list + * and then simply continuing with inserting items from second + * list to the first one. + * + * However, we must store pointer to second item in the second + * list, as the 'next' pointer of the first item will be altered + */ + + // Store pointer to the second item in RRSet2 RDATA so that + // we later start from this item. + walk2 = knot_rrset_rdata_get_next(rrset2, walk2); + assert(walk2 == rrset2->rdata->next || walk2 == NULL); + + // Connect the first item from second list to the first list. + rrset1->rdata = rrset2->rdata; + // Close the cyclic list (by pointing to itself). + rrset1->rdata->next = rrset1->rdata; + } else if (rrset2->rdata == NULL) { + return KNOT_EOK; + } + + /* + * Check that rrset1 does not contain any rdata from rrset2, if so + * such RDATA shall not be inserted. + */ + + /* Get last RDATA from first rrset, we'll need it for insertion. */ + knot_rdata_t *insert_after = rrset1->rdata; + while (insert_after->next != rrset1->rdata) { + dbg_rrset_detail("rrset: merge_dupl: first rrset rdata: %p.\n", + insert_after); + insert_after = insert_after->next; + } + assert(insert_after->next == rrset1->rdata); + + while (walk2 != NULL) { + knot_rdata_t *walk1 = rrset1->rdata; + char dupl = 0; + while ((walk1 != NULL) && + !dupl) { + const knot_rrtype_descriptor_t *desc = + knot_rrtype_descriptor_by_type(rrset1->type); + assert(desc); + /* If walk1 and walk2 are equal, do not insert. */ + dupl = !knot_rdata_compare(walk1, walk2, + desc->wireformat); + walk1 = knot_rrset_rdata_get_next(rrset1, walk1); + dbg_rrset_detail("rrset: merge_dupl: next item: %p.\n", + walk1); + } + if (!dupl) { + dbg_rrset_detail("rrset: merge_dupl: Inserting " + "unique item (%p).\n", + walk2); + knot_rdata_t *tmp = walk2; + /* + * We need to move this, insertion + * will corrupt pointers. + */ + walk2 = knot_rrset_rdata_get_next(rrset2, walk2); + /* Insert this item at the end of first list. */ + tmp->next = insert_after->next; + insert_after->next = tmp; + insert_after = tmp; + /*!< \todo This message has to be removed after bugfix. */ + dbg_rrset_detail("rrset: merge_no_dupl: Insert after=%p" + ", tmp=%p, tmp->next=%p, " + " rrset1->rdata=%p" + "\n", + insert_after, tmp, tmp->next, + rrset1->rdata); + assert(tmp->next == rrset1->rdata); + } else { + dbg_rrset_detail("rrset: merge_dupl: Skipping and " + "freeing duplicated item " + "of type: %s (%p).\n", + knot_rrtype_to_string(rrset1->type), + walk2); + /* + * Not freeing this item will result in a leak. + * Since this operation destroys the second + * list, we have to free the item here. + */ + knot_rdata_t *tmp = walk2; + dbg_rrset_detail("rrset: merge_dupl: freeing: %p.\n", + tmp); + walk2 = knot_rrset_rdata_get_next(rrset2, walk2); + knot_rdata_deep_free(&tmp, rrset1->type, 1); + assert(tmp == NULL); + /* Maybe caller should be warned about this. */ + } + } + + assert(walk2 == NULL); +dbg_rrset_exec_detail( + dbg_rrset_detail("rrset: merge_dupl: RDATA after merge:\n "); + knot_rdata_t *walk1 = rrset1->rdata; + while (walk1 != NULL) { + dbg_rrset_detail("%p ->\n", walk1); + walk1 = knot_rrset_rdata_get_next(rrset1, walk1); + } + dbg_rrset_detail("rrset: merge_dupl: RDATA after merge: r1:%p r2: %p\n", + rrset1->rdata, rrset2->rdata); +); + /* + * Since there is a possibility of corrupted list for second RRSet, it + * is safer to set its list to NULL, so that it cannot be used. + */ + rrset2->rdata = NULL; return KNOT_EOK; } diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h index b5c11dc..36d8da5 100644..100755 --- a/src/libknot/rrset.h +++ b/src/libknot/rrset.h @@ -105,6 +105,21 @@ knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type, */ int knot_rrset_add_rdata(knot_rrset_t *rrset, knot_rdata_t *rdata); +/*! + * \brief Adds the given RDATA to the RRSet but will not insert duplicated data. + * + * \warning Should be only used to insert one RDATA. (NO lists) + * + * \param rrset RRSet to add the RDATA to. + * \param rdata RDATA to add to the RRSet. + * + * \retval KNOT_EOK + * \retval KNOT_EBADARG + * + * \todo Provide some function for comparing RDATAs. + */ +int knot_rrset_add_rdata_order(knot_rrset_t *rrset, knot_rdata_t *rdata); + knot_rdata_t * knot_rrset_remove_rdata(knot_rrset_t *rrset, const knot_rdata_t *rdata); @@ -249,7 +264,8 @@ int knot_rrset_compare(const knot_rrset_t *r1, knot_rrset_compare_type_t cmp); /*! \todo Add unit test. */ -int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to); +int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to, + int copy_rdata_dnames); /*! \todo Add unit test. */ int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to); @@ -296,9 +312,9 @@ void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner, * \brief Merges two RRSets. * * Merges \a r1 into \a r2 by concatenating the list of RDATAs in \a r2 after - * the list of RDATAs in \a r1. \a r2 is unaffected by this, though you must not - * destroy the RDATAs in \a r2 as they are now also in \a r1. (You may use - * function knot_rrset_free() though, as it does not touch RDATAs). + * the list of RDATAs in \a r1. You must not + * destroy the RDATAs in \a r2 as they are now identical to RDATAs in \a r1. + * (You may use function knot_rrset_free() though, as it does not touch RDATAs). * * \note Member \a rrsigs is preserved from the first RRSet. * @@ -311,6 +327,26 @@ void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner, */ int knot_rrset_merge(void **r1, void **r2); + +/*! + * \brief Merges two RRSets, but will discard and free any duplicates in \a r2. + * + * Merges \a r1 into \a r2 by concatenating the list of RDATAs in \a r2 after + * the list of RDATAs in \a r1. You must not + * destroy the RDATAs in \a r2 as they are now identical to RDATAs in \a r1. + * (You may use function knot_rrset_free() though, as it does not touch RDATAs). + * + * \note Member \a rrsigs is preserved from the first RRSet. + * + * \param r1 Pointer to RRSet to be merged into. + * \param r2 Poitner to RRSet to be merged. + * + * \retval KNOT_EOK + * \retval KNOT_EBADARG if the RRSets could not be merged, because their + * Owner, Type, Class or TTL does not match. + */ +int knot_rrset_merge_no_dupl(void **r1, void **r2); + #endif /* _KNOT_RRSET_H_ */ /*! @} */ diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c index 148fc87..173538c 100644..100755 --- a/src/libknot/tsig-op.c +++ b/src/libknot/tsig-op.c @@ -21,20 +21,27 @@ #include <time.h> #include "common.h" +#include "common/base64.h" #include "tsig.h" #include "tsig-op.h" #include "util/wire.h" -#include "libknot/util/conv.h" #include "util/error.h" #include "util/debug.h" #include "consts.h" const int KNOT_TSIG_MAX_DIGEST_SIZE = 64; // size of HMAC-SHA512 digest - +const uint16_t KNOT_TSIG_FUDGE_DEFAULT = 300; // default Fudge value +enum b64_const { + B64BUFSIZE = 65535 +}; static int knot_tsig_check_algorithm(const knot_rrset_t *tsig_rr) { + if (tsig_rr == NULL) { + return KNOT_EBADARG; + } + const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr); if (!alg_name) { return KNOT_EMALF; @@ -53,6 +60,10 @@ static int knot_tsig_check_algorithm(const knot_rrset_t *tsig_rr) static int knot_tsig_check_key(const knot_rrset_t *tsig_rr, const knot_key_t *tsig_key) { + if (tsig_rr == NULL || tsig_key == NULL) { + return KNOT_EBADARG; + } + const knot_dname_t *tsig_name = knot_rrset_owner(tsig_rr); if (!tsig_name) { return KNOT_EMALF; @@ -94,28 +105,25 @@ static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len, return KNOT_TSIG_EBADSIG; } - /* Create digest, using length of the algorithm. */ -// *digest = malloc(sizeof(uint8_t) * tsig_alg_digest_length(tsig_alg)); -// if (!digest) { -// ERR_ALLOC_FAILED; -// return KNOT_ENOMEM; -// } - /* Decode key from Base64. */ char decoded_key[B64BUFSIZE]; - - int decoded_key_size = b64_pton(key->secret, (uint8_t *)decoded_key, - B64BUFSIZE); + + size_t decoded_key_size = B64BUFSIZE; + int ret = base64_decode(key->secret, strlen(key->secret), + decoded_key, + &decoded_key_size); + if (ret != 1) { + dbg_tsig("TSIG: New decode function failed! (%d)\n", ret); + return KNOT_ERROR; + } + if (decoded_key_size < 0) { dbg_tsig("TSIG: Could not decode Base64\n"); return KNOT_ERROR; } - + dbg_tsig_detail("TSIG: decoded key size: %d\n", decoded_key_size); dbg_tsig_detail("TSIG: decoded key: '%*s'\n", decoded_key_size, decoded_key); - -// dbg_tsig_detail("TSIG: using this wire for digest calculation\n"); -// dbg_tsig_hex_detail(wire, wire_len); dbg_tsig_detail("Wire for signing is %zu bytes long.\n", wire_len); /* Compute digest. */ @@ -152,6 +160,7 @@ static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr, uint64_t prev_time_signed) { if (!tsig_rr) { + dbg_tsig("TSIG: check_time_signed: NULL argument.\n"); return KNOT_EBADARG; } @@ -184,21 +193,14 @@ static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr, return KNOT_EOK; } -static int knot_tsig_write_tsig_timers(uint8_t *wire, - const knot_rrset_t *tsig_rr) -{ - // put time signed - knot_wire_write_u48(wire, tsig_rdata_time_signed(tsig_rr)); - - // put fudge - knot_wire_write_u16(wire + 6, tsig_rdata_fudge(tsig_rr)); - - return KNOT_EOK; -} - static int knot_tsig_write_tsig_variables(uint8_t *wire, const knot_rrset_t *tsig_rr) { + if (wire == NULL || tsig_rr == NULL) { + dbg_tsig("TSIG: write tsig variables: NULL arguments.\n"); + return KNOT_EBADARG; + } + /* Copy TSIG variables - starting with key name. */ const knot_dname_t *tsig_owner = knot_rrset_owner(tsig_rr); if (!tsig_owner) { @@ -210,24 +212,23 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire, memcpy(wire + offset, knot_dname_name(tsig_owner), sizeof(uint8_t) * knot_dname_size(tsig_owner)); - dbg_tsig("TSIG: write variables: written owner (tsig alg): \n"); - /*knot_rrset_class(tsig_rr));*/ - dbg_tsig_hex_detail(wire + offset, knot_dname_size(tsig_owner)); + dbg_tsig_verb("TSIG: write variables: written owner (tsig alg): \n"); + dbg_tsig_hex_verb(wire + offset, knot_dname_size(tsig_owner)); offset += knot_dname_size(tsig_owner); /*!< \todo which order? */ /* Copy class. */ knot_wire_write_u16(wire + offset, knot_rrset_class(tsig_rr)); - dbg_tsig("TSIG: write variables: written CLASS: %u - ", - knot_rrset_class(tsig_rr)); + dbg_tsig_verb("TSIG: write variables: written CLASS: %u - ", + knot_rrset_class(tsig_rr)); dbg_tsig_hex_detail(wire + offset, sizeof(uint16_t)); offset += sizeof(uint16_t); /* Copy TTL - always 0. */ knot_wire_write_u32(wire + offset, knot_rrset_ttl(tsig_rr)); - dbg_tsig("TSIG: write variables: written TTL: %u - ", - knot_rrset_ttl(tsig_rr)); + dbg_tsig_verb("TSIG: write variables: written TTL: %u - ", + knot_rrset_ttl(tsig_rr)); dbg_tsig_hex_detail(wire + offset, sizeof(uint32_t)); offset += sizeof(uint32_t); @@ -237,28 +238,25 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire, dbg_tsig("TSIG: write variables: no algorithm name.\n"); return KNOT_EBADARG; } -// alg_name = knot_dname_new_from_str("HMAC-MD5.SIG-ALG.REG.INT.", - //strlen("HMAC-MD5.SIG-ALG.REG.INT."), - //NULL); memcpy(wire + offset, knot_dname_name(alg_name), sizeof(uint8_t) * knot_dname_size(alg_name)); offset += knot_dname_size(alg_name); - dbg_tsig_detail("TSIG: write variables: written alg name: %s\n", - knot_dname_to_str(alg_name)); + dbg_tsig_verb("TSIG: write variables: written alg name: %s\n", + knot_dname_to_str(alg_name)); /* Following data are written in network order. */ /* Time signed. */ knot_wire_write_u48(wire + offset, tsig_rdata_time_signed(tsig_rr)); offset += 6; - dbg_tsig_detail("TSIG: write variables: time signed: %llu - ", - tsig_rdata_time_signed(tsig_rr)); + dbg_tsig_verb("TSIG: write variables: time signed: %llu - ", + tsig_rdata_time_signed(tsig_rr)); dbg_tsig_hex_detail(wire + offset - 6, 6); /* Fudge. */ knot_wire_write_u16(wire + offset, tsig_rdata_fudge(tsig_rr)); offset += sizeof(uint16_t); - dbg_tsig_detail("TSIG: write variables: fudge: %hu\n", - tsig_rdata_fudge(tsig_rr)); + dbg_tsig_verb("TSIG: write variables: fudge: %hu\n", + tsig_rdata_fudge(tsig_rr)); /* TSIG error. */ knot_wire_write_u16(wire + offset, tsig_rdata_error(tsig_rr)); offset += sizeof(uint16_t); @@ -279,7 +277,7 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire, offset += sizeof(uint16_t); /* Skip the length. */ - dbg_tsig_detail("Copying other data.\n"); + dbg_tsig_verb("Copying other data.\n"); memcpy(wire + offset, other_data, other_data_length); return KNOT_EOK; @@ -288,7 +286,14 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire, static int knot_tsig_wire_write_timers(uint8_t *wire, const knot_rrset_t *tsig_rr) { + if (wire == NULL || tsig_rr == NULL) { + dbg_tsig("TSIG: write timers: NULL arguments.\n"); + return KNOT_EBADARG; + } + + //write time signed knot_wire_write_u48(wire, tsig_rdata_time_signed(tsig_rr)); + //write fudge knot_wire_write_u16(wire + 6, tsig_rdata_fudge(tsig_rr)); return KNOT_EOK; @@ -308,21 +313,14 @@ static int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len, /* Create tmp TSIG. */ int ret = KNOT_EOK; -// knot_rrset_t *tmp_tsig = -// knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0); -// if (!tmp_tsig) { -// return KNOT_ENOMEM; -// } - -// tsig_rdata_store_current_time(tmp_tsig); /* * Create tmp wire, it should contain message * plus request mac plus tsig varibles. */ - dbg_tsig("Counting wire size: %zu, %zu, %zu.\n", - msg_len, request_mac_len, - tsig_rdata_tsig_variables_length(tmp_tsig)); + dbg_tsig_verb("Counting wire size: %zu, %zu, %zu.\n", + msg_len, request_mac_len, + tsig_rdata_tsig_variables_length(tmp_tsig)); size_t wire_len = sizeof(uint8_t) * (msg_len + request_mac_len + ((request_mac_len > 0) ? 2 : 0) + @@ -339,23 +337,21 @@ static int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len, /* Copy the request MAC - should work even if NULL. */ if (request_mac_len > 0) { - dbg_tsig_detail("Copying request MAC size\n"); + dbg_tsig_verb("Copying request MAC size\n"); knot_wire_write_u16(pos, request_mac_len); pos += 2; } - dbg_tsig("Copying request mac.\n"); + dbg_tsig_verb("Copying request mac.\n"); memcpy(pos, request_mac, sizeof(uint8_t) * request_mac_len); dbg_tsig_detail("TSIG: create wire: request mac: "); dbg_tsig_hex_detail(pos, request_mac_len); pos += request_mac_len; /* Copy the original message. */ - dbg_tsig("Copying original message.\n"); + dbg_tsig_verb("Copying original message.\n"); memcpy(pos, msg, msg_len); - dbg_tsig_detail("TSIG: create wire: original message: \n"); - //dbg_tsig_hex_detail(pos, msg_len); pos += msg_len; /* Copy TSIG variables. */ - dbg_tsig("Writing TSIG variables.\n"); + dbg_tsig_verb("Writing TSIG variables.\n"); ret = knot_tsig_write_tsig_variables(pos, tmp_tsig); if (ret != KNOT_EOK) { dbg_tsig("TSIG: create wire: failed to write TSIG " @@ -375,20 +371,8 @@ static int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len, return ret; } -// assert(digest_tmp_len > 0); free(wire); -// if (digest_tmp_len > *digest_len) { -// *digest_len = 0; -// return KNOT_ESPACE; -// } - -// knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); - - // everything went ok, save the digest to the output parameter -// memcpy(digest, digest_tmp, digest_tmp_len); -// *digest_len = digest_tmp_len; - return KNOT_EOK; } @@ -411,9 +395,9 @@ static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len, * Create tmp wire, it should contain message * plus request mac plus tsig varibles. */ - dbg_tsig("Counting wire size: %zu, %zu, %zu.\n", - msg_len, prev_mac_len, - tsig_rdata_tsig_timers_length()); + dbg_tsig_verb("Counting wire size: %zu, %zu, %zu.\n", + msg_len, prev_mac_len, + tsig_rdata_tsig_timers_length()); size_t wire_len = sizeof(uint8_t) * (msg_len + prev_mac_len + tsig_rdata_tsig_timers_length() + 2); @@ -426,27 +410,24 @@ static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len, memset(wire, 0, wire_len); /* Copy the request MAC - should work even if NULL. */ - dbg_tsig("Copying request mac size.\n"); + dbg_tsig_verb("Copying request mac size.\n"); knot_wire_write_u16(wire, prev_mac_len); - dbg_tsig("Copying request mac.\n"); + dbg_tsig_verb("Copying request mac.\n"); memcpy(wire + 2, prev_mac, sizeof(uint8_t) * prev_mac_len); dbg_tsig_detail("TSIG: create wire: request mac: "); dbg_tsig_hex_detail(wire + 2, prev_mac_len); /* Copy the original message. */ - dbg_tsig("Copying original message.\n"); + dbg_tsig_verb("Copying original message.\n"); memcpy(wire + prev_mac_len + 2, msg, msg_len); - dbg_tsig_detail("TSIG: create wire: original message: \n"); - //dbg_tsig_hex_detail(wire + prev_mac_len, msg_len); /* Copy TSIG variables. */ - dbg_tsig("Writing TSIG timers.\n"); - ret = knot_tsig_write_tsig_timers(wire + prev_mac_len + msg_len + 2, + dbg_tsig_verb("Writing TSIG timers.\n"); + ret = knot_tsig_wire_write_timers(wire + prev_mac_len + msg_len + 2, tmp_tsig); -// ret = knot_tsig_write_tsig_variables(wire + prev_mac_len + msg_len, -// tmp_tsig); if (ret != KNOT_EOK) { dbg_tsig("TSIG: create wire: failed to write TSIG " "timers: %s\n", knot_strerror(ret)); + free(wire); return ret; } @@ -457,6 +438,7 @@ static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len, dbg_tsig("TSIG: create wire: failed to compute digest: %s\n", knot_strerror(ret)); *digest_len = 0; + free(wire); return ret; } @@ -478,7 +460,7 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, knot_dname_t *key_name_copy = knot_dname_deep_copy(key->name); if (!key_name_copy) { - dbg_tsig_detail("TSIG: key_name_copy = NULL\n"); + dbg_tsig("TSIG: key_name_copy = NULL\n"); return KNOT_ENOMEM; } @@ -488,14 +470,14 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, /* Should be retained by rrsig or freed, release. */ knot_dname_release(key_name_copy); if (!tmp_tsig) { - dbg_tsig_detail("TSIG: tmp_tsig = NULL\n"); + dbg_tsig("TSIG: tmp_tsig = NULL\n"); return KNOT_ENOMEM; } /* Create rdata for TSIG RR. */ knot_rdata_t *rdata = knot_rdata_new(); if (!rdata) { - dbg_tsig_detail("TSIG: rdata = NULL\n"); + dbg_tsig("TSIG: rdata = NULL\n"); knot_rrset_free(&tmp_tsig); return KNOT_ENOMEM; } @@ -510,7 +492,7 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, knot_rdata_item_t *items = malloc(sizeof(knot_rdata_item_t) * desc->length); if (!items) { - dbg_tsig_detail("TSIG: items = NULL\n"); + dbg_tsig("TSIG: items = NULL\n"); ERR_ALLOC_FAILED; knot_rrset_free(&tmp_tsig); knot_rdata_free(&rdata); @@ -521,7 +503,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, int ret = knot_rdata_set_items(rdata, items, desc->length); if (ret != KNOT_EOK) { - dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret)); + dbg_tsig("TSIG: rdata_set_items returned %s\n", + knot_strerror(ret)); free(items); knot_rrset_free(&tmp_tsig); knot_rdata_free(&rdata); @@ -554,7 +537,7 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, tsig_rdata_set_other_data(tmp_tsig, 0, 0); } - tsig_rdata_set_fudge(tmp_tsig, 300); /*! \todo Bleeding eyes :-) */ + tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); /* Set original ID */ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); @@ -583,13 +566,11 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, int rr_count = 0; tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp); - //knot_rrset_dump(tmp_tsig, 1); - /* Write RRSet to wire */ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, &tsig_wire_len, &rr_count); if (ret != KNOT_EOK) { - dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret)); + dbg_tsig("TSIG: rrset_to_wire = %s\n", knot_strerror(ret)); *digest_len = 0; knot_rrset_free(&tmp_tsig); knot_rdata_free(&rdata); @@ -633,7 +614,7 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, /* Create rdata for TSIG RR. */ knot_rdata_t *rdata = knot_rdata_new(); if (!rdata) { - dbg_tsig_detail("TSIG: rdata = NULL\n"); + dbg_tsig("TSIG: rdata = NULL\n"); knot_rrset_free(&tmp_tsig); return KNOT_ENOMEM; } @@ -642,7 +623,7 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, ret = knot_rrset_add_rdata(tmp_tsig, rdata); if (ret != KNOT_EOK) { - dbg_tsig_detail("TSIG: could not add rdata\n"); + dbg_tsig("TSIG: could not add rdata\n"); knot_rrset_free(&tmp_tsig); knot_rdata_free(&rdata); return ret; @@ -656,7 +637,7 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, knot_rdata_item_t *items = malloc(sizeof(knot_rdata_item_t) * desc->length); if (!items) { - dbg_tsig_detail("TSIG: items = NULL\n"); + dbg_tsig("TSIG: items = NULL\n"); ERR_ALLOC_FAILED; knot_rrset_free(&tmp_tsig); knot_rdata_free(&rdata); @@ -667,7 +648,8 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, ret = knot_rdata_set_items(rdata, items, desc->length); if (ret != KNOT_EOK) { - dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret)); + dbg_tsig("TSIG: rdata_set_items returned %s\n", + knot_strerror(ret)); knot_rrset_free(&tmp_tsig); knot_rdata_free(&rdata); free(items); @@ -676,7 +658,7 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, free(items); tsig_rdata_store_current_time(tmp_tsig); - tsig_rdata_set_fudge(tmp_tsig, 300); + tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); /* Create wire to be signed. */ size_t wire_len = prev_digest_len + to_sign_len @@ -739,8 +721,8 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, /* Set other data. */ tsig_rdata_set_other_data(tmp_tsig, 0, NULL); - dbg_tsig_detail("Message max length: %zu, message length: %zu\n", - msg_max_len, *msg_len); + dbg_tsig_verb("Message max length: %zu, message length: %zu\n", + msg_max_len, *msg_len); size_t tsig_wire_size = msg_max_len - *msg_len; int rr_count = 0; @@ -788,7 +770,7 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr, return ret; } - dbg_tsig("TSIG: time checked.\n"); + dbg_tsig_verb("TSIG: time checked.\n"); /* Check that libknot knows the algorithm. */ ret = knot_tsig_check_algorithm(tsig_rr); @@ -796,7 +778,7 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr, return ret; } - dbg_tsig("TSIG: algorithm checked.\n"); + dbg_tsig_verb("TSIG: algorithm checked.\n"); /* Check that key is valid, ie. the same as given in args. */ ret = knot_tsig_check_key(tsig_rr, tsig_key); @@ -804,22 +786,15 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr, return ret; } - dbg_tsig("TSIG: key validity checked.\n"); + dbg_tsig_verb("TSIG: key validity checked.\n"); /* Time OK algorithm OK, key name OK - do digest. */ /* Calculate the size of TSIG RR. */ size_t tsig_len = tsig_wire_actsize(tsig_rr); - dbg_tsig_detail("TSIG: check digest: wire before strip: \n"); - //dbg_tsig_hex_detail(wire, size); - /* Strip the TSIG. */ size -= tsig_len; - dbg_tsig_detail("TSIG: check digest: wire after strip (stripped %zu):\n", - tsig_len); - //dbg_tsig_hex_detail(wire, size); - uint8_t *wire_to_sign = malloc(sizeof(uint8_t) * size); if (!wire_to_sign) { ERR_ALLOC_FAILED; @@ -858,16 +833,7 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr, return ret; } -// uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE]; -// size_t digest_tmp_len = 0; -// ret = knot_tsig_compute_digest(wire, size, digest_tmp, -// &digest_tmp_len, tsig_key); -// if (ret != KNOT_EOK) { -// dbg_tsig("TSIG: digest could not be calculated\n"); -// return ret; -// } - - dbg_tsig("TSIG: digest calculated\n"); + dbg_tsig_verb("TSIG: digest calculated\n"); /* Compare MAC from TSIG RR RDATA with just computed digest. */ @@ -880,17 +846,16 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr, const uint8_t *tsig_mac = tsig_rdata_mac(tsig_rr); if (mac_length != tsig_alg_digest_length(alg)) { - dbg_tsig("TSIG: calculated digest length and given length do not match!\n"); + dbg_tsig("TSIG: calculated digest length and given length do " + "not match!\n"); return KNOT_TSIG_EBADSIG; } -// assert(tsig_alg_digest_length(alg) == mac_length); - - dbg_tsig("TSIG: calc digest : "); - dbg_tsig_hex(digest_tmp, digest_tmp_len); + dbg_tsig_verb("TSIG: calc digest : "); + dbg_tsig_hex_verb(digest_tmp, digest_tmp_len); - dbg_tsig("TSIG: given digest: "); - dbg_tsig_hex(tsig_mac, mac_length); + dbg_tsig_verb("TSIG: given digest: "); + dbg_tsig_hex_verb(tsig_mac, mac_length); if (strncasecmp((char *)(tsig_mac), (char *)digest_tmp, mac_length) != 0) { @@ -904,7 +869,7 @@ int knot_tsig_server_check(const knot_rrset_t *tsig_rr, const uint8_t *wire, size_t size, const knot_key_t *tsig_key) { - dbg_tsig_verb("tsig_server_check()\n"); + dbg_tsig("tsig_server_check()\n"); return knot_tsig_check_digest(tsig_rr, wire, size, NULL, 0, tsig_key, 0, 0); } @@ -915,7 +880,7 @@ int knot_tsig_client_check(const knot_rrset_t *tsig_rr, const knot_key_t *tsig_key, uint64_t prev_time_signed) { - dbg_tsig_verb("tsig_client_check()\n"); + dbg_tsig("tsig_client_check()\n"); return knot_tsig_check_digest(tsig_rr, wire, size, request_mac, request_mac_len, tsig_key, prev_time_signed, 0); @@ -928,9 +893,7 @@ int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr, const knot_key_t *tsig_key, uint64_t prev_time_signed) { -// return knot_tsig_client_check(tsig_rr, wire, size, prev_digest, -// prev_digest_len, tsig_key); - dbg_tsig_verb("tsig_client_check_next()\n"); + dbg_tsig("tsig_client_check_next()\n"); return knot_tsig_check_digest(tsig_rr, wire, size, prev_digest, prev_digest_len, tsig_key, prev_time_signed, 1); @@ -950,15 +913,14 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, knot_dname_t *key_name = knot_dname_deep_copy(knot_rrset_owner(tsig_rr)); if (key_name == NULL) { - dbg_tsig_detail("TSIG: failed to copy owner\n"); + dbg_tsig("TSIG: failed to copy owner\n"); return KNOT_ERROR; } knot_rrset_t *tmp_tsig = - knot_rrset_new(key_name, - KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0); + knot_rrset_new(key_name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0); if (!tmp_tsig) { - dbg_tsig_detail("TSIG: tmp_tsig = NULL\n"); + dbg_tsig("TSIG: tmp_tsig = NULL\n"); knot_dname_free(&key_name); return KNOT_ENOMEM; } @@ -966,7 +928,7 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, /* Create rdata for TSIG RR. */ knot_rdata_t *rdata = knot_rdata_new(); if (!rdata) { - dbg_tsig_detail("TSIG: rdata = NULL\n"); + dbg_tsig("TSIG: rdata = NULL\n"); knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); return KNOT_ENOMEM; } @@ -981,7 +943,7 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, knot_rdata_item_t *items = malloc(sizeof(knot_rdata_item_t) * desc->length); if (items == NULL) { - dbg_tsig_detail("TSIG: items = NULL\n"); + dbg_tsig("TSIG: items = NULL\n"); ERR_ALLOC_FAILED; knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); return KNOT_ENOMEM; @@ -992,8 +954,8 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, int ret = knot_rdata_set_items(rdata, items, desc->length); free(items); if (ret != KNOT_EOK) { - dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", - knot_strerror(ret)); + dbg_tsig("TSIG: rdata_set_items returned %s\n", + knot_strerror(ret)); knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); return ret; } @@ -1001,14 +963,18 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, knot_dname_t *alg_name = knot_dname_deep_copy(tsig_rdata_alg_name(tsig_rr)); if (alg_name == NULL) { - dbg_tsig_detail("TSIG: failed to copy alg name\n"); + dbg_tsig("TSIG: failed to copy alg name\n"); knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); return KNOT_ERROR; } tsig_rdata_set_alg_name(tmp_tsig, alg_name); tsig_rdata_set_time_signed(tmp_tsig, tsig_rdata_time_signed(tsig_rr)); - tsig_rdata_set_fudge(tmp_tsig, tsig_rdata_fudge(tsig_rr)); + + /* Comparing to BIND it was found out that the Fudge should always be + * set to the server's value. + */ + tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); tsig_rdata_set_mac(tmp_tsig, 0, NULL); knot_dname_release(alg_name); /* Already copied in tsig_rdata_set_alg_name() */ @@ -1029,7 +995,7 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, &tsig_wire_len, &rr_count); if (ret != KNOT_EOK) { - dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret)); + dbg_tsig("TSIG: rrset_to_wire = %s\n", knot_strerror(ret)); knot_rrset_deep_free(&tmp_tsig, 1, 1, 1); return ret; } diff --git a/src/libknot/tsig-op.h b/src/libknot/tsig-op.h index 07a84a8..07a84a8 100644..100755 --- a/src/libknot/tsig-op.h +++ b/src/libknot/tsig-op.h diff --git a/src/libknot/tsig.c b/src/libknot/tsig.c index e8df92e..86b7f9d 100644..100755 --- a/src/libknot/tsig.c +++ b/src/libknot/tsig.c @@ -319,21 +319,21 @@ tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig) /* Get the algorithm name. */ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig); if (!alg_name) { - dbg_tsig_detail("TSIG: rdata: cannot get algorithm name.\n"); + dbg_tsig("TSIG: rdata: cannot get algorithm name.\n"); return KNOT_TSIG_ALG_NULL; } /* Convert alg name to string. */ char *name = knot_dname_to_str(alg_name); if (!name) { - dbg_tsig_detail("TSIG: rdata: cannot convert alg name.\n"); + dbg_tsig("TSIG: rdata: cannot convert alg name.\n"); return KNOT_TSIG_ALG_NULL; } knot_lookup_table_t *item = knot_lookup_by_name(tsig_alg_table, name); free(name); if (!item) { - dbg_tsig_detail("TSIG: rdata: unknown algorithm.\n"); + dbg_tsig("TSIG: rdata: unknown algorithm.\n"); return KNOT_TSIG_ALG_NULL; } @@ -342,7 +342,7 @@ tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig) uint64_t tsig_rdata_time_signed(const knot_rrset_t *tsig) { - /*!< \note How about assert. Or maybe change API??? */ + /*! \note How about assert. Or maybe change API??? */ if (!tsig) { return 0; } @@ -366,7 +366,7 @@ uint64_t tsig_rdata_time_signed(const knot_rrset_t *tsig) uint16_t tsig_rdata_fudge(const knot_rrset_t *tsig) { - /*!< \note How about assert. Or maybe change API??? */ + /*! \note How about assert. Or maybe change API??? */ if (!tsig) { return 0; } @@ -390,7 +390,7 @@ uint16_t tsig_rdata_fudge(const knot_rrset_t *tsig) const uint8_t *tsig_rdata_mac(const knot_rrset_t *tsig) { - /*!< \note How about assert. Or maybe change API??? */ + /*! \note How about assert. Or maybe change API??? */ if (!tsig) { return 0; } @@ -424,7 +424,7 @@ size_t tsig_rdata_mac_length(const knot_rrset_t *tsig) uint16_t tsig_rdata_orig_id(const knot_rrset_t *tsig) { - /*!< \note How about assert. Or maybe change API??? */ + /*! \note How about assert. Or maybe change API??? */ if (!tsig) { return 0; } @@ -448,7 +448,7 @@ uint16_t tsig_rdata_orig_id(const knot_rrset_t *tsig) uint16_t tsig_rdata_error(const knot_rrset_t *tsig) { - /*!< \note How about assert. Or maybe change API??? */ + /*! \note How about assert. Or maybe change API??? */ if (!tsig) { return 0; } @@ -472,7 +472,7 @@ uint16_t tsig_rdata_error(const knot_rrset_t *tsig) const uint8_t *tsig_rdata_other_data(const knot_rrset_t *tsig) { - /*!< \note How about assert. Or maybe change API??? */ + /*! \note How about assert. Or maybe change API??? */ if (!tsig) { return 0; } @@ -491,7 +491,7 @@ const uint8_t *tsig_rdata_other_data(const knot_rrset_t *tsig) uint16_t tsig_rdata_other_data_length(const knot_rrset_t *tsig) { - /*!< \note How about assert. Or maybe change API??? */ + /*! \note How about assert. Or maybe change API??? */ if (!tsig) { return 0; } @@ -558,6 +558,9 @@ uint16_t tsig_alg_digest_length(tsig_algorithm_t alg) size_t tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig) { + if (tsig == NULL) { + return 0; + } /* Key name, Algorithm name and Other data have variable lengths. */ const knot_dname_t *key_name = knot_rrset_owner(tsig); if (!key_name) { @@ -569,13 +572,6 @@ size_t tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig) return 0; } -// dbg_tsig_detail("key_name: %.*s (size: %u) alg_name: %.*s (size: %u)\n", knot_dname_size(key_name), -// key_name->name, alg_name->size, alg_name->name, -// key_name->size, alg_name->size); - -// dbg_tsig_hex_detail(key_name->name, key_name->size); -// dbg_tsig_hex_detail(alg_name->name, alg_name->size); - uint16_t other_data_length = tsig_rdata_other_data_length(tsig); return knot_dname_size(key_name) + knot_dname_size(alg_name) + @@ -594,7 +590,7 @@ int tsig_rdata_store_current_time(knot_rrset_t *tsig) return KNOT_EBADARG; } time_t curr_time = time(NULL); - /*!< \todo bleeding eyes. */ + /*! \todo bleeding eyes. */ tsig_rdata_set_time_signed(tsig, (uint64_t)curr_time); return KNOT_EOK; } @@ -612,6 +608,10 @@ const char* tsig_alg_to_str(tsig_algorithm_t alg) size_t tsig_wire_maxsize(const knot_key_t* key) { + if (key == NULL) { + return 0; + } + size_t alg_name_size = strlen(tsig_alg_to_str(key->algorithm)) + 1; return knot_dname_size(key->name) + @@ -632,6 +632,10 @@ size_t tsig_wire_maxsize(const knot_key_t* key) size_t tsig_wire_actsize(const knot_rrset_t *tsig) { + if (tsig == NULL) { + return 0; + } + return knot_dname_size(knot_rrset_owner(tsig)) + sizeof(uint16_t) + /* TYPE */ sizeof(uint16_t) + /* CLASS */ @@ -648,3 +652,12 @@ size_t tsig_wire_actsize(const knot_rrset_t *tsig) tsig_rdata_other_data_length(tsig); } +int tsig_rdata_is_ok(const knot_rrset_t *tsig) +{ + return (tsig + && knot_rrset_rdata(tsig) != NULL + && knot_rdata_item_count(knot_rrset_rdata(tsig)) >= 7 + && tsig_rdata_alg_name(tsig) != NULL + && tsig_rdata_time_signed(tsig) != 0); +} + diff --git a/src/libknot/tsig.h b/src/libknot/tsig.h index 73fa832..249184d 100644..100755 --- a/src/libknot/tsig.h +++ b/src/libknot/tsig.h @@ -140,6 +140,8 @@ uint16_t tsig_alg_digest_length(tsig_algorithm_t alg); size_t tsig_wire_maxsize(const knot_key_t *key); size_t tsig_wire_actsize(const knot_rrset_t *tsig); +int tsig_rdata_is_ok(const knot_rrset_t *tsig); + #endif /* _KNOT_TSIG_H_ */ /*! @} */ diff --git a/src/libknot/updates/changesets.c b/src/libknot/updates/changesets.c index 1c3d3b9..502a858 100644..100755 --- a/src/libknot/updates/changesets.c +++ b/src/libknot/updates/changesets.c @@ -115,23 +115,20 @@ int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count, // try to find the RRSet in the list of RRSets, but search backwards // as it is probable that the last RRSet is the one to which the RR // belongs - int i = *count - 1; - - while (i >= 0 && !knot_changeset_rrsets_match((*rrsets)[i], rr)) { - --i; - } - if (i >= 0) { - // found RRSet to merge the new one into - if (knot_rrset_merge((void **)&(*rrsets)[i], - (void **)&rr) != KNOT_EOK) { + // Just check the last RRSet. If the RR belongs to it, merge it, + // otherwise just add the RR to the end of the list + + if (*count > 0 + && knot_changeset_rrsets_match((*rrsets)[*count - 1], rr)) { + // Create changesets exactly as they came, with possibly + // duplicate records + if (knot_rrset_merge((void **)&(*rrsets)[*count - 1], + (void **)&rr) != KNOT_EOK) { return KNOT_ERROR; } - // remove the RR - /*! \todo does this make sense? */ - knot_rrset_free(&rr); // used to be deep free with all 1's - + knot_rrset_free(&rr); return KNOT_EOK; } else { return knot_changeset_add_rrset(rrsets, count, allocated, rr); @@ -211,7 +208,7 @@ int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa, int knot_changesets_check_size(knot_changesets_t *changesets) { /* Check if allocated is sufficient. */ - if (changesets->count <= changesets->allocated) { + if (changesets->count < changesets->allocated) { return KNOT_EOK; } @@ -243,7 +240,6 @@ int knot_changesets_check_size(knot_changesets_t *changesets) void knot_free_changeset(knot_changeset_t **changeset) { - /* XXX XXX investigate wrong frees. */ assert((*changeset)->add_allocated >= (*changeset)->add_count); assert((*changeset)->remove_allocated >= (*changeset)->remove_count); assert((*changeset)->allocated >= (*changeset)->size); @@ -292,7 +288,3 @@ void knot_free_changesets(knot_changesets_t **changesets) free(*changesets); *changesets = NULL; } - -/*---------------------------------------------------------------------------*/ - - diff --git a/src/libknot/updates/changesets.h b/src/libknot/updates/changesets.h index 0570fc9..642b155 100644..100755 --- a/src/libknot/updates/changesets.h +++ b/src/libknot/updates/changesets.h @@ -5,7 +5,7 @@ * * \brief Structure for representing IXFR/DDNS changeset and its API. * - * \addtogroup libknot + * \addtogroup xfr * @{ */ /* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> @@ -90,9 +90,9 @@ typedef struct { int new_rdata_count; int new_rdata_allocated; -// /*! -// * Deleted (without contents) after successful update. -// */ + /*! + * Deleted (without contents) after successful update. + */ knot_node_t **old_nodes; int old_nodes_count; int old_nodes_allocated; diff --git a/src/libknot/updates/ddns.c b/src/libknot/updates/ddns.c index 025cd6b..905c44b 100644..100755 --- a/src/libknot/updates/ddns.c +++ b/src/libknot/updates/ddns.c @@ -97,7 +97,7 @@ static int knot_ddns_add_prereq_rrset(const knot_rrset_t *rrset, } knot_rrset_t *new_rrset = NULL; - ret = knot_rrset_deep_copy(rrset, &new_rrset); + ret = knot_rrset_deep_copy(rrset, &new_rrset, 0); if (ret != KNOT_EOK) { return ret; } @@ -202,7 +202,7 @@ static int knot_ddns_add_update(knot_changeset_t *changeset, * copy. */ knot_rrset_t *rrset_copy; - ret = knot_rrset_deep_copy(rrset, &rrset_copy); + ret = knot_rrset_deep_copy(rrset, &rrset_copy, 0); if (ret != KNOT_EOK) { return ret; } diff --git a/src/libknot/updates/ddns.h b/src/libknot/updates/ddns.h index dceebed..35dfcb7 100644..100755 --- a/src/libknot/updates/ddns.h +++ b/src/libknot/updates/ddns.h @@ -5,7 +5,7 @@ * * \brief Dynamic updates processing. * - * \addtogroup query_processing + * \addtogroup ddns * @{ */ /* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c index ea8178d..c870ece 100644..100755 --- a/src/libknot/updates/xfr-in.c +++ b/src/libknot/updates/xfr-in.c @@ -102,7 +102,7 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype, if (wire_size > *size) { dbg_xfrin("Not enough space provided for the wire " - "format of the query.\n"); + "format of the query.\n"); knot_packet_free(&pkt); return KNOT_ESPACE; } @@ -112,7 +112,7 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype, char *name = knot_dname_to_str(xfr->tsig_key->name); dbg_xfrin_detail("Signing XFR query with key (name %s): \n", name); - free(name); + free(name); dbg_xfrin_hex_detail(xfr->tsig_key->secret, xfr->tsig_key->secret_size); @@ -152,9 +152,9 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype, int xfrin_create_soa_query(knot_dname_t *owner, knot_ns_xfr_t *xfr, size_t *size) { - /*! \todo [TSIG] Should TSIG apply for SOA query too? */ return xfrin_create_query(owner, KNOT_RRTYPE_SOA, - KNOT_CLASS_IN, xfr, size, 0, 0); + KNOT_CLASS_IN, xfr, size, 0, + xfr->tsig_key != NULL); } /*----------------------------------------------------------------------------*/ @@ -164,8 +164,8 @@ int xfrin_transfer_needed(const knot_zone_contents_t *zone, { // first, parse the rest of the packet assert(!knot_packet_is_query(soa_response)); - dbg_xfrin("Response - parsed: %zu, total wire size: %zu\n", - soa_response->parsed, soa_response->size); + dbg_xfrin_verb("Response - parsed: %zu, total wire size: %zu\n", + soa_response->parsed, soa_response->size); int ret; if (soa_response->parsed < soa_response->size) { @@ -199,7 +199,7 @@ dbg_xfrin_exec( dbg_xfrin("Malformed data in SOA of zone %s\n", name); free(name); ); - return KNOT_EMALF; // maybe some other error + return KNOT_EMALF; // maybe some other error } /* @@ -273,8 +273,8 @@ static int xfrin_add_orphan_rrsig(xfrin_orphan_rrsig_t **rrsigs, && knot_rdata_rrsig_type_covered(knot_rrset_rdata( last->rrsig)) == knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr))) { - ret = knot_rrset_merge((void **)&last->rrsig, - (void **)&rr); + ret = knot_rrset_merge_no_dupl((void **)&last->rrsig, + (void **)&rr); if (ret != KNOT_EOK) { return ret; } else { @@ -341,7 +341,7 @@ void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs) } /*----------------------------------------------------------------------------*/ -/*! \note [TSIG] */ + static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr, int tsig_req) { @@ -400,8 +400,8 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr, } if (ret != KNOT_EOK) { - /*! \note [TSIG] No need to check TSIG error - * here, propagate and check elsewhere.*/ + /* No need to check TSIG error + * here, propagate and check elsewhere.*/ knot_rrset_deep_free(&tsig, 1, 1, 1); return ret; } @@ -425,9 +425,7 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr, tsig_rdata_time_signed(tsig); - }/* else { // TSIG not required and not there - - }*/ + } } else if (tsig != NULL) { // TSIG where it should not be knot_rrset_deep_free(&tsig, 1, 1, 1); @@ -441,9 +439,7 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr, /*----------------------------------------------------------------------------*/ -int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size, - xfrin_constructed_zone_t **constr*/ - knot_ns_xfr_t *xfr) +int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr) { const uint8_t *pkt = xfr->wire; size_t size = xfr->wire_size; @@ -451,11 +447,10 @@ int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size, (xfrin_constructed_zone_t **)(&xfr->data); if (pkt == NULL || constr == NULL) { - dbg_xfrin("Wrong parameters supported.\n"); return KNOT_EBADARG; } - dbg_xfrin("Processing AXFR packet of size %zu.\n", size); + dbg_xfrin_verb("Processing AXFR packet of size %zu.\n", size); // check if the response is OK if (knot_wire_get_rcode(pkt) != KNOT_RCODE_NOERROR) { @@ -473,8 +468,7 @@ int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size, int ret = knot_packet_parse_from_wire(packet, pkt, size, 1); if (ret != KNOT_EOK) { - dbg_xfrin("Could not parse packet: %s.\n", - knot_strerror(ret)); + dbg_xfrin("Could not parse packet: %s.\n", knot_strerror(ret)); knot_packet_free(&packet); /*! \todo Cleanup. */ return KNOT_EMALF; @@ -510,18 +504,16 @@ int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size, if (*constr == NULL) { // this should be the first packet - /*! \note [TSIG] Packet number for checking TSIG validation. */ + /*! Packet number for checking TSIG validation. */ xfr->packet_nr = 0; -// /*! \note [TSIG] Storing total size of data for TSIG digest. */ -// xfr->tsig_data_size = 0; // create new zone /*! \todo Ensure that the packet is the first one. */ if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) { dbg_xfrin("No zone created, but the first RR in " - "Answer is not a SOA RR.\n"); + "Answer is not a SOA RR.\n"); knot_packet_free(&packet); - knot_node_free(&node, 0); + knot_node_free(&node); knot_rrset_deep_free(&rr, 1, 1, 1); /*! \todo Cleanup. */ return KNOT_EMALF; @@ -543,7 +535,7 @@ dbg_xfrin_exec( ); /*! \todo Cleanup. */ knot_packet_free(&packet); - knot_node_free(&node, 0); + knot_node_free(&node); knot_rrset_deep_free(&rr, 1, 1, 1); return KNOT_EMALF; } @@ -564,19 +556,20 @@ dbg_xfrin_exec( if (*constr == NULL) { dbg_xfrin("Failed to create new constr. zone.\n"); knot_packet_free(&packet); - knot_node_free(&node, 0); + knot_node_free(&node); knot_rrset_deep_free(&rr, 1, 1, 1); return KNOT_ENOMEM; } memset(*constr, 0, sizeof(xfrin_constructed_zone_t)); - (*constr)->contents = knot_zone_contents_new(node, 0, 1, NULL); -// assert(0); + dbg_xfrin_verb("Creating new zone contents.\n"); + (*constr)->contents = knot_zone_contents_new(node, 0, 1, + xfr->zone); if ((*constr)->contents== NULL) { dbg_xfrin("Failed to create new zone.\n"); knot_packet_free(&packet); - knot_node_free(&node, 0); + knot_node_free(&node); knot_rrset_deep_free(&rr, 1, 1, 1); /*! \todo Cleanup. */ return KNOT_ENOMEM; @@ -588,21 +581,19 @@ dbg_xfrin_exec( assert(zone->apex == node); assert(zone->apex->owner == rr->owner); // add the RRSet to the node - //ret = knot_node_add_rrset(node, rr, 0); ret = knot_zone_contents_add_rrset(zone, rr, &node, KNOT_RRSET_DUPL_MERGE, 1); if (ret < 0) { dbg_xfrin("Failed to add RRSet to zone node: %s.\n", knot_strerror(ret)); knot_packet_free(&packet); - knot_node_free(&node, 0); + knot_node_free(&node); knot_rrset_deep_free(&rr, 1, 1, 1); /*! \todo Cleanup. */ return KNOT_ERROR; } else if (ret > 0) { dbg_xfrin("Merged SOA RRSet.\n"); // merged, free the RRSet - //knot_rrset_deep_free(&rr, 1, 0, 0); knot_rrset_free(&rr); } @@ -613,20 +604,6 @@ dbg_xfrin_exec( ++xfr->packet_nr; } - /*! \note [TSIG] add the packet wire size to the data to be verified by - * TSIG - */ -// if (xfr->tsig_key) { -// dbg_xfrin("Adding packet wire to TSIG data (size till now: %zu," -// " adding: %zu).\n", xfr->tsig_data_size, -// xfr->wire_size); -// assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size -// >= xfr->wire_size); -// memcpy(xfr->tsig_data + xfr->tsig_data_size, xfr->wire, -// xfr->wire_size); -// xfr->tsig_data_size += xfr->wire_size; -// } - assert(zone != NULL); while (ret == KNOT_EOK && rr != NULL) { @@ -637,9 +614,9 @@ dbg_xfrin_exec( if (node != NULL && knot_dname_compare(rr->owner, node->owner) != 0) { -dbg_xfrin_exec( +dbg_xfrin_exec_detail( char *name = knot_dname_to_str(node->owner); - dbg_xfrin("Node owner: %s\n", name); + dbg_xfrin_detail("Node owner: %s\n", name); free(name); ); if (!in_zone) { @@ -659,16 +636,15 @@ dbg_xfrin_exec( assert(knot_zone_contents_apex((zone)) != NULL); assert(knot_node_rrset(knot_zone_contents_apex((zone)), KNOT_RRTYPE_SOA) != NULL); - dbg_xfrin("Found last SOA, transfer finished.\n"); + dbg_xfrin_verb("Found last SOA, transfer finished.\n"); - dbg_xfrin("Verifying TSIG...\n"); + dbg_xfrin_verb("Verifying TSIG...\n"); /*! \note [TSIG] Now check if there is not a TSIG record * at the end of the packet. */ ret = xfrin_check_tsig(packet, xfr, 1); - dbg_xfrin_detail("xfrin_check_tsig() returned %d\n", - ret); + dbg_xfrin_verb("xfrin_check_tsig() returned %d\n", ret); knot_packet_free(&packet); knot_rrset_deep_free(&rr, 1, 1, 1); @@ -682,8 +658,7 @@ dbg_xfrin_exec( ret = xfrin_process_orphan_rrsigs(zone, (*constr)->rrsigs); if (ret != KNOT_EOK) { - dbg_xfrin("Failed to process orphan " - "RRSIGs\n"); + dbg_xfrin("Failed to process orphan RRSIGs\n"); /*! \todo Cleanup?? */ return ret; } @@ -700,8 +675,8 @@ dbg_xfrin_exec( ret = knot_zone_contents_add_rrsigs(zone, rr, &tmp_rrset, &node, KNOT_RRSET_DUPL_MERGE, 1); if (ret == KNOT_ENONODE || ret == KNOT_ENORRSET) { - dbg_xfrin("No node or RRSet for RRSIGs\n"); - dbg_xfrin("Saving for later insertion.\n"); + dbg_xfrin_verb("No node or RRSet for RRSIGs\n"); + dbg_xfrin_verb("Saving for later insertion.\n"); if (ret == KNOT_ENORRSET) { in_zone = 1; @@ -711,13 +686,13 @@ dbg_xfrin_exec( rr); if (ret > 0) { - dbg_xfrin("Merged RRSIGs.\n"); + dbg_xfrin_detail("Merged RRSIGs.\n"); knot_rrset_free(&rr); } else if (ret != KNOT_EOK) { dbg_xfrin("Failed to save orphan" - " RRSIGs.\n"); + " RRSIGs.\n"); knot_packet_free(&packet); - knot_node_free(&node, 0); // ??? + knot_node_free(&node); // ??? knot_rrset_deep_free(&rr, 1, 1, 1); return ret; } @@ -725,15 +700,15 @@ dbg_xfrin_exec( dbg_xfrin("Failed to add RRSIGs (%s).\n", knot_strerror(ret)); knot_packet_free(&packet); - knot_node_free(&node, 0); // ??? + knot_node_free(&node); // ??? knot_rrset_deep_free(&rr, 1, 1, 1); return KNOT_ERROR; /*! \todo Other error code. */ } else if (ret == 1) { assert(node != NULL); dbg_xfrin_exec_verb( char *name = knot_dname_to_str(node->owner); - dbg_xfrin("Found node for the record in " - "zone: %s. Merged.\n", name); + dbg_xfrin_detail("Found node for the record in " + "zone: %s. Merged.\n", name); free(name); ); in_zone = 1; @@ -741,13 +716,12 @@ dbg_xfrin_exec_verb( } else if (ret == 2) { // should not happen assert(0); -// knot_rrset_deep_free(&rr, 1, 1, 1); } else { assert(node != NULL); dbg_xfrin_exec_verb( char *name = knot_dname_to_str(node->owner); - dbg_xfrin("Found node for the record in " - "zone: %s.\n", name); + dbg_xfrin_detail("Found node for the record in " + "zone: %s.\n", name); free(name); ); in_zone = 1; @@ -760,12 +734,12 @@ dbg_xfrin_exec_verb( continue; } - /*! \note [TSIG] TSIG where it should not be - in Answer section.*/ + /* TSIG where it should not be - in Answer section.*/ if (knot_rrset_type(rr) == KNOT_RRTYPE_TSIG) { // not allowed here - dbg_xfrin(" in Answer section.\n"); + dbg_xfrin("TSIG in Answer section.\n"); knot_packet_free(&packet); - knot_node_free(&node, 0); // ??? + knot_node_free(&node); // ??? knot_rrset_deep_free(&rr, 1, 1, 1); return KNOT_EMALF; } @@ -786,7 +760,7 @@ dbg_xfrin_exec_verb( if (node == NULL && (node = get_node(zone, knot_rrset_owner(rr))) != NULL) { // the node for this RR was found in the zone - dbg_xfrin("Found node for the record in zone.\n"); + dbg_xfrin_detail("Found node for the record in zone\n"); in_zone = 1; } @@ -800,31 +774,30 @@ dbg_xfrin_exec_verb( knot_rrset_deep_free(&rr, 1, 1, 1); return KNOT_ENOMEM; } - dbg_xfrin("Created new node for the record.\n"); + dbg_xfrin_detail("Created new node for the record.\n"); // insert the RRSet to the node - ret = knot_node_add_rrset(node, rr, 1); + ret = knot_node_add_rrset_no_dupl(node, rr); if (ret < 0) { - dbg_xfrin("Failed to add RRSet to node (%s" - ")\n", knot_strerror(ret)); + dbg_xfrin("Failed to add RRSet to node (%s)\n", + knot_strerror(ret)); knot_packet_free(&packet); - knot_node_free(&node, 0); // ??? + knot_node_free(&node); // ??? knot_rrset_deep_free(&rr, 1, 1, 1); return KNOT_ERROR; } else if (ret > 0) { // should not happen, this is new node assert(0); -// knot_rrset_deep_free(&rr, 1, 0, 0); } // insert the node into the zone ret = add_node(zone, node, 1, 0, 1); assert(node != NULL); if (ret != KNOT_EOK) { - dbg_xfrin("Failed to add node to zone (%s)" - ".\n", knot_strerror(ret)); + dbg_xfrin("Failed to add node to zone (%s).\n", + knot_strerror(ret)); knot_packet_free(&packet); - knot_node_free(&node, 0); // ??? + knot_node_free(&node); // ??? knot_rrset_deep_free(&rr, 1, 1, 1); return KNOT_ERROR; } @@ -837,12 +810,11 @@ dbg_xfrin_exec_verb( KNOT_RRSET_DUPL_MERGE, 1); if (ret < 0) { knot_packet_free(&packet); - dbg_xfrin("Failed to add RRSet to zone:" - "%s.\n", knot_strerror(ret)); + dbg_xfrin("Failed to add RRSet to zone :%s.\n", + knot_strerror(ret)); return KNOT_ERROR; } else if (ret > 0) { // merged, free the RRSet -// knot_rrset_deep_free(&rr, 1, 0, 0); knot_rrset_free(&rr); } @@ -858,12 +830,11 @@ dbg_xfrin_exec_verb( if (ret < 0) { // some error in parsing - dbg_xfrin("Could not parse next RR: %s.\n", - knot_strerror(ret)); + dbg_xfrin("Could not parse next RR: %s.\n", knot_strerror(ret)); knot_packet_free(&packet); if (!in_zone) { - knot_node_free(&node, 0); + knot_node_free(&node); } knot_rrset_deep_free(&rr, 1, 1, 1); @@ -878,25 +849,23 @@ dbg_xfrin_exec_verb( assert(node != NULL); ret = knot_zone_contents_add_node(zone, node, 1, 0, 1); if (ret != KNOT_EOK) { - dbg_xfrin("Failed to add last node into zone (%s)" - ".\n", knot_strerror(ret)); + dbg_xfrin("Failed to add last node into zone (%s).\n", + knot_strerror(ret)); knot_packet_free(&packet); - knot_node_free(&node, 0); + knot_node_free(&node); return KNOT_ERROR; /*! \todo Other error */ } } - /*! \note [TSIG] Now check if there is not a TSIG record at the end of - * the packet. - */ + /* Now check if there is not a TSIG record at the end of the packet. */ ret = xfrin_check_tsig(packet, xfr, knot_ns_tsig_required(xfr->packet_nr)); ++xfr->packet_nr; knot_packet_free(&packet); - dbg_xfrin("Processed one AXFR packet successfully.\n"); + dbg_xfrin_verb("Processed one AXFR packet successfully.\n"); - /*! \note [TSIG] TSIG errors are propagated and reported in a standard + /* TSIG errors are propagated and reported in a standard * manner, as we're in response processing, no further error response * should be sent. */ @@ -920,8 +889,7 @@ static int xfrin_parse_first_rr(knot_packet_t **packet, const uint8_t *pkt, int ret = knot_packet_parse_from_wire(*packet, pkt, size, 1); if (ret != KNOT_EOK) { - dbg_xfrin("Could not parse packet: %s.\n", - knot_strerror(ret)); + dbg_xfrin("Could not parse packet: %s.\n", knot_strerror(ret)); knot_packet_free(packet); return KNOT_EMALF; } @@ -986,7 +954,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr) } if (*chs == NULL) { - dbg_xfrin("Changesets empty, creating new.\n"); + dbg_xfrin_verb("Changesets empty, creating new.\n"); ret = knot_changeset_allocate(chs); if (ret != KNOT_EOK) { @@ -1007,7 +975,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr) (*chs)->first_soa = rr; state = -1; - dbg_xfrin("First SOA of IXFR saved, state set to -1.\n"); + dbg_xfrin_verb("First SOA of IXFR saved, state set to -1.\n"); // parse the next one ret = knot_packet_parse_next_rr_answer(packet, &rr); @@ -1050,7 +1018,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr) ret = KNOT_EBADARG; goto cleanup; } - dbg_xfrin("Changesets present.\n"); + dbg_xfrin_detail("Changesets present.\n"); } /* @@ -1090,7 +1058,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr) * calls to this function. */ if (state != -1) { - dbg_xfrin("State is not -1, deciding...\n"); + dbg_xfrin_detail("State is not -1, deciding...\n"); // there should be at least one started changeset right now if ((*chs)->count <= 0) { knot_rrset_deep_free(&rr, 1, 1, 1); @@ -1108,15 +1076,15 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr) } } - dbg_xfrin("State before the loop: %d\n", state); + dbg_xfrin_detail("State before the loop: %d\n", state); /*! \todo This may be implemented with much less IFs! */ while (ret == KNOT_EOK && rr != NULL) { dbg_xfrin_exec_verb( - dbg_xfrin_verb("Next loop, state: %d\n", state); + dbg_xfrin_detail("Next loop, state: %d\n", state); char *name = knot_dname_to_str(knot_rrset_owner(rr)); - dbg_xfrin_verb("Actual RR: %s, type %s.\n", name, + dbg_xfrin_detail("Actual RR: %s, type %s.\n", name, knot_rrtype_to_string(knot_rrset_type(rr))); free(name); ); @@ -1128,7 +1096,7 @@ dbg_xfrin_exec_verb( // is quite weird in fact if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) { dbg_xfrin("First RR is not a SOA RR!\n"); - dbg_xfrin("RR type: %s\n", + dbg_xfrin_verb("RR type: %s\n", knot_rrtype_to_string(knot_rrset_type(rr))); ret = KNOT_EMALF; knot_rrset_deep_free(&rr, 1, 1, 1); @@ -1222,15 +1190,12 @@ dbg_xfrin_exec_verb( } } break; - // dead code -// default: -// assert(0); } // parse the next RR - dbg_xfrin_verb("Parsing next RR..\n"); + dbg_xfrin_detail("Parsing next RR..\n"); ret = knot_packet_parse_next_rr_answer(packet, &rr); - dbg_xfrin_verb("Returned %d, %p.\n", ret, rr); + dbg_xfrin_detail("Returned %d, %p.\n", ret, rr); } /*! \note Check TSIG, we're at the end of packet. It may not be @@ -1238,7 +1203,7 @@ dbg_xfrin_exec_verb( */ ret = xfrin_check_tsig(packet, xfr, knot_ns_tsig_required(xfr->packet_nr)); - dbg_xfrin_detail("xfrin_check_tsig() returned %d\n", ret); + dbg_xfrin_verb("xfrin_check_tsig() returned %d\n", ret); ++xfr->packet_nr; /*! \note [TSIG] Cleanup and propagate error if TSIG validation fails.*/ @@ -1255,7 +1220,7 @@ cleanup: /* We should go here only if some error occured. */ assert(ret < 0); - dbg_xfrin("Cleanup after processing IXFR/IN packet.\n"); + dbg_xfrin_detail("Cleanup after processing IXFR/IN packet.\n"); knot_free_changesets(chs); knot_packet_free(&packet); xfr->data = 0; @@ -1337,6 +1302,9 @@ static int xfrin_changes_check_rdata(knot_rdata_t ***rdatas, uint **types, } int new_count = (*allocated == 0) ? 2 : *allocated * 2; + while (new_count < count + to_add) { + new_count *= 2; + } /* Allocate new memory block. */ knot_rdata_t **rdatas_new = malloc(new_count * sizeof(knot_rdata_t *)); @@ -1372,36 +1340,21 @@ static int xfrin_changes_check_rdata(knot_rdata_t ***rdatas, uint **types, static void xfrin_changes_add_rdata(knot_rdata_t **rdatas, uint *types, int *count, knot_rdata_t *rdata, uint type) { - dbg_xfrin_detail("Adding RDATA to RDATA list: %p\n", rdata); - if (rdata == NULL) { return; } -//dbg_xfrin_exec_detail( -// // try to find the first RDATA in the given list -// for (int i = 0; i < *count; ++i) { -// knot_rdata_t *r = rdatas[i]; -// if (r == NULL) { -// continue; -// } -// while (r != NULL && r->next != rdatas[i]) { -// if (r == rdata) { -// dbg_xfrin_detail("Found same RDATA: %p\n", rdata); -// knot_rdata_dump(rdata, type, 0); -// } -// r = r->next; -// } -// if (r == rdata) { -// dbg_xfrin_detail("Found same RDATA: %p\n", rdata); -// knot_rdata_dump(rdata, type, 0); -// } -// } -//); + // Add all RDATAs from the chain!! - rdatas[*count] = rdata; - types[*count] = type; - ++*count; + knot_rdata_t *r = rdata; + do { + dbg_xfrin_detail("Adding RDATA to RDATA list: %p\n", r); + rdatas[*count] = r; + types[*count] = type; + ++*count; + + r = r->next; + } while (r != NULL && r != rdata); } /*----------------------------------------------------------------------------*/ @@ -1412,7 +1365,6 @@ static void xfrin_zone_contents_free(knot_zone_contents_t **contents) if ((*contents)->table != NULL) { ck_destroy_table(&(*contents)->table, NULL, 0); -// ck_table_free(&(*contents)->table); } // free the zone tree with nodes @@ -1459,10 +1411,9 @@ static knot_rdata_t *xfrin_remove_rdata(knot_rrset_t *from, static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, knot_changes_t *changes) { - dbg_xfrin_verb("Copying old RRSet: %p\n", old); + dbg_xfrin_detail("Copying old RRSet: %p\n", old); // create new RRSet by copying the old one -// int ret = knot_rrset_shallow_copy(old, copy); - int ret = knot_rrset_deep_copy(old, copy); + int ret = knot_rrset_deep_copy(old, copy, 0); if (ret != KNOT_EOK) { dbg_xfrin("Failed to create RRSet copy.\n"); return KNOT_ENOMEM; @@ -1479,11 +1430,14 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, return ret; } + int count = knot_rrset_rdata_rr_count(*copy); + count += knot_rrset_rdata_rr_count((*copy)->rrsigs); + // add the copied RDATA to the list of new RDATA ret = xfrin_changes_check_rdata(&changes->new_rdata, &changes->new_rdata_types, changes->new_rdata_count, - &changes->new_rdata_allocated, 2); + &changes->new_rdata_allocated, count); if (ret != KNOT_EOK) { dbg_xfrin("Failed to add new RRSet to list.\n"); knot_rrset_deep_free(copy, 1, 1, 1); @@ -1492,7 +1446,8 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, changes->new_rrsets[changes->new_rrsets_count++] = *copy; - dbg_xfrin_verb("Adding RDATA from the RRSet copy to new RDATA list.\n"); + dbg_xfrin_detail("Adding RDATA from the RRSet copy to new RDATA list." + "\n"); xfrin_changes_add_rdata(changes->new_rdata, changes->new_rdata_types, &changes->new_rdata_count, knot_rrset_get_rdata(*copy), @@ -1502,8 +1457,9 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, assert(old->rrsigs != NULL); changes->new_rrsets[changes->new_rrsets_count++] = (*copy)->rrsigs; - dbg_xfrin_verb("Adding RDATA from RRSIG of the RRSet copy to " - "new RDATA list.\n"); + + dbg_xfrin_detail("Adding RDATA from RRSIG of the RRSet copy to " + "new RDATA list.\n"); xfrin_changes_add_rdata(changes->new_rdata, changes->new_rdata_types, &changes->new_rdata_count, @@ -1520,11 +1476,14 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, return ret; } + count = knot_rrset_rdata_rr_count(old); + count += knot_rrset_rdata_rr_count(old->rrsigs); + // and old RDATA to the list of old RDATA ret = xfrin_changes_check_rdata(&changes->old_rdata, &changes->old_rdata_types, changes->old_rdata_count, - &changes->old_rdata_allocated, 2); + &changes->old_rdata_allocated, count); if (ret != KNOT_EOK) { dbg_xfrin("Failed to add old RRSet to list.\n"); return ret; @@ -1532,7 +1491,7 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, changes->old_rrsets[changes->old_rrsets_count++] = old; - dbg_xfrin_verb("Adding RDATA from old RRSet to old RDATA list.\n"); + dbg_xfrin_detail("Adding RDATA from old RRSet to old RDATA list.\n"); xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, &changes->old_rdata_count, old->rdata, knot_rrset_type(old)); @@ -1541,8 +1500,8 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, assert(old->rrsigs != NULL); changes->old_rrsets[changes->old_rrsets_count++] = old->rrsigs; - dbg_xfrin_verb("Adding RDATA from RRSIG of the old RRSet to " - "old RDATA list.\n"); + dbg_xfrin_detail("Adding RDATA from RRSIG of the old RRSet to " + "old RDATA list.\n"); xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, &changes->old_rdata_count, @@ -1558,15 +1517,15 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, static int xfrin_copy_rrset(knot_node_t *node, knot_rr_type_t type, knot_rrset_t **rrset, knot_changes_t *changes) { -dbg_xfrin_exec_verb( +dbg_xfrin_exec_detail( char *name = knot_dname_to_str(knot_node_owner(node)); - dbg_xfrin_verb("Removing RRSet of type %s from node %s (%p)\n", + dbg_xfrin_detail("Removing RRSet of type %s from node %s (%p)\n", knot_rrtype_to_string(type), name, node); free(name); ); knot_rrset_t *old = knot_node_remove_rrset(node, type); - dbg_xfrin_verb("Removed RRSet: %p\n", old); + dbg_xfrin_detail("Removed RRSet: %p\n", old); dbg_xfrin_detail("Other RRSet of the same type in the node: %p\n", knot_node_rrset(node, type)); @@ -1580,7 +1539,7 @@ dbg_xfrin_exec_verb( return ret; } - dbg_xfrin_verb("Copied old rrset %p to new %p.\n", old, *rrset); + dbg_xfrin_detail("Copied old rrset %p to new %p.\n", old, *rrset); // replace the RRSet in the node copy by the new one ret = knot_node_add_rrset(node, *rrset, 0); @@ -1597,7 +1556,8 @@ dbg_xfrin_exec_verb( static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, const knot_rrset_t *remove, knot_node_t *node, - knot_rrset_t **rrset) + knot_rrset_t **rrset, + knot_rrset_t **rrsigs_old) { assert(changes != NULL); assert(remove != NULL); @@ -1613,37 +1573,35 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, int copied = 0; - if (!*rrset - || knot_dname_compare(knot_rrset_owner(*rrset), - knot_node_owner(node)) != 0 - || knot_rrset_type(*rrset) != knot_rdata_rrsig_type_covered( - knot_rrset_rdata(remove))) { + if (*rrset + && knot_dname_compare(knot_rrset_owner(*rrset), + knot_node_owner(node)) == 0 + && knot_rrset_type(*rrset) == knot_rdata_rrsig_type_covered( + knot_rrset_rdata(remove))) { + // this RRSet should be the already copied RRSet so we may + // update it right away + /*! \todo Does this case even occur? */ + dbg_xfrin_verb("Using RRSet from previous iteration.\n"); + } else { // find RRSet based on the Type Covered knot_rr_type_t type = knot_rdata_rrsig_type_covered( knot_rrset_rdata(remove)); // copy the rrset - dbg_xfrin_verb("Copying RRSet that carries the RRSIGs.\n"); + dbg_xfrin_detail("Copying RRSet that carries the RRSIGs.\n"); ret = xfrin_copy_rrset(node, type, rrset, changes); if (ret != KNOT_EOK) { dbg_xfrin("Failed to copy rrset from changeset.\n"); return ret; } + dbg_xfrin_detail("Copied RRSet:\n"); + knot_rrset_dump(*rrset, 0); copied = 1; - } else { - // we should have the right RRSIG RRSet in *rrset - assert(knot_rrset_type(*rrset) - == knot_rdata_rrsig_type_covered( - knot_rrset_rdata(remove))); - // this RRSet should be the already copied RRSet so we may - // update it right away - /*! \todo Does this case even occur? */ - dbg_xfrin_verb("Using RRSet from previous iteration.\n"); } - + // get the old rrsigs knot_rrset_t *old = knot_rrset_get_rrsigs(*rrset); - dbg_xfrin_verb("Old RRSIGs from RRSet: %p\n", old); + dbg_xfrin_detail("Old RRSIGs from RRSet: %p\n", old); if (old == NULL) { return 1; } @@ -1651,14 +1609,22 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, // copy the RRSIGs knot_rrset_t *rrsigs = NULL; if (!copied) { - ret = xfrin_copy_old_rrset(old, &rrsigs, changes); - if (ret != KNOT_EOK) { - return ret; + // check if the stored RRSIGs are not the right ones + if (*rrsigs_old && *rrsigs_old == (*rrset)->rrsigs) { + dbg_xfrin_verb("Using RRSIG from previous iteration\n"); + rrsigs = *rrsigs_old; + } else { + ret = xfrin_copy_old_rrset(old, &rrsigs, changes); + if (ret != KNOT_EOK) { + return ret; + } + dbg_xfrin_detail("Copied RRSIGs: %p\n", rrsigs); + dbg_xfrin_detail("Copied RRSet:\n"); + knot_rrset_dump(rrsigs, 0); } - dbg_xfrin_verb("Copied RRSIGs: %p\n", rrsigs); } else { rrsigs = old; - dbg_xfrin_verb("Using old RRSIGs: %p\n", rrsigs); + dbg_xfrin_detail("Using old RRSIGs: %p\n", rrsigs); } // set the RRSIGs to the new RRSet copy @@ -1667,21 +1633,25 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, return KNOT_ERROR; } + *rrsigs_old = rrsigs; + // now in '*rrset' we have a copy of the RRSet which holds the RRSIGs // and in 'rrsigs' we have the copy of the RRSIGs knot_rdata_t *rdata = xfrin_remove_rdata(rrsigs, remove); if (rdata == NULL) { dbg_xfrin("Failed to remove RDATA from RRSet: %s.\n", - knot_strerror(ret)); + knot_strerror(ret)); return 1; } + + int count = knot_rdata_count(rdata); // connect the RDATA to the list of old RDATA ret = xfrin_changes_check_rdata(&changes->old_rdata, &changes->old_rdata_types, changes->old_rdata_count, - &changes->old_rdata_allocated, 1); + &changes->old_rdata_allocated, count); if (ret != KNOT_EOK) { return ret; } @@ -1704,7 +1674,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, 1); if (ret != KNOT_EOK) { dbg_xfrin("Failed to add empty RRSet to the " - "list of old RRSets."); + "list of old RRSets."); // delete the RRSet right away knot_rrset_free(&rrsigs); return ret; @@ -1729,7 +1699,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, 1); if (ret != KNOT_EOK) { dbg_xfrin("Failed to add empty RRSet to " - "the list of old RRSets."); + "the list of old RRSets."); // delete the RRSet right away knot_rrset_free(rrset); return ret; @@ -1762,10 +1732,13 @@ static int xfrin_apply_remove_normal(knot_changes_t *changes, // now we have the copy of the node, so lets get the right RRSet // check if we do not already have it - if (!*rrset - || knot_dname_compare(knot_rrset_owner(*rrset), - knot_node_owner(node)) != 0 - || knot_rrset_type(*rrset) != knot_rrset_type(remove)) { + if (*rrset + && knot_dname_compare(knot_rrset_owner(*rrset), + knot_node_owner(node)) == 0 + && knot_rrset_type(*rrset) == knot_rrset_type(remove)) { + /*! \todo Does some other case even occur? */ + dbg_xfrin_verb("Using RRSet from previous loop.\n"); + } else { /*! * \todo This may happen also with already * copied RRSet. In that case it would be @@ -1774,30 +1747,29 @@ static int xfrin_apply_remove_normal(knot_changes_t *changes, */ ret = xfrin_copy_rrset(node, knot_rrset_type(remove), rrset, changes); - dbg_xfrin_detail("Copied RRSet:\n"); - knot_rrset_dump(*rrset, 0); if (ret != KNOT_EOK) { return ret; } - } /*! \todo Does some other case even occur? */ + dbg_xfrin_detail("Copied RRSet:\n"); + knot_rrset_dump(*rrset, 0); + } if (*rrset == NULL) { dbg_xfrin_verb("RRSet not found for RR to be removed.\n"); return 1; } -dbg_xfrin_exec_verb( +dbg_xfrin_exec_detail( char *name = knot_dname_to_str(knot_rrset_owner(*rrset)); - dbg_xfrin("Updating RRSet with owner %s, type %s\n", name, - knot_rrtype_to_string(knot_rrset_type(*rrset))); + dbg_xfrin_detail("Updating RRSet with owner %s, type %s\n", name, + knot_rrtype_to_string(knot_rrset_type(*rrset))); free(name); ); - // remove the specified RRs from the RRSet (de facto difference of - // sets) + // remove the specified RRs from the RRSet (de facto difference of sets) knot_rdata_t *rdata = xfrin_remove_rdata(*rrset, remove); if (rdata == NULL) { - dbg_xfrin("Failed to remove RDATA from RRSet\n"); + dbg_xfrin_verb("Failed to remove RDATA from RRSet\n"); // In this case, the RDATA was not found in the RRSet return 1; } @@ -1814,11 +1786,12 @@ dbg_xfrin_exec_detail( } ); + int count = knot_rdata_count(rdata); // connect the RDATA to the list of old RDATA ret = xfrin_changes_check_rdata(&changes->old_rdata, &changes->old_rdata_types, changes->old_rdata_count, - &changes->old_rdata_allocated, 1); + &changes->old_rdata_allocated, count); if (ret != KNOT_EOK) { return ret; } @@ -1859,7 +1832,7 @@ dbg_xfrin_exec_detail( } /*----------------------------------------------------------------------------*/ - +/*! \todo Needs review - RRs may not be merged into RRSets. */ static int xfrin_apply_remove_all_rrsets(knot_changes_t *changes, knot_node_t *node, uint16_t type) { @@ -1918,9 +1891,6 @@ static knot_node_t *xfrin_add_new_node(knot_zone_contents_t *contents, // insert the node into zone structures and create parents if // necessary -// dbg_xfrin("Adding new node to zone. From owner: %s type %s\n", -// knot_dname_to_str(node->owner), -// knot_rrtype_to_string(rrset->type)); if (is_nsec3) { ret = knot_zone_contents_add_nsec3_node(contents, node, 1, 0, 1); @@ -1959,35 +1929,53 @@ static int xfrin_apply_add_normal(knot_changes_t *changes, int ret; - dbg_xfrin_verb("applying rrset:\n"); +dbg_xfrin_exec_detail( + dbg_xfrin_detail("applying rrset:\n"); knot_rrset_dump(add, 0); +); + int copied = 0; /*! \note Reusing RRSet from previous function caused it not to be * removed from the node. * Maybe modification of the code would allow reusing the RRSet * as in apply_add_rrsigs() - the RRSet should not be copied * in such case. */ -// if (!*rrset -// || knot_dname_compare(knot_rrset_owner(*rrset), -// knot_node_owner(node)) != 0 -// || knot_rrset_type(*rrset) -// != knot_rrset_type(add)) { -// dbg_xfrin("Removing rrset!\n"); -// *rrset = knot_node_remove_rrset(node, knot_rrset_type(add)); -// } - - *rrset = knot_node_remove_rrset(node, knot_rrset_type(add)); - - dbg_xfrin_verb("Removed RRSet: \n"); + if (*rrset + && knot_dname_compare(knot_rrset_owner(*rrset), + knot_node_owner(node)) == 0 + && knot_rrset_type(*rrset) == knot_rrset_type(add)) { + dbg_xfrin_verb("Using RRSet from previous iteration.\n"); + } else { + dbg_xfrin_detail("Removing rrset!\n"); + *rrset = knot_node_remove_rrset(node, knot_rrset_type(add)); + + knot_rrset_t *old = *rrset; + + if (*rrset != NULL) { + ret = xfrin_copy_old_rrset(old, rrset, changes); + if (ret != KNOT_EOK) { + return ret; + } + + dbg_xfrin_detail("Copied RRSet: %p\n", *rrset); + dbg_xfrin_detail("Copied RRSet:\n"); + knot_rrset_dump(*rrset, 0); + copied = 1; + } + } + +dbg_xfrin_exec_detail( + dbg_xfrin_detail("Removed RRSet: \n"); knot_rrset_dump(*rrset, 1); +); if (*rrset == NULL) { -dbg_xfrin_exec_verb( +dbg_xfrin_exec_detail( char *name = knot_dname_to_str(add->owner); - dbg_xfrin_verb("RRSet to be added not found in zone.\n"); - dbg_xfrin_verb("owner: %s type: %s\n", name, - knot_rrtype_to_string(add->type)); + dbg_xfrin_detail("RRSet to be added not found in zone.\n"); + dbg_xfrin_detail("owner: %s type: %s\n", name, + knot_rrtype_to_string(add->type)); free(name); ); // add the RRSet from the changeset to the node @@ -2013,25 +2001,12 @@ dbg_xfrin_exec_verb( return 1; // return 1 to indicate the add RRSet was used } - knot_rrset_t *old = *rrset; - -dbg_xfrin_exec_verb( +dbg_xfrin_exec_detail( char *name = knot_dname_to_str(knot_rrset_owner(*rrset)); - dbg_xfrin("Found RRSet with owner %s, type %s\n", name, - knot_rrtype_to_string(knot_rrset_type(*rrset))); + dbg_xfrin_detail("Found RRSet with owner %s, type %s\n", name, + knot_rrtype_to_string(knot_rrset_type(*rrset))); free(name); ); -// knot_rrset_dump(*rrset, 1); - ret = xfrin_copy_old_rrset(old, rrset, changes); - if (ret != KNOT_EOK) { - return ret; - } - - dbg_xfrin_verb("Copied RRSet: %p\n", *rrset); - -// dbg_xfrin("After copy: Found RRSet with owner %s, type %s\n", -// knot_dname_to_str((*rrset)->owner), -// knot_rrtype_to_string(knot_rrset_type(*rrset))); // merge the changeset RRSet to the copy /* What if the update fails? @@ -2044,25 +2019,38 @@ dbg_xfrin_exec_verb( * * TODO: add the 'add' rrset to list of old RRSets? */ - dbg_xfrin_verb("Merging RRSets with owners: %s, %s types: %s, %s\n", - (*rrset)->owner->name, add->owner->name, - knot_rrtype_to_string((*rrset)->type), - knot_rrtype_to_string(add->type)); + dbg_xfrin_detail("Merging RRSets with owners: %s, %s types: %s, %s\n", + (*rrset)->owner->name, add->owner->name, + knot_rrtype_to_string((*rrset)->type), + knot_rrtype_to_string(add->type)); dbg_xfrin_detail("RDATA in RRSet1: %p, RDATA in RRSet2: %p\n", (*rrset)->rdata, add->rdata); - ret = knot_rrset_merge((void **)rrset, (void **)&add); + /* In case the RRSet is empty (and only remained there because of the + * RRSIGs) it may happen that the TTL may be different than that of + * the new RRs. Update the TTL according to the first RR. + */ + + if (knot_rrset_rdata(*rrset) == NULL + && knot_rrset_ttl(*rrset) != knot_rrset_ttl(add)) { + knot_rrset_set_ttl(*rrset, knot_rrset_ttl(add)); + } + + ret = knot_rrset_merge_no_dupl((void **)rrset, (void **)&add); if (ret != KNOT_EOK) { - dbg_xfrin("Failed to merge changeset RRSet to copy.\n"); - return KNOT_ERROR; + dbg_xfrin("Failed to merge changeset RRSet.\n"); + return ret; } - dbg_xfrin_verb("Merge returned: %d\n", ret); + dbg_xfrin_detail("Merge returned: %d\n", ret); knot_rrset_dump(*rrset, 1); - ret = knot_node_add_rrset(node, *rrset, 0); - if (ret < 0) { - dbg_xfrin("Failed to add merged RRSet to the node.\n"); - return ret; + if (copied) { + ret = knot_node_add_rrset(node, *rrset, 0); + + if (ret < 0) { + dbg_xfrin("Failed to add merged RRSet to the node.\n"); + return ret; + } } // return 2 so that the add RRSet is removed from @@ -2078,6 +2066,7 @@ static int xfrin_apply_add_rrsig(knot_changes_t *changes, knot_rrset_t *add, knot_node_t *node, knot_rrset_t **rrset, + knot_rrset_t **rrsigs_old, knot_zone_contents_t *contents) { assert(changes != NULL); @@ -2095,8 +2084,8 @@ static int xfrin_apply_add_rrsig(knot_changes_t *changes, dbg_xfrin_exec_verb( char *name = knot_dname_to_str(knot_rrset_owner(add)); const char *typestr = knot_rrtype_to_string(type); - dbg_xfrin("Adding RRSIG: Owner %s, type covered %s.\n", - name, typestr); + dbg_xfrin_verb("Adding RRSIG: Owner %s, type covered %s.\n", + name, typestr); free(name); ); @@ -2105,11 +2094,12 @@ dbg_xfrin_exec_verb( /*! \note Here the check is OK, because if we aready have the RRSet, * it's a copied one, so it is OK to modify it right away. */ - if (!*rrset - || knot_dname_compare(knot_rrset_owner(*rrset), - knot_node_owner(node)) != 0 - || knot_rrset_type(*rrset) != knot_rdata_rrsig_type_covered( - knot_rrset_rdata(add))) { + if (*rrset + && knot_dname_compare(knot_rrset_owner(*rrset), + knot_node_owner(node)) == 0 + && knot_rrset_type(*rrset) == type) { + dbg_xfrin_verb("Using RRSet from previous iteration.\n"); + } else { // copy the rrset ret = xfrin_copy_rrset(node, type, rrset, changes); if (ret < 0) { @@ -2118,15 +2108,12 @@ dbg_xfrin_exec_verb( *rrset = NULL; } copied = 1; - } else { - // we should have the right RRSIG RRSet in *rrset - assert(knot_rrset_type(*rrset) == type); - // this RRSet should be the already copied RRSet so we may - // update it right away + dbg_xfrin_detail("Copied RRSet:\n"); + knot_rrset_dump(*rrset, 0); } if (*rrset == NULL) { - dbg_xfrin_verb("RRSet to be added not found in zone.\n"); + dbg_xfrin_detail("RRSet to be added not found in zone.\n"); // create a new RRSet to add the RRSIGs into *rrset = knot_rrset_new(knot_node_get_owner(node), type, @@ -2136,7 +2123,7 @@ dbg_xfrin_exec_verb( dbg_xfrin("Failed to create new RRSet for RRSIGs.\n"); return KNOT_ENOMEM; } - dbg_xfrin_verb("Created new RRSet for RRSIG: %p.\n", *rrset); + dbg_xfrin_detail("Created new RRSet for RRSIG: %p.\n", *rrset); // add the RRset to the list of new RRsets ret = xfrin_changes_check_rrsets( @@ -2144,8 +2131,7 @@ dbg_xfrin_exec_verb( &changes->new_rrsets_count, &changes->new_rrsets_allocated, 1); if (ret != KNOT_EOK) { - dbg_xfrin("Failed to add old RRSet to " - "list.\n"); + dbg_xfrin("Failed to add old RRSet to list.\n"); knot_rrset_free(rrset); return ret; } @@ -2163,20 +2149,19 @@ dbg_xfrin_exec_verb( changes->new_rrsets[changes->new_rrsets_count++] = *rrset; } -dbg_xfrin_exec_verb( +dbg_xfrin_exec_detail( char *name = knot_dname_to_str(knot_rrset_owner(*rrset)); - dbg_xfrin("Found RRSet with owner %s, type %s\n", name, - knot_rrtype_to_string(knot_rrset_type(*rrset))); + dbg_xfrin_detail("Found RRSet with owner %s, type %s\n", name, + knot_rrtype_to_string(knot_rrset_type(*rrset))); free(name); ); if (knot_rrset_rrsigs(*rrset) == NULL) { - dbg_xfrin_verb("Adding new RRSIGs to RRSet.\n"); + dbg_xfrin_detail("Adding new RRSIGs to RRSet.\n"); ret = knot_zone_contents_add_rrsigs(contents, add, rrset, &node, KNOT_RRSET_DUPL_SKIP, 1); -// ret = knot_rrset_set_rrsigs(*rrset, add); if (ret < 0) { dbg_xfrin("Failed to add RRSIGs to the RRSet.\n"); return KNOT_ERROR; @@ -2191,20 +2176,30 @@ dbg_xfrin_exec_verb( knot_rrset_t *rrsig; if (!copied) { - ret = xfrin_copy_old_rrset(old, &rrsig, changes); - if (ret != KNOT_EOK) { - return ret; + // check if the stored RRSIGs are not the right ones + if (*rrsigs_old && *rrsigs_old == old) { + dbg_xfrin_verb("Using RRSIG from previous iteration\n"); + rrsig = *rrsigs_old; + } else { + ret = xfrin_copy_old_rrset(old, &rrsig, changes); + if (ret != KNOT_EOK) { + return ret; + } + dbg_xfrin_detail("Copied RRSIGs: %p\n", rrsig); + dbg_xfrin_detail("Copied RRSet:\n"); + knot_rrset_dump(rrsig, 0); } } else { rrsig = old; + dbg_xfrin_verb("Using old RRSIGs: %p\n", rrsig); } // replace the old RRSIGs with the new ones knot_rrset_set_rrsigs(*rrset, rrsig); // merge the changeset RRSet to the copy - dbg_xfrin_verb("Merging RRSIG to the one in the RRSet.\n"); - ret = knot_rrset_merge((void **)&rrsig, (void **)&add); + dbg_xfrin_detail("Merging RRSIG to the one in the RRSet.\n"); + ret = knot_rrset_merge_no_dupl((void **)&rrsig, (void **)&add); if (ret != KNOT_EOK) { dbg_xfrin("Failed to merge changeset RRSIG to copy: %s" ".\n", knot_strerror(ret)); @@ -2232,23 +2227,50 @@ void xfrin_cleanup_successful_update(knot_changes_t **changes) // delete old RDATA for (int i = 0; i < (*changes)->old_rdata_count; ++i) { dbg_xfrin_detail("Deleting old RDATA: %p, type: %s\n", - (*changes)->old_rdata[i], - knot_rrtype_to_string((*changes)->old_rdata_types[i])); + (*changes)->old_rdata[i], + knot_rrtype_to_string((*changes)->old_rdata_types[i])); knot_rdata_dump((*changes)->old_rdata[i], (*changes)->old_rdata_types[i], 0); - knot_rdata_t *rdata = (*changes)->old_rdata[i]; - if (rdata != NULL) { - do { - knot_rdata_t *tmp = rdata->next; - knot_rdata_deep_free(&rdata, - (*changes)->old_rdata_types[i], 1); - rdata = tmp; - } while (rdata != NULL - && rdata != (*changes)->old_rdata[i]); - } + // RDATA are stored separately so do not delete the whole chain + knot_rdata_deep_free(&(*changes)->old_rdata[i], + (*changes)->old_rdata_types[i], 1); +// knot_rdata_t *rdata = (*changes)->old_rdata[i]; +// if (rdata != NULL) { +// do { +// knot_rdata_t *tmp = rdata->next; +// knot_rdata_deep_free(&rdata, +// (*changes)->old_rdata_types[i], 1); +// rdata = tmp; +// } while (rdata != NULL +// && rdata != (*changes)->old_rdata[i]); +// } } + // free the empty nodes + for (int i = 0; i < (*changes)->old_nodes_count; ++i) { +dbg_xfrin_exec_detail( + char *name = knot_dname_to_str( + knot_node_owner((*changes)->old_nodes[i])); + dbg_xfrin_detail("Deleting old empty node: %p, owner: %s\n", + (*changes)->old_nodes[i], name); + free(name); +); + knot_node_free(&(*changes)->old_nodes[i]); + } + + // free empty NSEC3 nodes + for (int i = 0; i < (*changes)->old_nsec3_count; ++i) { +dbg_xfrin_exec_detail( + char *name = knot_dname_to_str( + knot_node_owner((*changes)->old_nsec3[i])); + dbg_xfrin_detail("Deleting old empty node: %p, owner: %s\n", + (*changes)->old_nsec3[i], name); + free(name); +); + knot_node_free(&(*changes)->old_nsec3[i]); + } + // free allocated arrays of nodes and rrsets free((*changes)->new_rrsets); free((*changes)->new_rdata); @@ -2304,13 +2326,21 @@ static void xfrin_switch_node_in_dname_table(knot_dname_t *dname, void *data) { UNUSED(data); - if (knot_dname_node(dname) == NULL) { +dbg_xfrin_exec_detail( + char *name = knot_dname_to_str(dname); + dbg_xfrin_detail("Switching node in dname %s (%p)\n", name, dname->node); + free(name); +); + /* dname is not checked here (for NULL value), which resulted in crash + * on howl recently. However, dname should not be NULL here at all, + * it is a sign of some other error. + */ + + if (dname->node == NULL) { return; } - assert(knot_node_new_node(knot_dname_node(dname)) != NULL); - knot_dname_set_node(dname, knot_node_get_new_node( - knot_dname_get_node(dname))); + knot_dname_update_node(dname); } /*----------------------------------------------------------------------------*/ @@ -2333,10 +2363,10 @@ static int xfrin_switch_nodes(knot_zone_contents_t *contents_copy) xfrin_switch_node_in_hash_table, NULL); assert(ret == 0); - // Traverse also the dname table and change the node pointers in dnames - knot_zone_contents_dname_table_apply(contents_copy, - xfrin_switch_node_in_dname_table, - NULL); +// // Traverse also the dname table and change the node pointers in dnames +// knot_zone_contents_dname_table_apply(contents_copy, +// xfrin_switch_node_in_dname_table, +// NULL); return KNOT_EOK; } @@ -2349,7 +2379,6 @@ static void xfrin_zone_contents_free2(knot_zone_contents_t **contents) if ((*contents)->table != NULL) { ck_destroy_table(&(*contents)->table, NULL, 0); -// ck_table_free(&(*contents)->table); } // free the zone tree, but only the structure @@ -2423,8 +2452,8 @@ void xfrin_rollback_update(knot_zone_contents_t *old_contents, } for (int i = 0; i < (*changes)->new_rdata_count; ++i) { - dbg_xfrin("Freeing %d. RDATA chain: %p\n", i, - (*changes)->new_rdata[i]); + dbg_xfrin_detail("Freeing %d. RDATA: %p\n", i, + (*changes)->new_rdata[i]); /* * In some case, the same RDATA may be stored in @@ -2438,28 +2467,14 @@ void xfrin_rollback_update(knot_zone_contents_t *old_contents, * already deleted, but that may be very time-consuming. */ - // discard the whole chain of RDATA - knot_rdata_t *rdata = (*changes)->new_rdata[i]; - knot_rdata_t *rdata_next = NULL; - - while (rdata != NULL && rdata->next != - (*changes)->new_rdata[i]) { - assert(rdata->next != rdata); - rdata_next = rdata->next; - dbg_xfrin(" Deleting RDATA: %p\n", rdata); - knot_rdata_deep_free(&rdata, - (*changes)->new_rdata_types[i], 1); - rdata = rdata_next; - } - - assert(rdata == NULL - || rdata->next == (*changes)->new_rdata[i]); + /* + * Every RDATA from a chain is stored separately. + * We thus do not follow the RDATA chains and free only + * the first RDATA in each. + */ - dbg_xfrin(" Deleting RDATA: %p\n", rdata); - knot_rdata_deep_free(&rdata, + knot_rdata_deep_free(&(*changes)->new_rdata[i], (*changes)->new_rdata_types[i], 1); - - //(*changes)->new_rdata[i] = NULL; } // free allocated arrays of nodes and rrsets @@ -2481,9 +2496,9 @@ void xfrin_rollback_update(knot_zone_contents_t *old_contents, /*----------------------------------------------------------------------------*/ -static int xfrin_apply_remove2(knot_zone_contents_t *contents, - knot_changeset_t *chset, - knot_changes_t *changes) +static int xfrin_apply_remove(knot_zone_contents_t *contents, + knot_changeset_t *chset, + knot_changes_t *changes) { /* * Iterate over removed RRSets, and remove them from the new nodes @@ -2492,7 +2507,7 @@ static int xfrin_apply_remove2(knot_zone_contents_t *contents, */ int ret = 0; knot_node_t *node = NULL; - knot_rrset_t *rrset = NULL; + knot_rrset_t *rrset = NULL, *rrsigs = NULL; int is_nsec3 = 0; @@ -2501,8 +2516,8 @@ dbg_xfrin_exec_verb( char *name = knot_dname_to_str( knot_rrset_owner(chset->remove[i])); dbg_xfrin_verb("Removing RRSet: %s, type %s\n", name, - knot_rrtype_to_string( - knot_rrset_type(chset->remove[i]))); + knot_rrtype_to_string( + knot_rrset_type(chset->remove[i]))); free(name); ); dbg_xfrin_exec_detail( @@ -2551,7 +2566,7 @@ dbg_xfrin_exec_detail( // this should work also for UPDATE ret = xfrin_apply_remove_rrsigs(changes, chset->remove[i], - node, &rrset); + node, &rrset, &rrsigs); } else { // this should work also for UPDATE ret = xfrin_apply_remove_normal(changes, @@ -2559,7 +2574,7 @@ dbg_xfrin_exec_detail( node, &rrset); } - dbg_xfrin_verb("xfrin_apply_remove() ret = %d\n", ret); + dbg_xfrin_detail("xfrin_apply_remove() ret = %d\n", ret); if (ret > 0) { continue; @@ -2573,13 +2588,14 @@ dbg_xfrin_exec_detail( /*----------------------------------------------------------------------------*/ -static int xfrin_apply_add2(knot_zone_contents_t *contents, - knot_changeset_t *chset, - knot_changes_t *changes) +static int xfrin_apply_add(knot_zone_contents_t *contents, + knot_changeset_t *chset, + knot_changes_t *changes) { int ret = 0; knot_node_t *node = NULL; knot_rrset_t *rrset = NULL; + knot_rrset_t *rrsigs = NULL; int is_nsec3 = 0; @@ -2588,8 +2604,8 @@ dbg_xfrin_exec_verb( char *name = knot_dname_to_str( knot_rrset_owner(chset->add[i])); dbg_xfrin_verb("Adding RRSet: %s, type: %s\n", name, - knot_rrtype_to_string( - knot_rrset_type(chset->add[i]))); + knot_rrtype_to_string( + knot_rrset_type(chset->add[i]))); free(name); ); dbg_xfrin_exec_detail( @@ -2605,14 +2621,14 @@ dbg_xfrin_exec_detail( knot_rrset_rdata(chset->add[i])) == KNOT_RRTYPE_NSEC3)) { - dbg_xfrin_verb("This is NSEC3-related RRSet.\n"); + dbg_xfrin_detail("This is NSEC3-related RRSet.\n"); is_nsec3 = 1; } // check if the old node is not the one we should use if (!node || knot_rrset_owner(chset->add[i]) != knot_node_owner(node)) { - dbg_xfrin_verb("Searching for node...\n"); + dbg_xfrin_detail("Searching for node...\n"); if (is_nsec3) { node = knot_zone_contents_get_nsec3_node( contents, @@ -2624,7 +2640,8 @@ dbg_xfrin_exec_detail( if (node == NULL) { // create new node, connect it properly to the // zone nodes - dbg_xfrin_verb("Node not found. Creating new.\n"); + dbg_xfrin_detail("Node not found. Creating new." + "\n"); node = xfrin_add_new_node(contents, chset->add[i], is_nsec3); @@ -2640,7 +2657,8 @@ dbg_xfrin_exec_detail( if (knot_rrset_type(chset->add[i]) == KNOT_RRTYPE_RRSIG) { ret = xfrin_apply_add_rrsig(changes, chset->add[i], - node, &rrset, contents); + node, &rrset, &rrsigs, + contents); } else { ret = xfrin_apply_add_normal(changes, chset->add[i], node, &rrset, contents); @@ -2648,8 +2666,8 @@ dbg_xfrin_exec_detail( assert(ret != KNOT_EOK); - dbg_xfrin_verb("xfrin_apply_..() returned %d, rrset: %p\n", ret, - rrset); + dbg_xfrin_detail("xfrin_apply_..() returned %s, rrset: %p\n", + knot_strerror(ret), rrset); if (ret > 0) { if (ret == 1) { @@ -2670,21 +2688,24 @@ dbg_xfrin_exec_detail( = chset->add[i]; // the same goes for the RDATA + int count = knot_rrset_rdata_rr_count( + chset->add[i]); // connect the RDATA to the list of new RDATA - int res = xfrin_changes_check_rdata(&changes->new_rdata, + int res = xfrin_changes_check_rdata( + &changes->new_rdata, &changes->new_rdata_types, changes->new_rdata_count, - &changes->new_rdata_allocated, 1); + &changes->new_rdata_allocated, count); if (res != KNOT_EOK) { return res; } xfrin_changes_add_rdata(changes->new_rdata, - changes->new_rdata_types, - &changes->new_rdata_count, - knot_rrset_get_rdata(chset->add[i]), - knot_rrset_type(chset->add[i])); + changes->new_rdata_types, + &changes->new_rdata_count, + knot_rrset_get_rdata(chset->add[i]), + knot_rrset_type(chset->add[i])); chset->add[i] = NULL; } else if (ret == 2) { @@ -2712,9 +2733,9 @@ dbg_xfrin_exec_detail( /*----------------------------------------------------------------------------*/ -static int xfrin_apply_replace_soa2(knot_zone_contents_t *contents, - knot_changes_t *changes, - knot_changeset_t *chset) +static int xfrin_apply_replace_soa(knot_zone_contents_t *contents, + knot_changes_t *changes, + knot_changeset_t *chset) { dbg_xfrin("Replacing SOA record.\n"); knot_node_t *node = knot_zone_contents_get_apex(contents); @@ -2762,11 +2783,13 @@ static int xfrin_apply_replace_soa2(knot_zone_contents_t *contents, return ret; } + int count = knot_rrset_rdata_rr_count(rrset); + count += knot_rrset_rdata_rr_count(chset->soa_to); // save the new SOA RDATA ret = xfrin_changes_check_rdata(&changes->new_rdata, &changes->new_rdata_types, changes->new_rdata_count, - &changes->new_rdata_allocated, 1); + &changes->new_rdata_allocated, count); if (ret != KNOT_EOK) { dbg_xfrin("Failed to add new RDATA to list.\n"); return ret; @@ -2813,9 +2836,9 @@ static int xfrin_apply_replace_soa2(knot_zone_contents_t *contents, /*----------------------------------------------------------------------------*/ -static int xfrin_apply_changeset2(knot_zone_contents_t *contents, - knot_changes_t *changes, - knot_changeset_t *chset) +static int xfrin_apply_changeset(knot_zone_contents_t *contents, + knot_changes_t *changes, + knot_changeset_t *chset) { /* * Applies one changeset to the zone. Checks if the changeset may be @@ -2836,19 +2859,17 @@ static int xfrin_apply_changeset2(knot_zone_contents_t *contents, return KNOT_ERROR; } - int ret = xfrin_apply_remove2(contents, chset, changes); + int ret = xfrin_apply_remove(contents, chset, changes); if (ret != KNOT_EOK) { -// xfrin_clean_changes_after_fail2(changes); return ret; } - ret = xfrin_apply_add2(contents, chset, changes); + ret = xfrin_apply_add(contents, chset, changes); if (ret != KNOT_EOK) { -// xfrin_clean_changes_after_fail(changes); return ret; } - return xfrin_apply_replace_soa2(contents, changes, chset); + return xfrin_apply_replace_soa(contents, changes, chset); } /*----------------------------------------------------------------------------*/ @@ -2871,6 +2892,8 @@ static void xfrin_mark_empty(knot_node_t *node, void *data) } changes->old_nodes[changes->old_nodes_count++] = node; + // mark the node as empty + knot_node_set_empty(node); if (node->parent != NULL) { assert(node->parent->children > 0); @@ -2903,6 +2926,8 @@ static void xfrin_mark_empty_nsec3(knot_node_t *node, void *data) } changes->old_nsec3[changes->old_nsec3_count++] = node; + // mark the node as empty + knot_node_set_empty(node); if (node->parent != NULL) { assert(node->parent->children > 0); @@ -2950,11 +2975,13 @@ static int xfrin_remove_empty_nodes(knot_zone_contents_t *contents, zone_node = NULL; hash_item = NULL; +dbg_xfrin_exec_detail( char *name = knot_dname_to_str(knot_node_owner( changes->old_nodes[i])); dbg_xfrin_detail("Old node #%d: %p, %s\n", i, changes->old_nodes[i], name); free(name); +); ret = knot_zone_contents_remove_node( contents, changes->old_nodes[i], &zone_node, @@ -2967,9 +2994,7 @@ static int xfrin_remove_empty_nodes(knot_zone_contents_t *contents, free(hash_item); free(zone_node); - knot_node_free(&changes->old_nodes[i], 0); } - changes->old_nodes_count = 0; // remove NSEC3 nodes for (int i = 0; i < changes->old_nsec3_count; ++i) { @@ -2990,9 +3015,7 @@ static int xfrin_remove_empty_nodes(knot_zone_contents_t *contents, } free(zone_node); - knot_node_free(&changes->old_nsec3[i], 0); } - changes->old_nsec3_count = 0; return KNOT_EOK; } @@ -3060,9 +3083,6 @@ int xfrin_apply_changesets(knot_zone_t *zone, dbg_xfrin("Applying changesets to zone...\n"); -// dbg_xfrin("\nOLD ZONE CONTENTS:\n\n"); -// knot_zone_contents_dump(old_contents, 1); - /* * Ensure that the zone generation is set to 0. */ @@ -3130,7 +3150,7 @@ int xfrin_apply_changesets(knot_zone_t *zone, dbg_xfrin_verb("Old contents apex: %p, new apex: %p\n", old_contents->apex, contents_copy->apex); for (int i = 0; i < chsets->count; ++i) { - if ((ret = xfrin_apply_changeset2(contents_copy, changes, + if ((ret = xfrin_apply_changeset(contents_copy, changes, &chsets->sets[i])) != KNOT_EOK) { xfrin_rollback_update(old_contents, @@ -3155,7 +3175,8 @@ int xfrin_apply_changesets(knot_zone_t *zone, */ /* - * Select and delete empty nodes. + * Select and remove empty nodes from zone trees. Do not free them right + * away as they may be referenced by some domain names. */ ret = xfrin_remove_empty_nodes(contents_copy, changes); if (ret != KNOT_EOK) { @@ -3165,11 +3186,9 @@ int xfrin_apply_changesets(knot_zone_t *zone, return ret; } - dbg_xfrin("Adjusting zone contents.\n"); dbg_xfrin_verb("Old contents apex: %p, new apex: %p\n", old_contents->apex, contents_copy->apex); -// ret = xfrin_adjust_contents(contents_copy, &changes); ret = knot_zone_contents_adjust(contents_copy); if (ret != KNOT_EOK) { dbg_xfrin("Failed to finalize zone contents: %s\n", @@ -3187,7 +3206,6 @@ int xfrin_apply_changesets(knot_zone_t *zone, return ret; } - //xfrin_cleanup_update(&changes); chsets->changes = changes; *new_contents = contents_copy; @@ -3215,6 +3233,20 @@ int xfrin_switch_zone(knot_zone_t *zone, dbg_xfrin_verb("Old contents: %p, apex: %p, new apex: %p\n", old, (old) ? old->apex : NULL, new_contents->apex); + // switch pointers in domain names, now only the new zone is used + if (transfer_type == XFR_TYPE_IIN) { + // Traverse also the dname table and change the node pointers + // in dnames + int ret = knot_zone_contents_dname_table_apply( + new_contents, + xfrin_switch_node_in_dname_table, NULL); + assert(ret == KNOT_EOK); + } + + // set generation to old, so that the flags may be used in next transfer + // and we do not search for new nodes anymore + knot_zone_contents_set_gen_old(new_contents); + // wait for readers to finish dbg_xfrin_verb("Waiting for readers to finish...\n"); synchronize_rcu(); diff --git a/src/libknot/updates/xfr-in.h b/src/libknot/updates/xfr-in.h index b009152..a762b81 100644..100755 --- a/src/libknot/updates/xfr-in.h +++ b/src/libknot/updates/xfr-in.h @@ -5,7 +5,7 @@ * * \brief XFR client API. * - * \addtogroup query_processing + * \addtogroup xfr * @{ */ /* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> @@ -189,9 +189,6 @@ int xfrin_switch_zone(knot_zone_t *zone, knot_zone_contents_t *new_contents, int deep_free); -//void xfrin_cleanup_failed_update(knot_zone_contents_t *old_contents, -// knot_zone_contents_t **new_contents); - void xfrin_cleanup_successful_update(knot_changes_t **changes); void xfrin_rollback_update(knot_zone_contents_t *old_contents, diff --git a/src/libknot/util/conv.c b/src/libknot/util/conv.c deleted file mode 100644 index 6626ddd..0000000 --- a/src/libknot/util/conv.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdlib.h> -#include <stdint.h> -#include <ctype.h> -#include "conv.h" - -#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */ - - -static const char Base64[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char Pad64 = '='; - -static int b64rmap_initialized = 0; -static uint8_t b64rmap[256]; - -static const uint8_t b64rmap_special = 0xf0; -static const uint8_t b64rmap_end = 0xfd; -static const uint8_t b64rmap_space = 0xfe; -static const uint8_t b64rmap_invalid = 0xff; - -/** - * Initializing the reverse map is not thread safe. - * Which is fine for NSD. For now... - **/ -static void b64_initialize_rmap() -{ - int i; - char ch; - - /* Null: end of string, stop parsing */ - b64rmap[0] = b64rmap_end; - - for (i = 1; i < 256; ++i) { - ch = (char)i; - /* Whitespaces */ - if (isspace(ch)) { - b64rmap[i] = b64rmap_space; - } - /* Padding: stop parsing */ - else if (ch == Pad64) { - b64rmap[i] = b64rmap_end; - } - /* Non-base64 char */ - else { - b64rmap[i] = b64rmap_invalid; - } - } - - /* Fill reverse mapping for base64 chars */ - for (i = 0; Base64[i] != '\0'; ++i) { - b64rmap[(uint8_t)Base64[i]] = i; - } - - b64rmap_initialized = 1; -} - -static int b64_pton_do(char const *src, uint8_t *target, size_t targsize) -{ - int tarindex, state, ch; - uint8_t ofs; - - state = 0; - tarindex = 0; - - while (1) { - ch = *src++; - ofs = b64rmap[ch]; - - if (ofs >= b64rmap_special) { - /* Ignore whitespaces */ - if (ofs == b64rmap_space) { - continue; - } - /* End of base64 characters */ - if (ofs == b64rmap_end) { - break; - } - /* A non-base64 character. */ - return (-1); - } - - switch (state) { - case 0: - if ((size_t)tarindex >= targsize) { - return (-1); - } - target[tarindex] = ofs << 2; - state = 1; - break; - case 1: - if ((size_t)tarindex + 1 >= targsize) { - return (-1); - } - target[tarindex] |= ofs >> 4; - target[tarindex+1] = (ofs & 0x0f) - << 4 ; - tarindex++; - state = 2; - break; - case 2: - if ((size_t)tarindex + 1 >= targsize) { - return (-1); - } - target[tarindex] |= ofs >> 2; - target[tarindex+1] = (ofs & 0x03) - << 6; - tarindex++; - state = 3; - break; - case 3: - if ((size_t)tarindex >= targsize) { - return (-1); - } - target[tarindex] |= ofs; - tarindex++; - state = 0; - break; - default: - abort(); - } - } - - /* - * We are done decoding Base-64 chars. Let's see if we ended - * on a byte boundary, and/or with erroneous trailing characters. - */ - - if (ch == Pad64) { /* We got a pad char. */ - ch = *src++; /* Skip it, get next. */ - switch (state) { - case 0: /* Invalid = in first position */ - case 1: /* Invalid = in second position */ - return (-1); - - case 2: /* Valid, means one byte of info */ - /* Skip any number of spaces. */ - for ((void)NULL; ch != '\0'; ch = *src++) - if (b64rmap[ch] != b64rmap_space) { - break; - } - /* Make sure there is another trailing = sign. */ - if (ch != Pad64) { - return (-1); - } - ch = *src++; /* Skip the = */ - /* Fall through to "single trailing =" case. */ - /* FALLTHROUGH */ - - case 3: /* Valid, means two bytes of info */ - /* - * We know this char is an =. Is there anything but - * whitespace after it? - */ - for ((void)NULL; ch != '\0'; ch = *src++) - if (b64rmap[ch] != b64rmap_space) { - return (-1); - } - - /* - * Now make sure for cases 2 and 3 that the "extra" - * bits that slopped past the last full byte were - * zeros. If we don't check them, they become a - * subliminal channel. - */ - if (target[tarindex] != 0) { - return (-1); - } - } - } else { - /* - * We ended by seeing the end of the string. Make sure we - * have no partial bytes lying around. - */ - if (state != 0) { - return (-1); - } - } - - return (tarindex); -} - - -static int b64_pton_len(char const *src) -{ - int tarindex, state, ch; - uint8_t ofs; - - state = 0; - tarindex = 0; - - while (1) { - ch = *src++; - ofs = b64rmap[ch]; - - if (ofs >= b64rmap_special) { - /* Ignore whitespaces */ - if (ofs == b64rmap_space) { - continue; - } - /* End of base64 characters */ - if (ofs == b64rmap_end) { - break; - } - /* A non-base64 character. */ - return (-1); - } - - switch (state) { - case 0: - state = 1; - break; - case 1: - tarindex++; - state = 2; - break; - case 2: - tarindex++; - state = 3; - break; - case 3: - tarindex++; - state = 0; - break; - default: - abort(); - } - } - - /* - * We are done decoding Base-64 chars. Let's see if we ended - * on a byte boundary, and/or with erroneous trailing characters. - */ - - if (ch == Pad64) { /* We got a pad char. */ - ch = *src++; /* Skip it, get next. */ - switch (state) { - case 0: /* Invalid = in first position */ - case 1: /* Invalid = in second position */ - return (-1); - - case 2: /* Valid, means one byte of info */ - /* Skip any number of spaces. */ - for ((void)NULL; ch != '\0'; ch = *src++) - if (b64rmap[ch] != b64rmap_space) { - break; - } - /* Make sure there is another trailing = sign. */ - if (ch != Pad64) { - return (-1); - } - ch = *src++; /* Skip the = */ - /* Fall through to "single trailing =" case. */ - /* FALLTHROUGH */ - - case 3: /* Valid, means two bytes of info */ - /* - * We know this char is an =. Is there anything but - * whitespace after it? - */ - for ((void)NULL; ch != '\0'; ch = *src++) - if (b64rmap[ch] != b64rmap_space) { - return (-1); - } - - } - } else { - /* - * We ended by seeing the end of the string. Make sure we - * have no partial bytes lying around. - */ - if (state != 0) { - return (-1); - } - } - - return (tarindex); -} - -int b64_pton(char const *src, uint8_t *target, size_t targsize) -{ - if (!b64rmap_initialized) { - b64_initialize_rmap(); - } - - if (target) { - return b64_pton_do(src, target, targsize); - } else { - return b64_pton_len(src); - } -} - -#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */ diff --git a/src/libknot/util/conv.h b/src/libknot/util/conv.h deleted file mode 100644 index d1e6dae..0000000 --- a/src/libknot/util/conv.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _KNOT_CONV_H_ -#define _KNOT_CONV_H_ - -#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */ - -/*! - * \brief Base64 presentation to wire conversion. - */ -int b64_pton(char const *src, uint8_t *target, size_t targsize); - -#endif // _KNOT_CONV_H_ diff --git a/src/libknot/util/debug.c b/src/libknot/util/debug.c index f14c3cd..a6555ad 100644..100755 --- a/src/libknot/util/debug.c +++ b/src/libknot/util/debug.c @@ -105,7 +105,6 @@ void knot_rrset_dump(const knot_rrset_t *rrset, char loaded_zone) return; } - fprintf(stderr, " rdata count: %d\n", rrset->rdata->count); knot_rdata_t *tmp = rrset->rdata; while (tmp->next != rrset->rdata && tmp->next != NULL) { @@ -133,11 +132,6 @@ void knot_node_dump(knot_node_t *node, void *loaded_zone) hex_print((char *)node->owner->labels, node->owner->label_count); fprintf(stderr, "node: %p\n", node); fprintf(stderr, "node (in node's owner): %p\n", node->owner->node); - if (loaded_zone && node->prev != NULL) { - name = knot_dname_to_str(node->prev->owner); - fprintf(stderr, "previous node: %s\n", name); - free(name); - } if (knot_node_is_deleg_point(node)) { fprintf(stderr, "delegation point\n"); @@ -197,6 +191,8 @@ void knot_node_dump(knot_node_t *node, void *loaded_zone) } else { fprintf(stderr, "none\n"); } + + fprintf(stderr, "Zone: %p\n", node->zone); fprintf(stderr, "RRSet count: %d\n", node->rrset_count); diff --git a/src/libknot/util/debug.h b/src/libknot/util/debug.h index b6aba6e..731fed8 100644..100755 --- a/src/libknot/util/debug.h +++ b/src/libknot/util/debug.h @@ -42,6 +42,7 @@ #define KNOT_ZONE_DEBUG #define KNOT_ZONEDB_DEBUG #define KNOT_NODE_DEBUG + #define KNOT_ZONEDIFF_DEBUG #endif #ifdef KNOT_NS_DEBUG @@ -368,6 +369,48 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone); #define dbg_zonedb_exec_detail(cmds) #endif +#ifdef KNOT_ZONEDIFF_DEBUG + +/* Brief messages. */ +#ifdef DEBUG_ENABLE_BRIEF +#define dbg_zonediff(msg...) fprintf(stderr, msg) +#define dbg_zonediff_hex(data, len) hex_print((data), (len)) +#else +#define dbg_zonediff(msg...) +#define dbg_zonediff_hex(data, len) +#endif + +/* Verbose messages. */ +#ifdef DEBUG_ENABLE_VERBOSE +#define dbg_zonediff_verb(msg...) fprintf(stderr, msg) +#define dbg_zonediff_hex_verb(data, len) hex_print((data), (len)) +#else +#define dbg_zonediff_verb(msg...) +#define dbg_zonediff_hex_verb(data, len) +#endif + +/* Detail messages. */ +#ifdef DEBUG_ENABLE_DETAILS +#define dbg_zonediff_detail(msg...) fprintf(stderr, msg) +#define dbg_zonediff_hex_detail(data, len) hex_print((data), (len)) +#define dbg_zonediff_exec_detail(cmds) do { cmds } while (0) +#else +#define dbg_zonediff_detail(msg...) +#define dbg_zonediff_hex_detail(data, len) +#define dbg_zonediff_exec_detail(cmds) +#endif + +/* No messages. */ +#else +#define dbg_zonediff(msg...) +#define dbg_zonediff_hex(data, len) +#define dbg_zonediff_verb(msg...) +#define dbg_zonediff_hex_verb(data, len) +#define dbg_zonediff_detail(msg...) +#define dbg_zonediff_hex_detail(data, len) +#define dbg_zonediff_exec_detail(cmds) +#endif + /******************************************************************************/ #ifdef KNOT_RESPONSE_DEBUG @@ -607,7 +650,6 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone); /* Brief messages. */ #ifdef DEBUG_ENABLE_BRIEF #define dbg_ck_hash(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg) -#define dbg_ck_rehash(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg) #define dbg_ck_hash_hex(data, len) hex_log(LOG_SERVER, (data), (len)) #else #define dbg_ck_hash(msg...) @@ -636,7 +678,6 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone); /* No messages. */ #else #define dbg_ck_hash(msg...) -#define dbg_ck_rehash(msg...) #define dbg_ck_hash_hex(data, len) #define dbg_ck_hash_verb(msg...) #define dbg_ck_hash_hex_verb(data, len) @@ -646,6 +687,21 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone); /******************************************************************************/ +#ifdef KNOT_STASH_DEBUG + +#ifdef DEBUG_ENABLE_BRIEF +#define dbg_stash(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg) +#else +#define dbg_stash(msg...) +#endif + +#else +#define dbg_stash(msg...) +#endif + + +/******************************************************************************/ + #ifdef KNOT_XFRIN_DEBUG /* Brief messages. */ @@ -780,37 +836,46 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone); #ifdef DEBUG_ENABLE_BRIEF #define dbg_rrset(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg) #define dbg_rrset_hex(data, len) hex_log(LOG_ZONE, (data), (len)) +#define dbg_rrset_exec(cmds) do { cmds } while (0) #else #define dbg_rrset(msg...) #define dbg_rrset_hex(data, len) +#define dbg_rrset_exec(cmds) #endif /* Verbose messages. */ #ifdef DEBUG_ENABLE_VERBOSE #define dbg_rrset_verb(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg) #define dbg_rrset_hex_verb(data, len) hex_log(LOG_ZONE, (data), (len)) +#define dbg_rrset_exec_verb(cmds) do { cmds } while (0) #else #define dbg_rrset_verb(msg...) #define dbg_rrset_hex_verb(data, len) +#define dbg_rrset_exec_verb(cmds) #endif /* Detail messages. */ #ifdef DEBUG_ENABLE_DETAILS #define dbg_rrset_detail(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg) #define dbg_rrset_hex_detail(data, len) hex_log(LOG_ZONE, (data), (len)) +#define dbg_rrset_exec_detail(cmds) do { cmds } while (0) #else #define dbg_rrset_detail(msg...) #define dbg_rrset_hex_detail(data, len) +#define dbg_rrset_exec_detail(cmds) #endif /* No messages. */ #else #define dbg_rrset(msg...) #define dbg_rrset_hex(data, len) +#define dbg_rrset_exec(cmds) #define dbg_rrset_verb(msg...) #define dbg_rrset_hex_verb(data, len) +#define dbg_rrset_exec_verb(cmds) #define dbg_rrset_detail(msg...) #define dbg_rrset_hex_detail(data, len) +#define dbg_rrset_exec_detail(cmds) #endif /******************************************************************************/ diff --git a/src/libknot/util/descriptor.c b/src/libknot/util/descriptor.c index 63e1747..35ae77e 100644..100755 --- a/src/libknot/util/descriptor.c +++ b/src/libknot/util/descriptor.c @@ -569,3 +569,5 @@ int knot_rrtype_is_metatype(uint16_t type) || type == KNOT_RRTYPE_OPT); } +/*! @} */ + diff --git a/src/libknot/util/descriptor.h b/src/libknot/util/descriptor.h index b7e4a53..b7e4a53 100644..100755 --- a/src/libknot/util/descriptor.h +++ b/src/libknot/util/descriptor.h diff --git a/src/libknot/util/error.h b/src/libknot/util/error.h index 888669a..96eff68 100644..100755 --- a/src/libknot/util/error.h +++ b/src/libknot/util/error.h @@ -52,6 +52,7 @@ enum knot_error { KNOT_EACCES, /*!< Permission is denied. */ KNOT_ECRYPTO, /*!< Error in crypto library. */ KNOT_ENSEC3PAR, /*!< Missing or wrong NSEC3PARAM record. */ + KNOT_ENSEC3CHAIN, /*!< Missing or wrong NSEC3 chain in the zone. */ KNOT_EBADZONE, /*!< Domain name does not belong to the zone. */ KNOT_EHASH, /*!< Error in hash table. */ KNOT_EZONEIN, /*!< Error inserting zone. */ @@ -65,10 +66,12 @@ enum knot_error { KNOT_ENOXFR, /*!< Transfer was not sent. */ KNOT_ENOIXFR, /*!< Transfer is not IXFR (is in AXFR format). */ KNOT_EXFRREFUSED, /*!< Zone transfer refused by the server. */ + KNOT_EXFRDENIED, /*!< Transfer not allowed. */ KNOT_ECONN, /*!< Connection reset. */ KNOT_EIXFRSPACE, /*!< IXFR reply did not fit in. */ KNOT_ECNAME, /*!< CNAME loop found in zone. */ - KNOT_ERROR_COUNT = 34 + KNOT_ENODIFF, /*!< No zone diff can be created. */ + KNOT_ERROR_COUNT = 37 }; /*! \brief Table linking error messages to error codes. */ diff --git a/src/libknot/util/libknot_error.c b/src/libknot/util/libknot_error.c index f787565..dd4280d 100644..100755 --- a/src/libknot/util/libknot_error.c +++ b/src/libknot/util/libknot_error.c @@ -24,7 +24,7 @@ const error_table_t knot_error_msgs[KNOT_ERROR_COUNT] = { {KNOT_ENOTSUP, "Operation not supported."}, {KNOT_EAGAIN, "OS lacked necessary resources."}, {KNOT_ERANGE, "Value is out of range."}, - {KNOT_EBADARG, "Wrong argument supported."}, + {KNOT_EBADARG, "Wrong argument supplied."}, {KNOT_EFEWDATA, "Not enough data to parse."}, {KNOT_ESPACE, "Not enough space provided."}, {KNOT_EMALF, "Malformed data."}, @@ -32,6 +32,7 @@ const error_table_t knot_error_msgs[KNOT_ERROR_COUNT] = { {KNOT_EACCES, "Permission to perform requested operation is denied."}, {KNOT_ECRYPTO, "Error in crypto library."}, {KNOT_ENSEC3PAR, "Missing or wrong NSEC3PARAM record."}, + {KNOT_ENSEC3CHAIN, "Missing or wrong NSEC3 chain in the zone."}, {KNOT_EBADZONE, "Domain name does not belong to the given zone."}, {KNOT_EHASH, "Error in hash table."}, {KNOT_EZONEIN, "Error inserting zone."}, @@ -45,11 +46,13 @@ const error_table_t knot_error_msgs[KNOT_ERROR_COUNT] = { {KNOT_ENOXFR, "Transfer was not sent."}, {KNOT_ENOIXFR, "Transfer is not IXFR (is in AXFR format)."}, {KNOT_EXFRREFUSED, "Zone transfer refused by the server."}, + {KNOT_EXFRDENIED, "Transfer not allowed."}, {KNOT_TSIG_EBADSIG, "Failed to verify TSIG MAC." }, {KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid." }, {KNOT_TSIG_EBADTIME, "TSIG signing time out of range." }, {KNOT_ECONN, "Connection reset."}, {KNOT_EIXFRSPACE, "IXFR reply did not fit in."}, {KNOT_ECNAME, "CNAME loop found in zone."}, + {KNOT_ENODIFF, "Cannot create zone diff."}, {KNOT_ERROR, 0} }; diff --git a/src/libknot/util/tolower.c b/src/libknot/util/tolower.c index d71c467..d71c467 100644..100755 --- a/src/libknot/util/tolower.c +++ b/src/libknot/util/tolower.c diff --git a/src/libknot/util/tolower.h b/src/libknot/util/tolower.h index 6b9e98c..2e92258 100644..100755 --- a/src/libknot/util/tolower.h +++ b/src/libknot/util/tolower.h @@ -55,3 +55,5 @@ static inline uint8_t knot_tolower(uint8_t c) { } #endif /* _KNOT_TOLOWER_H_ */ + +/*! @} */ diff --git a/src/libknot/util/utils.c b/src/libknot/util/utils.c index 04e12c5..04e12c5 100644..100755 --- a/src/libknot/util/utils.c +++ b/src/libknot/util/utils.c diff --git a/src/libknot/util/utils.h b/src/libknot/util/utils.h index fd275b3..fd275b3 100644..100755 --- a/src/libknot/util/utils.h +++ b/src/libknot/util/utils.h diff --git a/src/libknot/util/wire.h b/src/libknot/util/wire.h index 0a24ff1..0a24ff1 100644..100755 --- a/src/libknot/util/wire.h +++ b/src/libknot/util/wire.h diff --git a/src/libknot/zone/dname-table.c b/src/libknot/zone/dname-table.c index d2d97c2..d2d97c2 100644..100755 --- a/src/libknot/zone/dname-table.c +++ b/src/libknot/zone/dname-table.c diff --git a/src/libknot/zone/dname-table.h b/src/libknot/zone/dname-table.h index 945b6de..945b6de 100644..100755 --- a/src/libknot/zone/dname-table.h +++ b/src/libknot/zone/dname-table.h diff --git a/src/libknot/zone/node.c b/src/libknot/zone/node.c index 4ad2a27..c196f29 100644..100755 --- a/src/libknot/zone/node.c +++ b/src/libknot/zone/node.c @@ -81,67 +81,29 @@ static inline void knot_node_flags_set_nonauth(uint8_t *flags) *flags |= KNOT_NODE_FLAGS_NONAUTH; } -///*----------------------------------------------------------------------------*/ -///*! -// * \brief Returns the old node flag -// * -// * \param flags Flags to retrieve the flag from. -// * -// * \return A byte with only the old node flag set if it was set in \a flags. -// */ -//static inline uint8_t knot_node_flags_get_old(uint8_t flags) -//{ -// return flags & KNOT_NODE_FLAGS_OLD; -//} - -///*----------------------------------------------------------------------------*/ -///*! -// * \brief Sets the old node flag. -// * -// * \param flags Flags to set the flag in. -// */ -//static inline void knot_node_flags_set_new(uint8_t *flags) -//{ -// *flags |= KNOT_NODE_FLAGS_NEW; -//} - -///*----------------------------------------------------------------------------*/ -///*! -// * \brief Returns the new node flag -// * -// * \param flags Flags to retrieve the flag from. -// * -// * \return A byte with only the new node flag set if it was set in \a flags. -// */ -//static inline uint8_t knot_node_flags_get_new(uint8_t flags) -//{ -// return flags & KNOT_NODE_FLAGS_NEW; -//} - -///*----------------------------------------------------------------------------*/ -///*! -// * \brief Sets the new node flag. -// * -// * \param flags Flags to set the flag in. -// */ -//static inline void knot_node_flags_set_old(uint8_t *flags) -//{ -// *flags |= KNOT_NODE_FLAGS_OLD; -//} - -///*----------------------------------------------------------------------------*/ - -//static inline void knot_node_flags_clear_new(uint8_t *flags) -//{ -// *flags &= ~KNOT_NODE_FLAGS_NEW; -//} - -///*----------------------------------------------------------------------------*/ - -//static inline void knot_node_flags_clear_old(uint8_t *flags) -//{ -// *flags &= ~KNOT_NODE_FLAGS_OLD; -//} +/*----------------------------------------------------------------------------*/ +/*! + * \brief Sets the empty node flag. + * + * \param flags Flags to set the flag in. + */ +static inline void knot_node_flags_set_empty(uint8_t *flags) +{ + *flags |= KNOT_NODE_FLAGS_EMPTY; +} + +/*----------------------------------------------------------------------------*/ +/*! + * \brief Returns the empty node flag + * + * \param flags Flags to retrieve the flag from. + * + * \return A byte with only the empty node flag set if it was set in \a flags. + */ +static inline uint8_t knot_node_flags_get_empty(uint8_t flags) +{ + return flags & KNOT_NODE_FLAGS_EMPTY; +} /*----------------------------------------------------------------------------*/ /*! @@ -166,26 +128,6 @@ static int compare_rrset_types(void *rr1, void *rr2) } /*----------------------------------------------------------------------------*/ - -//static int knot_node_zone_gen_is_new(const knot_node_t *node) -//{ -// assert(node->zone != NULL); -// knot_zone_contents_t *cont = rcu_dereference(node->zone->contents); -// assert(cont != NULL); -// return knot_zone_contents_gen_is_new(cont); -//} - -///*----------------------------------------------------------------------------*/ - -//static int knot_node_zone_gen_is_old(const knot_node_t *node) -//{ -// assert(node->zone != NULL); -// knot_zone_contents_t *cont = rcu_dereference(node->zone->contents); -// assert(cont != NULL); -// return knot_zone_contents_gen_is_old(cont); -//} - -/*----------------------------------------------------------------------------*/ /* API functions */ /*----------------------------------------------------------------------------*/ @@ -211,9 +153,12 @@ knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent, } /*----------------------------------------------------------------------------*/ - +/*! \todo Consider replacing rrset_merge() with rrset_merge_no_dupl(). Currently + * this function is never called with merge=1, so it's not a problem, + * but it may be in the future. + */ int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset, - int merge) + int merge) { if (node == NULL) { return KNOT_EBADARG; @@ -235,6 +180,24 @@ int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset, } } +int knot_node_add_rrset_no_dupl(knot_node_t *node, knot_rrset_t *rrset) +{ + int ret = 0; + + if ((ret = (gen_tree_add(node->rrset_tree, rrset, + knot_rrset_merge_no_dupl))) < 0) { + dbg_node("Failed to add rrset to node->rrset_tree.\n"); + return KNOT_ERROR; + } + + if (ret >= 0) { + node->rrset_count += (ret > 0 ? 0 : 1); + return ret; + } else { + return KNOT_ERROR; + } +} + /*----------------------------------------------------------------------------*/ const knot_rrset_t *knot_node_rrset(const knot_node_t *node, @@ -315,6 +278,8 @@ struct knot_node_save_rrset_arg { size_t max_count; }; +/*----------------------------------------------------------------------------*/ + static void save_rrset_to_array(void *node, void *data) { struct knot_node_save_rrset_arg *args = @@ -329,6 +294,8 @@ static void save_rrset_to_array(void *node, void *data) args->array[args->count++] = rrset; } +/*----------------------------------------------------------------------------*/ + knot_rrset_t **knot_node_get_rrsets(const knot_node_t *node) { if (node == NULL || node->rrset_count == 0) { @@ -463,17 +430,6 @@ knot_node_t *knot_node_get_previous(const knot_node_t *node) /*----------------------------------------------------------------------------*/ -const knot_node_t *knot_node_next(const knot_node_t *node) -{ - if (node == NULL) { - return NULL; - } - - return node->next; -} - -/*----------------------------------------------------------------------------*/ - void knot_node_set_previous(knot_node_t *node, knot_node_t *prev) { if (node == NULL) { @@ -620,7 +576,7 @@ void knot_node_set_new_node(knot_node_t *node, /*----------------------------------------------------------------------------*/ -void knot_node_set_zone(knot_node_t *node, knot_zone_t *zone) +void knot_node_set_zone(knot_node_t *node, const knot_zone_t *zone) { if (node == NULL) { return; @@ -631,6 +587,17 @@ void knot_node_set_zone(knot_node_t *node, knot_zone_t *zone) /*----------------------------------------------------------------------------*/ +const knot_zone_t *knot_node_zone(const knot_node_t *node) +{ + if (node == NULL) { + return NULL; + } + + return node->zone; +} + +/*----------------------------------------------------------------------------*/ + void knot_node_update_ref(knot_node_t **ref) { if (*ref != NULL && (*ref)->new_node != NULL) { @@ -644,8 +611,6 @@ void knot_node_update_refs(knot_node_t *node) { // reference to previous node knot_node_update_ref(&node->prev); - // reference to next node - knot_node_update_ref(&node->next); // reference to parent knot_node_update_ref(&node->parent); // reference to wildcard child @@ -713,6 +678,20 @@ int knot_node_is_auth(const knot_node_t *node) /*----------------------------------------------------------------------------*/ +int knot_node_is_empty(const knot_node_t *node) +{ + return knot_node_flags_get_empty(node->flags); +} + +/*----------------------------------------------------------------------------*/ + +void knot_node_set_empty(knot_node_t *node) +{ + knot_node_flags_set_empty(&node->flags); +} + +/*----------------------------------------------------------------------------*/ + static void knot_node_free_rrsets_from_tree(void *item, void *data) { if (item == NULL) { @@ -727,14 +706,6 @@ static void knot_node_free_rrsets_from_tree(void *item, void *data) void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames) { - /* CLEANUP */ -// knot_rrset_t **rrsets = knot_node_get_rrsets(node); -// for (int i = 0; i < node->rrset_count; i++) { -// knot_rrset_deep_free(&(rrsets[i]), 0, 1, free_rdata_dnames); -// } - -// free(rrsets); - if (node == NULL) { return; } @@ -748,16 +719,16 @@ void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames) /*----------------------------------------------------------------------------*/ -void knot_node_free(knot_node_t **node, int fix_refs) +void knot_node_free(knot_node_t **node) { if (node == NULL || *node == NULL) { return; } - dbg_node("Freeing node: %p\n", *node); + dbg_node_detail("Freeing node: %p\n", *node); if ((*node)->rrset_tree != NULL) { - dbg_node("Freeing RRSets.\n"); + dbg_node_detail("Freeing RRSets.\n"); gen_tree_destroy(&(*node)->rrset_tree, NULL, NULL); } @@ -768,53 +739,13 @@ void knot_node_free(knot_node_t **node, int fix_refs) knot_dname_set_node((*node)->owner, NULL); } - dbg_node("Releasing owner.\n"); + dbg_node_detail("Releasing owner.\n"); knot_dname_release((*node)->owner); - // check nodes referencing this node and fix the references - - if (fix_refs) { - // previous node - dbg_node("Checking previous.\n"); - if ((*node)->prev && (*node)->prev->next == (*node)) { - (*node)->prev->next = (*node)->next; - } - - dbg_node("Checking next.\n"); - if ((*node)->next && (*node)->next->prev == (*node)) { - (*node)->next->prev = (*node)->prev; - } - - // NSEC3 node - dbg_node("Checking NSEC3.\n"); - if ((*node)->nsec3_node - && (*node)->nsec3_node->nsec3_referer == (*node)) { - (*node)->nsec3_node->nsec3_referer = NULL; - } - - dbg_node("Checking NSEC3 ref.\n"); - if ((*node)->nsec3_referer - && (*node)->nsec3_referer->nsec3_node == (*node)) { - (*node)->nsec3_referer->nsec3_node = NULL; - } - - // wildcard child node - dbg_node("Checking parent's wildcard child.\n"); - if ((*node)->parent - && (*node)->parent->wildcard_child == (*node)) { - (*node)->parent->wildcard_child = NULL; - } - - // fix parent's children count - if ((*node)->parent) { - --(*node)->parent->children; - } - } - free(*node); *node = NULL; - dbg_node("Done.\n"); + dbg_node_detail("Done.\n"); } /*----------------------------------------------------------------------------*/ diff --git a/src/libknot/zone/node.h b/src/libknot/zone/node.h index 11cd0ce..1ab814a 100644..100755 --- a/src/libknot/zone/node.h +++ b/src/libknot/zone/node.h @@ -62,8 +62,6 @@ struct knot_node { */ struct knot_node *prev; - struct knot_node *next; - /*! * \brief NSEC3 node corresponding to this node. * @@ -74,7 +72,7 @@ struct knot_node { struct knot_node *nsec3_referer; - struct knot_zone *zone; + const struct knot_zone *zone; struct knot_node *new_node; @@ -83,22 +81,12 @@ struct knot_node { unsigned short rrset_count; /*!< Number of RRSets stored in the node. */ /*! - * \brief Generation of node to be used. - * - * If set to 0, the old node will be used. Otherwise new nodes will - * be used. This applies when getting some referenced node. - - */ -// short **generation; - - /*! * \brief Various flags. * * Currently only two: * 0x01 - node is a delegation point * 0x02 - node is non-authoritative (under a delegation point) - * 0x80 - node is old and will be removed (during update) - * 0x40 - node is new, should not be used while zone is old + * 0x10 - node is empty and will be deleted after update */ uint8_t flags; }; @@ -113,9 +101,11 @@ typedef enum { /*! \brief Node is not authoritative (i.e. below a zone cut). */ KNOT_NODE_FLAGS_NONAUTH = (uint8_t)0x02, /*! \brief Node is old and will be removed (during update). */ - KNOT_NODE_FLAGS_OLD = (uint8_t)0x80, + KNOT_NODE_FLAGS_OLD = (uint8_t)0x04, /*! \brief Node is new and should not be used while zoen is old. */ - KNOT_NODE_FLAGS_NEW = (uint8_t)0x40 + KNOT_NODE_FLAGS_NEW = (uint8_t)0x08, + /*! \brief Node is empty and will be deleted after update. */ + KNOT_NODE_FLAGS_EMPTY = (uint8_t)0x10 } knot_node_flags_t; /*----------------------------------------------------------------------------*/ @@ -147,6 +137,8 @@ knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent, int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset, int merge); +int knot_node_add_rrset_no_dupl(knot_node_t *node, knot_rrset_t *rrset); + /*! * \brief Returns the RRSet of the given type from the node. * @@ -255,8 +247,6 @@ const knot_node_t *knot_node_previous(const knot_node_t *node); */ knot_node_t *knot_node_get_previous(const knot_node_t *node); -const knot_node_t *knot_node_next(const knot_node_t *node); - /*! * \brief Sets the previous node of the given node. * @@ -351,7 +341,9 @@ knot_node_t *knot_node_get_new_node(const knot_node_t *node); void knot_node_set_new_node(knot_node_t *node, knot_node_t *new_node); -void knot_node_set_zone(knot_node_t *node, struct knot_zone *zone); +void knot_node_set_zone(knot_node_t *node, const struct knot_zone *zone); + +const struct knot_zone *knot_node_zone(const knot_node_t *node); void knot_node_update_ref(knot_node_t **ref); @@ -405,6 +397,10 @@ void knot_node_clear_new(knot_node_t *node); void knot_node_clear_old(knot_node_t *node); +int knot_node_is_empty(const knot_node_t *node); + +void knot_node_set_empty(knot_node_t *node); + /*! * \brief Destroys the RRSets within the node structure. * @@ -428,7 +424,7 @@ void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames); * * \todo Document missing parameters. */ -void knot_node_free(knot_node_t **node, int fix_refs); +void knot_node_free(knot_node_t **node); /*! * \brief Compares two nodes according to their owner. diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c index 19268c4..7e453a5 100644..100755 --- a/src/libknot/zone/zone-contents.c +++ b/src/libknot/zone/zone-contents.c @@ -20,6 +20,8 @@ #include "util/error.h" #include "util/debug.h" #include "common/base32hex.h" +/*! \todo XXX TODO FIXME remove once testing is done. */ +#include "zcompile/zcompile.h" #include "consts.h" /*----------------------------------------------------------------------------*/ @@ -42,7 +44,7 @@ typedef struct { const uint8_t KNOT_ZONE_FLAGS_GEN_OLD = 0; /* xxxxxx00 */ const uint8_t KNOT_ZONE_FLAGS_GEN_NEW = 1 << 0; /* xxxxxx01 */ -const uint8_t KNOT_ZONE_FLAGS_GEN_FIN = 1 << 2; /* xxxxxx10 */ +const uint8_t KNOT_ZONE_FLAGS_GEN_FIN = 1 << 1; /* xxxxxx10 */ const uint8_t KNOT_ZONE_FLAGS_GEN_MASK = 3; /* 00000011 */ const uint8_t KNOT_ZONE_FLAGS_ANY_MASK = 4; /* 00000100 */ const uint8_t KNOT_ZONE_FLAGS_ANY = 4; /* 00000100 */ @@ -91,8 +93,8 @@ dbg_zone_exec( char *node_owner = knot_dname_to_str(knot_node_owner(node)); char *apex_owner = knot_dname_to_str(contents->apex->owner); dbg_zone("zone: Trying to insert foreign node to a " - "zone. Node owner: %s, zone apex: %s\n", - node_owner, apex_owner); + "zone. Node owner: %s, zone apex: %s\n", + node_owner, apex_owner); free(node_owner); free(apex_owner); ); @@ -137,7 +139,7 @@ static void knot_zone_contents_destroy_node_owner_from_tree( UNUSED(data); /*!< \todo change completely! */ - knot_node_free(&tnode->node, 0); + knot_node_free(&tnode->node); } /*----------------------------------------------------------------------------*/ @@ -159,11 +161,8 @@ static int knot_zone_contents_dnames_from_rdata_to_table( == KNOT_RDATA_WF_UNCOMPRESSED_DNAME || d->wireformat[j] == KNOT_RDATA_WF_LITERAL_DNAME) { -// printf("Saving dname from rdata to dname table: " -// "%p.\n", knot_rdata_get_item(rdata, j)->dname); rc = knot_dname_table_add_dname_check(table, &knot_rdata_get_item(rdata, j)->dname); -// printf("Returned: %d\n", rc); if (rc < 0) { dbg_zone("Error: %s\n", knot_strerror(rc)); return rc; @@ -171,7 +170,7 @@ static int knot_zone_contents_dnames_from_rdata_to_table( } } - dbg_zone("RDATA OK.\n"); + dbg_zone_detail("RDATA OK.\n"); return KNOT_EOK; } @@ -187,7 +186,7 @@ static int knot_zone_contents_dnames_from_rrset_to_table( // discard the old owner and replace it with the new knot_rrset_set_owner(rrset, owner); } - dbg_zone("RRSet owner: %p\n", rrset->owner); + dbg_zone_detail("RRSet owner: %p\n", rrset->owner); knot_rrtype_descriptor_t *desc = knot_rrtype_descriptor_by_type( knot_rrset_type(rrset)); @@ -197,10 +196,6 @@ static int knot_zone_contents_dnames_from_rrset_to_table( return KNOT_EOK; } // for each RDATA in RRSet -// char *name = knot_dname_to_str(rrset->owner); -// char *type = knot_rrtype_to_string(rrset->type); -// printf("Storing dnames from RDATA from RRSet %s, %s\n", name, type); -// free(name); knot_rdata_t *rdata = knot_rrset_get_rdata(rrset); while (rdata != NULL) { int rc = knot_zone_contents_dnames_from_rdata_to_table(table, @@ -227,10 +222,10 @@ static int knot_zone_contents_dnames_from_node_to_table( // insert owner char *name = knot_dname_to_str(node->owner); - dbg_zone("Node owner before inserting to dname table: %p.\n", - node->owner); - dbg_zone("Node owner before inserting to dname table: %s.\n", - name); + dbg_zone_detail("Node owner before inserting to dname table: %p.\n", + node->owner); + dbg_zone_detail("Node owner before inserting to dname table: %s.\n", + name); free(name); //knot_dname_t *old_owner = node->owner; int rc = knot_dname_table_add_dname_check(table, &node->owner); @@ -239,17 +234,18 @@ static int knot_zone_contents_dnames_from_node_to_table( return rc; } int replace_owner = (rc > 0); - dbg_zone("Node owner after inserting to dname table: %p.\n", - node->owner); + +dbg_zone_exec_detail( name = knot_dname_to_str(node->owner); - dbg_zone("Node owner after inserting to dname table: %s.\n", - name); + dbg_zone_detail("Node owner after inserting to dname table: %p (%s).\n", + node->owner, name); free(name); +); knot_rrset_t **rrsets = knot_node_get_rrsets(node); // for each RRSet for (int i = 0; i < knot_node_rrset_count(node); ++i) { - dbg_zone("Inserting RRSets from node to table.\n"); + dbg_zone_detail("Inserting RRSets from node to table.\n"); rc = knot_zone_contents_dnames_from_rrset_to_table(table, rrsets[i], replace_owner, node->owner); if (rc != KNOT_EOK) { @@ -264,27 +260,46 @@ static int knot_zone_contents_dnames_from_node_to_table( } /*----------------------------------------------------------------------------*/ -/*! - * \brief Finds and sets wildcard child for given node's owner. - * - * \param zone Current zone. - * \param node Node to be used. - */ -//static void find_and_set_wildcard_child(knot_zone_contents_t *zone, -// knot_node_t *node) -//{ -// knot_dname_t *chopped = knot_dname_left_chop(node->owner); -// assert(chopped); -// knot_node_t *wildcard_parent; -// wildcard_parent = -// knot_zone_contents_get_node(zone, chopped); -// knot_dname_free(&chopped); +static const knot_node_t *knot_zone_contents_find_wildcard_child( + knot_zone_contents_t *zone, const knot_node_t *closest_encloser) +{ + assert(zone != NULL); + assert(closest_encloser != NULL); + + knot_dname_t *tmp = knot_dname_new_from_str("*", 1, NULL); + CHECK_ALLOC(tmp, NULL); -// assert(wildcard_parent); /* it *has* to be there */ + knot_dname_t *wildcard = knot_dname_cat(tmp, knot_node_owner( + closest_encloser)); + if (wildcard == NULL) { + free(tmp); + return NULL; + } -// knot_node_set_wildcard_child(wildcard_parent, node); -//} + assert(wildcard == tmp); + +dbg_zone_exec_detail( + char *name = knot_dname_to_str(knot_node_owner(closest_encloser)); + char *name2 = knot_dname_to_str(wildcard); + dbg_zone_detail("Searching for wildcard child of %s (%s)\n", name, + name2); + free(name); + free(name2); +); + + const knot_node_t *found = NULL, *ce = NULL, *prev = NULL; + int ret = knot_zone_contents_find_dname(zone, wildcard, &found, &ce, + &prev); + + knot_dname_free(&wildcard); + + if (ret != KNOT_ZONE_NAME_FOUND) { + return NULL; + } else { + return found; + } +} /*----------------------------------------------------------------------------*/ /*! @@ -303,54 +318,79 @@ static int knot_zone_contents_dnames_from_node_to_table( * \param zone Zone to which the RDATA belongs. * \param pos Position of the RDATA item in the RDATA. */ -//static void knot_zone_contents_adjust_rdata_item(knot_rdata_t *rdata, -// knot_zone_contents_t *zone, -// knot_node_t *node, -// int pos) -//{ -// return; -// const knot_rdata_item_t *dname_item -// = knot_rdata_item(rdata, pos); - -// assert(dname_item); - -// if (dname_item != NULL) { -// knot_dname_t *dname = dname_item->dname; -// const knot_node_t *n = NULL; -// const knot_node_t *closest_encloser = NULL; -// const knot_node_t *prev = NULL; - -// if (knot_dname_is_wildcard(dname)) { -// find_and_set_wildcard_child(zone, node); -// } - -// int ret = knot_zone_contents_find_dname(zone, dname, &n, -// &closest_encloser, &prev); - -// // n = knot_zone_find_node(zone, dname); - -// if (ret == KNOT_EBADARG || ret == KNOT_EBADZONE) { -// // TODO: do some cleanup if needed -// return; -// } - -// assert(ret != KNOT_ZONE_NAME_FOUND -// || n == closest_encloser); - -// if (ret != KNOT_ZONE_NAME_FOUND -// && (closest_encloser != NULL)) { -// dbg_zone("Saving closest encloser to RDATA.\n"); -// // save pointer to the closest encloser -// knot_rdata_item_t *item = -// knot_rdata_get_item(rdata, pos); -// assert(item->dname != NULL); -// assert(item->dname->node == NULL); -// //skip_insert(list, (void *)item->dname, -// // (void *)closest_encloser->owner, NULL); -// item->dname->node = closest_encloser->owner->node; -// } -// } -//} +static void knot_zone_contents_adjust_rdata_item(knot_rdata_t *rdata, + knot_zone_contents_t *zone, + knot_node_t *node, int pos) +{ + const knot_rdata_item_t *dname_item = knot_rdata_item(rdata, pos); + + assert(dname_item); + + if (dname_item != NULL) { + knot_dname_t *dname = dname_item->dname; + + /* + * The case when dname.node is already set is handled here. + * No use to check it later. + */ + if (knot_dname_node(dname) != NULL + || !knot_dname_is_subdomain(dname, knot_node_owner( + knot_zone_contents_apex(zone)))) { + // The name's node is either already set + // or the name does not belong to the zone + dbg_zone_detail("Name's node either set or the name " + "does not belong to the zone (%p).\n", + knot_dname_node(dname)); + return; + } + + const knot_node_t *n = NULL; + const knot_node_t *closest_encloser = NULL; + const knot_node_t *prev = NULL; + + int ret = knot_zone_contents_find_dname(zone, dname, &n, + &closest_encloser, &prev); + + if (ret == KNOT_EBADARG || ret == KNOT_EBADZONE) { + // TODO: do some cleanup if needed + dbg_zone_detail("Failed to find the name in zone: %s\n", + knot_strerror(ret)); + return; + } + + assert(ret != KNOT_ZONE_NAME_FOUND || n == closest_encloser); + + if (ret != KNOT_ZONE_NAME_FOUND && (closest_encloser != NULL)) { + /*! + * \note There is no need to set closer encloser to the + * name. We may find the possible wildcard child + * right away. + * Having the closest encloser saved in the dname + * would disrupt the query processing algorithms + * anyway. + */ + + dbg_zone_verb("Trying to find wildcard child.\n"); + + n = knot_zone_contents_find_wildcard_child(zone, + closest_encloser); + + if (n != NULL) { + knot_dname_set_node(dname, (knot_node_t *)n); + dbg_zone_exec_detail( + char *name = knot_dname_to_str( + knot_node_owner(n)); + char *name2 = knot_dname_to_str(dname); + dbg_zone_detail("Set wildcard node %s " + "to RDATA dname %s.\n", + name, name2); + free(name); + free(name2); + ); + } + } + } +} /*----------------------------------------------------------------------------*/ /*! @@ -364,65 +404,64 @@ static int knot_zone_contents_dnames_from_node_to_table( * \param rrset RRSet to adjust RDATA in. * \param zone Zone to which the RRSet belongs. */ -//static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset, -// knot_zone_contents_t *zone, -// knot_node_t *node) -//{ -// uint16_t type = knot_rrset_type(rrset); +static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset, + knot_zone_contents_t *zone, + knot_node_t *node) +{ + uint16_t type = knot_rrset_type(rrset); -// knot_rrtype_descriptor_t *desc = -// knot_rrtype_descriptor_by_type(type); -// assert(desc); + knot_rrtype_descriptor_t *desc = + knot_rrtype_descriptor_by_type(type); + assert(desc); -// knot_rdata_t *rdata_first = knot_rrset_get_rdata(rrset); -// knot_rdata_t *rdata = rdata_first; + knot_rdata_t *rdata_first = knot_rrset_get_rdata(rrset); + knot_rdata_t *rdata = rdata_first; -// if (rdata == NULL) { -// return; -// } + if (rdata == NULL) { + return; + } -// while (rdata->next != rdata_first) { -// for (int i = 0; i < rdata->count; ++i) { -// if (desc->wireformat[i] -// == KNOT_RDATA_WF_COMPRESSED_DNAME -// || desc->wireformat[i] -// == KNOT_RDATA_WF_UNCOMPRESSED_DNAME -// || desc->wireformat[i] -// == KNOT_RDATA_WF_LITERAL_DNAME) { -// dbg_zone("Adjusting domain name at " -// "position %d of RDATA of record with owner " -// "%s and type %s.\n", -// i, rrset->owner->name, -// knot_rrtype_to_string(type)); - -// knot_zone_contents_adjust_rdata_item(rdata, -// zone, -// node, -// i); -// } -// } -// rdata = rdata->next; -// } + while (rdata->next != rdata_first) { + for (int i = 0; i < rdata->count; ++i) { + if (desc->wireformat[i] + == KNOT_RDATA_WF_COMPRESSED_DNAME + || desc->wireformat[i] + == KNOT_RDATA_WF_UNCOMPRESSED_DNAME + || desc->wireformat[i] + == KNOT_RDATA_WF_LITERAL_DNAME) { + dbg_zone("Adjusting domain name at " + "position %d of RDATA of record with owner " + "%s and type %s.\n", + i, rrset->owner->name, + knot_rrtype_to_string(type)); + + knot_zone_contents_adjust_rdata_item(rdata, + zone, node, + i); + } + } + rdata = rdata->next; + } -// for (int i = 0; i < rdata->count; ++i) { -// if (desc->wireformat[i] -// == KNOT_RDATA_WF_COMPRESSED_DNAME -// || desc->wireformat[i] -// == KNOT_RDATA_WF_UNCOMPRESSED_DNAME -// || desc->wireformat[i] -// == KNOT_RDATA_WF_LITERAL_DNAME) { -// dbg_zone("Adjusting domain name at " -// "position %d of RDATA of record with owner " -// "%s and type %s.\n", -// i, rrset->owner->name, -// knot_rrtype_to_string(type)); - -// knot_zone_contents_adjust_rdata_item(rdata, zone, -// node, i); -// } -// } + for (int i = 0; i < rdata->count; ++i) { + if (desc->wireformat[i] + == KNOT_RDATA_WF_COMPRESSED_DNAME + || desc->wireformat[i] + == KNOT_RDATA_WF_UNCOMPRESSED_DNAME + || desc->wireformat[i] + == KNOT_RDATA_WF_LITERAL_DNAME) { + dbg_zone("Adjusting domain name at " + "position %d of RDATA of record with owner " + "%s and type %s.\n", + i, rrset->owner->name, + knot_rrtype_to_string(type)); + + knot_zone_contents_adjust_rdata_item(rdata, zone, node, + i); + } + } -//} +} /*----------------------------------------------------------------------------*/ /*! @@ -435,32 +474,29 @@ static int knot_zone_contents_dnames_from_node_to_table( * \param node Zone node to adjust the RRSets in. * \param zone Zone to which the node belongs. */ -//static void knot_zone_contents_adjust_rrsets(knot_node_t *node, -// knot_zone_contents_t *zone) -//{ -// //return; -// knot_rrset_t **rrsets = knot_node_get_rrsets(node); -// short count = knot_node_rrset_count(node); - -// assert(count == 0 || rrsets != NULL); - -// for (int r = 0; r < count; ++r) { -// assert(rrsets[r] != NULL); -// dbg_zone("Adjusting next RRSet.\n"); -// knot_zone_contents_adjust_rdata_in_rrset(rrsets[r], zone, -// node); -// knot_rrset_t *rrsigs = rrsets[r]->rrsigs; -// if (rrsigs != NULL) { -// dbg_zone("Adjusting next RRSIGs.\n"); -// knot_zone_contents_adjust_rdata_in_rrset(rrsigs, -// zone, -// node); -// } -// } - -// free(rrsets); -//} +static void knot_zone_contents_adjust_rrsets(knot_node_t *node, + knot_zone_contents_t *zone) +{ + knot_rrset_t **rrsets = knot_node_get_rrsets(node); + short count = knot_node_rrset_count(node); + + assert(count == 0 || rrsets != NULL); + + for (int r = 0; r < count; ++r) { + assert(rrsets[r] != NULL); + dbg_zone("Adjusting next RRSet.\n"); + knot_zone_contents_adjust_rdata_in_rrset(rrsets[r], zone, + node); + knot_rrset_t *rrsigs = rrsets[r]->rrsigs; + if (rrsigs != NULL) { + dbg_zone("Adjusting next RRSIGs.\n"); + knot_zone_contents_adjust_rdata_in_rrset(rrsigs, zone, + node); + } + } + free(rrsets); +} /*----------------------------------------------------------------------------*/ /*! * \brief Adjusts zone node for faster query processing. @@ -481,35 +517,24 @@ static int knot_zone_contents_dnames_from_node_to_table( static void knot_zone_contents_adjust_node(knot_node_t *node, knot_zone_contents_t *zone) { - -dbg_zone_exec( - char *name = knot_dname_to_str(node->owner); - dbg_zone("----- Adjusting node %s -----\n", name); - free(name); -); - // adjust domain names in RDATA - /*! - * \note This is unnecessary, as the code in adjust_rdata_item() is not - * reachable anyway. However, it's not clear why we disabled the - * code, so this would need further investigation. - */ - //knot_zone_contents_adjust_rrsets(node, zone); + /*! \note Enabled again after a LONG time. Should test thoroughly. */ + knot_zone_contents_adjust_rrsets(node, zone); -dbg_zone_exec( +dbg_zone_exec_detail( if (knot_node_parent(node)) { char *name = knot_dname_to_str(knot_node_owner( knot_node_parent(node))); - dbg_zone("Parent: %s\n", name); - dbg_zone("Parent is delegation point: %s\n", + dbg_zone_detail("Parent: %s\n", name); + dbg_zone_detail("Parent is delegation point: %s\n", knot_node_is_deleg_point(knot_node_parent(node)) ? "yes" : "no"); - dbg_zone("Parent is non-authoritative: %s\n", + dbg_zone_detail("Parent is non-authoritative: %s\n", knot_node_is_non_auth(knot_node_parent(node)) ? "yes" : "no"); free(name); } else { - dbg_zone("No parent!\n"); + dbg_zone_detail("No parent!\n"); } ); // delegation point / non-authoritative node @@ -539,17 +564,19 @@ dbg_zone_exec( int match = knot_zone_contents_find_nsec3_for_name(zone, knot_node_owner(node), &nsec3, &prev); + UNUSED(prev); + if (match != KNOT_ZONE_NAME_FOUND) { nsec3 = NULL; } knot_node_set_nsec3_node(node, (knot_node_t *)nsec3); - dbg_zone("Set flags to the node: \n"); - dbg_zone("Delegation point: %s\n", - knot_node_is_deleg_point(node) ? "yes" : "no"); - dbg_zone("Non-authoritative: %s\n", - knot_node_is_non_auth(node) ? "yes" : "no"); + dbg_zone_detail("Set flags to the node: \n"); + dbg_zone_detail("Delegation point: %s\n", + knot_node_is_deleg_point(node) ? "yes" : "no"); + dbg_zone_detail("Non-authoritative: %s\n", + knot_node_is_non_auth(node) ? "yes" : "no"); } /*----------------------------------------------------------------------------*/ @@ -578,19 +605,16 @@ static void knot_zone_contents_adjust_node_in_tree( return; } - /* - * 1) Set previous node pointer. - */ - knot_node_set_previous(node, args->previous_node); +dbg_zone_exec_verb( + char *name = knot_dname_to_str(node->owner); + dbg_zone_verb("----- Adjusting node %s -----\n", name); + free(name); +); - if (args->first_node == NULL) { - args->first_node = node; - } knot_zone_contents_t *zone = args->zone; /* - * 2) Store domain names to dname table. - * + * 1) Store domain names to dname table. * TODO: make optional! */ assert(zone->dname_table != NULL); @@ -605,13 +629,35 @@ static void knot_zone_contents_adjust_node_in_tree( } /* - * 3) Do other adjusting (flags, closest enclosers, wildcard children, + * 2) Do other adjusting (flags, closest enclosers, wildcard children, * etc.). */ knot_zone_contents_adjust_node(node, zone); +} + +/*----------------------------------------------------------------------------*/ + +static void knot_zone_contents_adjust_node_in_tree_ptr( + knot_zone_tree_node_t *tnode, void *data) +{ + assert(data != NULL); + assert(tnode != NULL); + assert(tnode->node != NULL); + + knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data; + knot_node_t *node = tnode->node; /* - * 4) Store previous node depending on the type of this node. + * 1) Set previous node pointer. + */ + knot_node_set_previous(node, args->previous_node); + + if (args->first_node == NULL) { + args->first_node = node; + } + + /* + * 2) Store previous node depending on the type of this node. */ if (!knot_node_is_non_auth(node) && knot_node_rrset_count(node) > 0) { @@ -639,8 +685,11 @@ static void knot_zone_contents_adjust_nsec3_node_in_tree( knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data; knot_node_t *node = tnode->node; - // set previous node - knot_node_set_previous(node, args->previous_node); + if (args->err != KNOT_EOK) { + dbg_xfrin_detail("Error during adjusting: %s, skipping node.\n", + knot_strerror(args->err)); + return; + } // assure that owner has proper node if (knot_dname_node(knot_node_owner(node)) == NULL) { @@ -661,6 +710,22 @@ static void knot_zone_contents_adjust_nsec3_node_in_tree( args->err = ret; return; } +} + +/*----------------------------------------------------------------------------*/ + +static void knot_zone_contents_adjust_nsec3_node_in_tree_ptr( + knot_zone_tree_node_t *tnode, void *data) +{ + assert(data != NULL); + assert(tnode != NULL); + assert(tnode->node != NULL); + + knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data; + knot_node_t *node = tnode->node; + + // set previous node + knot_node_set_previous(node, args->previous_node); // here is nothing to consider, all nodes are the same args->previous_node = node; @@ -710,9 +775,9 @@ dbg_zone_exec( uint8_t *hashed_name = NULL; size_t hash_size = 0; -dbg_zone_exec( +dbg_zone_exec_verb( char *n = knot_dname_to_str(name); - dbg_zone("Hashing name %s.\n", n); + dbg_zone_verb("Hashing name %s.\n", n); free(n); ); @@ -737,8 +802,7 @@ dbg_zone_exec( if (size == 0) { char *n = knot_dname_to_str(name); - dbg_zone("Error while encoding hashed name %s to " - "base32.\n", n); + dbg_zone("Error while encoding hashed name %s to base32.\n", n); free(n); if (name_b32 != NULL) { free(name_b32); @@ -749,7 +813,7 @@ dbg_zone_exec( assert(name_b32 != NULL); free(hashed_name); - dbg_zone("Base32-encoded hash: %s\n", name_b32); + dbg_zone_verb("Base32-encoded hash: %s\n", name_b32); /* Will be returned to caller, make sure it is released after use. */ *nsec3_name = knot_dname_new_from_str(name_b32, size, NULL); @@ -757,8 +821,7 @@ dbg_zone_exec( free(name_b32); if (*nsec3_name == NULL) { - dbg_zone("Error while creating domain name for hashed" - " name.\n"); + dbg_zone("Error while creating domain name for hashed name.\n"); return KNOT_ERROR; } @@ -767,7 +830,7 @@ dbg_zone_exec( if (ret == NULL) { dbg_zone("Error while creating NSEC3 domain name for " - "hashed name.\n"); + "hashed name.\n"); knot_dname_release(*nsec3_name); return KNOT_ERROR; } @@ -805,31 +868,14 @@ static int knot_zone_contents_find_in_tree(knot_zone_tree_t *tree, assert(previous != NULL); knot_node_t *found = NULL, *prev = NULL; -// knot_node_t *found2 = NULL, *prev2 = NULL; int exact_match = knot_zone_tree_get_less_or_equal(tree, name, &found, &prev); -// assert(prev != NULL); assert(exact_match >= 0); *node = found; *previous = prev; -// if (prev == NULL) { -// // either the returned node is the root of the tree, or it is -// // the leftmost node in the tree; in both cases node was found -// // set the previous node of the found node -// assert(exact_match); -// assert(found != NULL); -// *previous = knot_node_get_previous(found, 1); -// } else { -// // otherwise check if the previous node is not an empty -// // non-terminal -// *previous = (knot_node_rrset_count(prev) == 0) -// ? knot_node_get_previous(prev, 1) -// : prev; -// } - return exact_match; } @@ -852,7 +898,6 @@ static void knot_zone_contents_node_to_hash(knot_zone_tree_node_t *tnode, */ #ifdef USE_HASH_TABLE - //assert(zone->table != NULL); // add the node also to the hash table if authoritative, or deleg. point if (zone->table != NULL && ck_insert_item(zone->table, @@ -977,23 +1022,6 @@ static void knot_zone_contents_check_loops_in_tree(knot_zone_tree_node_t *tnode, args->zone, next_name, &next_node, &ce); -// char *name1 = knot_dname_to_str(next_name); -// char *name2 = (next_node != NULL) -// ? knot_dname_to_str(knot_node_owner(next_node)) -// : "none"; -// char *name3 = (ce != NULL) -// ? knot_dname_to_str(knot_node_owner(ce)) -// : "none"; -// printf("Searched: %s, found: %p (%s), %p (%s); ret: %d" -// ".\n", name1, next_node, name2, ce, name3, ret); -// free(name1); -// if (next_node != NULL) { -// free(name2); -// } -// if (ce != NULL) { -// free(name3); -// } - if (ret != KNOT_ZONE_NAME_FOUND && ce != NULL) { // try to find wildcard child @@ -1025,6 +1053,32 @@ static void knot_zone_contents_check_loops_in_tree(knot_zone_tree_node_t *tnode, } /*----------------------------------------------------------------------------*/ + +static int knot_zc_nsec3_parameters_match(const knot_rdata_t *rdata, + const knot_nsec3_params_t *params) +{ + assert(rdata != NULL && params != NULL); + + dbg_zone_detail("RDATA algo: %u, iterations: %u, salt length: %u, salt:" + " %.*s\n", + knot_rdata_nsec3_algorithm(rdata), + knot_rdata_nsec3_iterations(rdata), + knot_rdata_nsec3_salt_length(rdata), + knot_rdata_nsec3_salt_length(rdata), + knot_rdata_nsec3_salt(rdata)); + dbg_zone_detail("NSEC3PARAM algo: %u, iterations: %u, salt length: %u, " + "salt: %.*s\n", params->algorithm, params->iterations, + params->salt_length, params->salt_length, params->salt); + + return (knot_rdata_nsec3_algorithm(rdata) == params->algorithm + && knot_rdata_nsec3_iterations(rdata) == params->iterations + && knot_rdata_nsec3_salt_length(rdata) == params->salt_length + && strncmp((const char *)knot_rdata_nsec3_salt(rdata), + (const char *)params->salt, params->salt_length) + == 0); +} + +/*----------------------------------------------------------------------------*/ /* API functions */ /*----------------------------------------------------------------------------*/ @@ -1041,22 +1095,19 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex, return NULL; } -// printf("created cont: %p (%s)\n", -// contents, knot_dname_to_str(apex->owner)); - contents->apex = apex; contents->zone = zone; - knot_node_set_zone(apex, zone); + knot_node_set_zone(apex, contents->zone); contents->node_count = 1; - dbg_zone("Creating tree for normal nodes.\n"); + dbg_zone_verb("Creating tree for normal nodes.\n"); contents->nodes = malloc(sizeof(knot_zone_tree_t)); if (contents->nodes == NULL) { ERR_ALLOC_FAILED; goto cleanup; } - dbg_zone("Creating tree for NSEC3 nodes.\n"); + dbg_zone_verb("Creating tree for NSEC3 nodes.\n"); contents->nsec3_nodes = malloc(sizeof(knot_zone_tree_t)); if (contents->nsec3_nodes == NULL) { ERR_ALLOC_FAILED; @@ -1064,7 +1115,7 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex, } if (use_domain_table) { - dbg_zone("Creating domain name table.\n"); + dbg_zone_verb("Creating domain name table.\n"); contents->dname_table = knot_dname_table_new(); if (contents->dname_table == NULL) { ERR_ALLOC_FAILED; @@ -1077,20 +1128,20 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex, //contents->node_count = node_count; /* Initialize NSEC3 params */ - dbg_zone("Initializing NSEC3 parameters.\n"); + dbg_zone_verb("Initializing NSEC3 parameters.\n"); contents->nsec3_params.algorithm = 0; contents->nsec3_params.flags = 0; contents->nsec3_params.iterations = 0; contents->nsec3_params.salt_length = 0; contents->nsec3_params.salt = NULL; - dbg_zone("Initializing zone trees.\n"); + dbg_zone_verb("Initializing zone trees.\n"); if (knot_zone_tree_init(contents->nodes) != KNOT_EOK || knot_zone_tree_init(contents->nsec3_nodes) != KNOT_EOK) { goto cleanup; } - dbg_zone("Inserting apex into the zone tree.\n"); + dbg_zone_verb("Inserting apex into the zone tree.\n"); if (knot_zone_tree_insert(contents->nodes, apex) != KNOT_EOK) { dbg_zone("Failed to insert apex to the zone tree.\n"); goto cleanup; @@ -1098,14 +1149,14 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex, #ifdef USE_HASH_TABLE if (node_count > 0) { - dbg_zone("Creating hash table.\n"); + dbg_zone_verb("Creating hash table.\n"); contents->table = ck_create_table(node_count); if (contents->table == NULL) { goto cleanup; } // insert the apex into the hash table - dbg_zone("Inserting apex into the hash table.\n"); + dbg_zone_verb("Inserting apex into the hash table.\n"); if (ck_insert_item(contents->table, (const char *)knot_dname_name( knot_node_owner(apex)), @@ -1121,7 +1172,7 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex, // insert names from the apex to the domain table if (use_domain_table) { - dbg_zone("Inserting names from apex to table.\n"); + dbg_zone_verb("Inserting names from apex to table.\n"); int rc = knot_zone_contents_dnames_from_node_to_table( contents->dname_table, apex); if (rc != KNOT_EOK) { @@ -1133,7 +1184,7 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex, return contents; cleanup: - dbg_zone("Cleaning up.\n"); + dbg_zone_verb("Cleaning up.\n"); free(contents->dname_table); free(contents->nodes); free(contents->nsec3_nodes); @@ -1234,6 +1285,12 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone, return KNOT_EBADARG; } +dbg_zone_exec_detail( + char *name = knot_dname_to_str(knot_node_owner(node)); + dbg_zone_detail("Adding node to zone: %s.\n", name); + free(name); +); + int ret = 0; if ((ret = knot_zone_contents_check_node(zone, node)) != 0) { dbg_zone("Node check failed.\n"); @@ -1257,10 +1314,6 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone, } #ifdef USE_HASH_TABLE -// char *name = knot_dname_to_str(node->owner); -// dbg_zone("Adding node with owner %s to hash table.\n", name); -// free(name); - //assert(zone->table != NULL); // add the node also to the hash table if authoritative, or deleg. point if (zone->table != NULL && ck_insert_item(zone->table, @@ -1281,7 +1334,7 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone, return KNOT_EOK; } - dbg_zone("Creating parents of the node.\n"); + dbg_zone_detail("Creating parents of the node.\n"); knot_dname_t *chopped = knot_dname_left_chop(knot_node_owner(node)); @@ -1293,7 +1346,7 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone, } if (knot_dname_compare(knot_node_owner(zone->apex), chopped) == 0) { - dbg_zone("Zone apex is the parent.\n"); + dbg_zone_detail("Zone apex is the parent.\n"); knot_node_set_parent(node, zone->apex); // check if the node is not wildcard child of the parent @@ -1307,7 +1360,7 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone, = knot_zone_contents_get_node(zone, chopped)) == NULL && chopped != NULL) { /* Adding new dname to zone + add to table. */ - dbg_zone("Creating new node.\n"); + dbg_zone_detail("Creating new node.\n"); assert(chopped); next_node = knot_node_new(chopped, NULL, flags); @@ -1322,7 +1375,7 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone, knot_zone_contents_dnames_from_node_to_table( zone->dname_table, next_node); if (ret != KNOT_EOK) { - knot_node_free(&next_node, 0); + knot_node_free(&next_node); knot_dname_release(chopped); return ret; } @@ -1339,14 +1392,13 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone, == NULL); assert(knot_node_owner(next_node) == chopped); - dbg_zone("Inserting new node to zone tree.\n"); -// TREE_INSERT(zone->tree, knot_node, avl, next_node); + dbg_zone_detail("Inserting new node to zone tree.\n"); ret = knot_zone_tree_insert(zone->nodes, next_node); if (ret != KNOT_EOK) { dbg_zone("Failed to insert new node " - "to zone tree.\n"); + "to zone tree.\n"); /*! \todo Delete the node?? */ /* Directly discard. */ knot_dname_release(chopped); @@ -1354,11 +1406,11 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone, } #ifdef USE_HASH_TABLE -dbg_zone_exec( +dbg_zone_exec_detail( char *name = knot_dname_to_str( knot_node_owner(next_node)); - dbg_zone("Adding new node with owner %s to " - "hash table.\n", name); + dbg_zone_detail("Adding new node with owner %s to " + "hash table.\n", name); free(name); ); @@ -1369,7 +1421,7 @@ dbg_zone_exec( knot_dname_size(knot_node_owner(next_node)), (void *)next_node) != 0) { dbg_zone("Error inserting node into " - "hash table!\n"); + "hash table!\n"); /*! \todo Delete the node?? */ /* Directly discard. */ knot_dname_release(chopped); @@ -1390,7 +1442,7 @@ dbg_zone_exec( ++zone->node_count; - dbg_zone("Next parent.\n"); + dbg_zone_detail("Next parent.\n"); node = next_node; knot_dname_t *chopped_last = chopped; chopped = knot_dname_left_chop(chopped); @@ -1406,7 +1458,7 @@ dbg_zone_exec( assert(knot_node_parent(node) == NULL); knot_node_set_parent(node, next_node); - dbg_zone("Created all parents.\n"); + dbg_zone_detail("Created all parents.\n"); } /* Directly discard. */ @@ -1419,15 +1471,22 @@ dbg_zone_exec( /*----------------------------------------------------------------------------*/ int knot_zone_contents_add_rrset(knot_zone_contents_t *zone, - knot_rrset_t *rrset, knot_node_t **node, - knot_rrset_dupl_handling_t dupl, - int use_domain_table) + knot_rrset_t *rrset, knot_node_t **node, + knot_rrset_dupl_handling_t dupl, + int use_domain_table) { if (zone == NULL || rrset == NULL || zone->apex == NULL || zone->apex->owner == NULL || node == NULL) { return KNOT_EBADARG; } +dbg_zone_exec_detail( + char *name = knot_dname_to_str(knot_rrset_owner(rrset)); + dbg_zone_detail("Adding RRSet to zone contents: %s, type %s\n", + name, knot_rrtype_to_string(knot_rrset_type(rrset))); + free(name); +); + // check if the RRSet belongs to the zone if (knot_dname_compare(knot_rrset_owner(rrset), zone->apex->owner) != 0 @@ -1448,8 +1507,12 @@ int knot_zone_contents_add_rrset(knot_zone_contents_t *zone, int rc; /*! \todo REMOVE RRSET */ - rc = knot_node_add_rrset(*node, rrset, - dupl == KNOT_RRSET_DUPL_MERGE); + if (dupl == KNOT_RRSET_DUPL_MERGE) { + rc = knot_node_add_rrset_no_dupl(*node, rrset); + } else { + rc = knot_node_add_rrset(*node, rrset, 0); + } + if (rc < 0) { dbg_zone("Failed to add RRSet to node.\n"); return rc; @@ -1458,14 +1521,13 @@ int knot_zone_contents_add_rrset(knot_zone_contents_t *zone, int ret = rc; if (use_domain_table) { - dbg_zone("Saving RRSet to table.\n"); + dbg_zone_detail("Saving RRSet to table.\n"); rc = knot_zone_contents_dnames_from_rrset_to_table( zone->dname_table, rrset, 0, (*node)->owner); if (rc != KNOT_EOK) { dbg_zone("Error saving domain names from " - "RRSIGs to the domain name table.\n " - "The zone may be in an inconsistent " - "state.\n"); + "RRSIGs to the domain name table.\n " + "The zone may be in an inconsistent state.\n"); // WARNING: the zone is not in consistent state now - // there may be domain names in it that are not inserted // into the domain table @@ -1480,7 +1542,7 @@ int knot_zone_contents_add_rrset(knot_zone_contents_t *zone, knot_rrset_set_owner(rrset, (*node)->owner); } - dbg_zone("RRSet OK.\n"); + dbg_zone_detail("RRSet OK.\n"); return ret; } @@ -1493,11 +1555,13 @@ int knot_zone_contents_add_rrsigs(knot_zone_contents_t *zone, knot_rrset_dupl_handling_t dupl, int use_domain_table) { + dbg_zone_verb("Adding RRSIGs to zone contents.\n"); + if (zone == NULL || rrsigs == NULL || rrset == NULL || node == NULL || zone->apex == NULL || zone->apex->owner == NULL) { dbg_zone_exec( dbg_zone("Parameters: zone=%p, rrsigs=%p, rrset=%p, " - "node=%p\n", zone, rrsigs, rrset, node); + "node=%p\n", zone, rrsigs, rrset, node); if (zone != NULL) { dbg_zone("zone->apex=%p\n", zone->apex); if (zone->apex != NULL) { @@ -1522,7 +1586,7 @@ dbg_zone_exec( if (*rrset != NULL && (knot_dname_compare(knot_rrset_owner(rrsigs), knot_rrset_owner(*rrset)) != 0)) { - dbg_zone("RRSIGs does not belong to the given RRSet.\n"); + dbg_zone("RRSIGs do not belong to the given RRSet.\n"); return KNOT_EBADARG; } @@ -1548,8 +1612,8 @@ dbg_zone_exec( // find the RRSet in the node // take only the first RDATA from the RRSIGs - dbg_zone("Finding RRSet for type %s\n", - knot_rrtype_to_string( + dbg_zone_detail("Finding RRSet for type %s\n", + knot_rrtype_to_string( knot_rdata_rrsig_type_covered( knot_rrset_rdata(rrsigs)))); *rrset = knot_node_get_rrset( @@ -1563,28 +1627,28 @@ dbg_zone_exec( assert(*rrset != NULL); - // add all domain names from the RRSet to domain name table int rc; int ret = KNOT_EOK; rc = knot_rrset_add_rrsigs(*rrset, rrsigs, dupl); if (rc < 0) { - dbg_dname("Failed to add RRSIGs to RRSet.\n"); + dbg_zone("Failed to add RRSIGs to RRSet.\n"); return rc; } else if (rc > 0) { - assert(dupl == KNOT_RRSET_DUPL_MERGE); + assert(dupl == KNOT_RRSET_DUPL_MERGE || + dupl == KNOT_RRSET_DUPL_SKIP); ret = 1; } + // add all domain names from the RRSet to domain name table if (use_domain_table) { - dbg_zone("Saving RRSIG RRSet to table.\n"); + dbg_zone_detail("Saving RRSIG RRSet to table.\n"); rc = knot_zone_contents_dnames_from_rrset_to_table( - zone->dname_table, rrsigs, 0, (*rrset)->owner); + zone->dname_table, (*rrset)->rrsigs, 0, (*rrset)->owner); if (rc != KNOT_EOK) { dbg_zone("Error saving domain names from " - "RRSIGs to the domain name table.\n " - "The zone may be in an inconsistent " - "state.\n"); + "RRSIGs to the domain name table.\n " + "The zone may be in an inconsistent state.\n"); // WARNING: the zone is not in consistent state now - // there may be domain names in it that are not inserted // into the domain table @@ -1598,7 +1662,7 @@ dbg_zone_exec( knot_rrset_set_owner((*rrset)->rrsigs, (*rrset)->owner); } - dbg_zone("RRSIGs OK\n"); + dbg_zone_detail("RRSIGs OK\n"); return ret; } @@ -1617,6 +1681,7 @@ int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone, int ret = 0; if ((ret = knot_zone_contents_check_node(zone, node)) != 0) { + dbg_zone("Failed node check: %s\n", knot_strerror(ret)); return ret; } @@ -1624,6 +1689,8 @@ int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone, // TREE_INSERT(zone->nsec3_nodes, knot_node, avl, node); ret = knot_zone_tree_insert(zone->nsec3_nodes, node); if (ret != KNOT_EOK) { + dbg_zone("Failed to insert node into NSEC3 tree: %s.\n", + knot_strerror(ret)); return ret; } @@ -1632,7 +1699,8 @@ int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone, zone->dname_table, node); if (ret != KNOT_EOK) { /*! \todo Remove the node from the tree. */ - dbg_zone("Failed to add dnames into table.\n"); + dbg_zone("Failed to add dnames into table: %s.\n", + knot_strerror(ret)); return ret; } } @@ -1682,8 +1750,12 @@ int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *zone, int rc; /*! \todo REMOVE RRSET */ - rc = knot_node_add_rrset(*node, rrset, - dupl == KNOT_RRSET_DUPL_MERGE); + if (dupl == KNOT_RRSET_DUPL_MERGE) { + rc = knot_node_add_rrset_no_dupl(*node, rrset); + } else { + rc = knot_node_add_rrset(*node, rrset, 0); + } + if (rc < 0) { return rc; } @@ -1691,14 +1763,13 @@ int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *zone, int ret = rc; if (use_domain_table) { - dbg_zone("Saving NSEC3 RRSet to table.\n"); + dbg_zone_detail("Saving NSEC3 RRSet to table.\n"); rc = knot_zone_contents_dnames_from_rrset_to_table( zone->dname_table, rrset, 0, (*node)->owner); if (rc != KNOT_EOK) { dbg_zone("Error saving domain names from " - "RRSIGs to the domain name table.\n " - "The zone may be in an inconsistent " - "state.\n"); + "RRSIGs to the domain name table.\n " + "The zone may be in an inconsistent state.\n"); // WARNING: the zone is not in consistent state now - // there may be domain names in it that are not inserted // into the domain table @@ -1713,7 +1784,7 @@ int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *zone, knot_rrset_set_owner(rrset, (*node)->owner); } - dbg_zone("NSEC3 OK\n"); + dbg_zone_detail("NSEC3 OK\n"); return ret; } @@ -1740,9 +1811,6 @@ dbg_zone_exec_verb( *removed_hash = ck_remove_item(contents->table, (const char *)knot_dname_name(owner), knot_dname_size(owner)); -// int ret = ck_detete_item(contents->table, -// (const char *)knot_dname_name(owner), -// knot_dname_size(owner), NULL, 0); if (*removed_hash == NULL) { return KNOT_ENONODE; } @@ -1837,11 +1905,6 @@ knot_node_t *knot_zone_contents_get_node(const knot_zone_contents_t *zone, return NULL; } - // create dummy node to use for lookup -// knot_node_t *tmp = knot_node_new((knot_dname_t *)name, NULL); -// knot_node_t *n = TREE_FIND(zone->tree, knot_node, avl, tmp); -// knot_node_free(&tmp, 0); - knot_node_t *n; int ret = knot_zone_tree_get(zone->nodes, name, &n); if (ret != KNOT_EOK) { @@ -1861,10 +1924,6 @@ knot_node_t *knot_zone_contents_get_nsec3_node( return NULL; } - // create dummy node to use for lookup -// knot_node_t *tmp = knot_node_new((knot_dname_t *)name, NULL); -// knot_node_t *n = TREE_FIND(zone->nsec3_nodes, knot_node, avl, tmp); -// knot_node_free(&tmp, 0); knot_node_t *n; int ret = knot_zone_tree_get(zone->nsec3_nodes, name, &n); @@ -1899,11 +1958,11 @@ int knot_zone_contents_find_dname(const knot_zone_contents_t *zone, return KNOT_EBADARG; } -dbg_zone_exec( +dbg_zone_exec_verb( char *name_str = knot_dname_to_str(name); char *zone_str = knot_dname_to_str(zone->apex->owner); - dbg_zone("Searching for name %s in zone %s...\n", - name_str, zone_str); + dbg_zone_verb("Searching for name %s in zone %s...\n", + name_str, zone_str); free(name_str); free(zone_str); ); @@ -1928,14 +1987,14 @@ dbg_zone_exec( *node = found; *previous = prev; -dbg_zone_exec( +dbg_zone_exec_detail( char *name_str = (*node) ? knot_dname_to_str((*node)->owner) : "(nil)"; char *name_str2 = (*previous != NULL) ? knot_dname_to_str((*previous)->owner) : "(nil)"; - dbg_zone("Search function returned %d, node %s and prev: %s\n", - exact_match, name_str, name_str2); +dbg_zone_detail("Search function returned %d, node %s (%p) and prev: %s (%p)\n", + exact_match, name_str, *node, name_str2, *previous); if (*node) { free(name_str); @@ -1944,19 +2003,23 @@ dbg_zone_exec( free(name_str2); } ); - - *closest_encloser = *node; - // there must be at least one node with domain name less or equal to // the searched name if the name belongs to the zone (the root) - if (*node == NULL) { + if (*node == NULL && *previous == NULL) { return KNOT_EBADZONE; } - // TODO: this could be replaced by saving pointer to closest encloser - // in node + /* This function was quite out of date. The find_in_tree() function + * may return NULL in the 'found' field, so we cannot search for the + * closest encloser from this node. + */ + + if (exact_match) { + *closest_encloser = *node; + } else { + *closest_encloser = *previous; + assert(*closest_encloser != NULL); - if (!exact_match) { int matched_labels = knot_dname_matched_labels( knot_node_owner((*closest_encloser)), name); while (matched_labels < knot_dname_label_count( @@ -1968,11 +2031,11 @@ dbg_zone_exec( } dbg_zone_exec( char *n = knot_dname_to_str(knot_node_owner((*closest_encloser))); - dbg_zone("Closest encloser: %s\n", n); + dbg_zone_detail("Closest encloser: %s\n", n); free(n); ); - dbg_zone("find_dname() returning %d\n", exact_match); + dbg_zone_verb("find_dname() returning %d\n", exact_match); return (exact_match) ? KNOT_ZONE_NAME_FOUND @@ -2055,11 +2118,11 @@ int knot_zone_contents_find_dname_hash(const knot_zone_contents_t *zone, return KNOT_EBADARG; } -dbg_zone_exec( +dbg_zone_exec_verb( char *name_str = knot_dname_to_str(name); char *zone_str = knot_dname_to_str(zone->apex->owner); - dbg_zone("Searching for name %s in zone %s...\n", - name_str, zone_str); + dbg_zone_verb("Searching for name %s in zone %s...\n", + name_str, zone_str); free(name_str); free(zone_str); ); @@ -2092,9 +2155,9 @@ dbg_zone_exec( *node = (const knot_node_t *)item->value; *closest_encloser = *node; - dbg_zone("Found node in hash table: %p (owner %p, " - "labels: %d)\n", *node, (*node)->owner, - knot_dname_label_count((*node)->owner)); + dbg_zone_detail("Found node in hash table: %p (owner %p, " + "labels: %d)\n", *node, (*node)->owner, + knot_dname_label_count((*node)->owner)); assert(*node != NULL); assert(*closest_encloser != NULL); return KNOT_ZONE_NAME_FOUND; @@ -2104,34 +2167,22 @@ dbg_zone_exec( // chop leftmost labels until some node is found // copy the name for chopping - /* Local allocation, will be discarded. */ - //knot_dname_t *name_copy = knot_dname_deep_copy(name); -dbg_zone_exec( - //char *n = knot_dname_to_str(name_copy); - dbg_zone("Finding closest encloser..\nStarting with: %.*s\n", - (int)name_size, name_tmp); - //free(n); -); + + dbg_zone_detail("Finding closest encloser..\nStarting with: %.*s\n", + (int)name_size, name_tmp); while (item == NULL) { - //knot_dname_left_chop_no_copy(name_copy); knot_zone_contents_left_chop(name_tmp, &name_size); -dbg_zone_exec( - //char *n = knot_dname_to_str(name_copy); - dbg_zone("Chopped leftmost label: %.*s\n", - (int)name_size, name_tmp); - //free(n); +dbg_zone_exec_detail( + dbg_zone_detail("Chopped leftmost label: %.*s\n", + (int)name_size, name_tmp); ); // not satisfied in root zone!! - //assert(name_copy->label_count > 0); assert(name_size > 0); item = ck_find_item(zone->table, name_tmp, name_size); } - /* Directly discard. */ - //knot_dname_free(&name_copy); - assert(item != NULL); *closest_encloser = (const knot_node_t *)item->value; @@ -2165,9 +2216,16 @@ int knot_zone_contents_find_nsec3_for_name(const knot_zone_contents_t *zone, return ret; } -dbg_zone_exec( + // check if the NSEC3 tree is not empty + if (zone->nsec3_nodes->th_root == NULL) { + dbg_zone("NSEC3 tree is empty.\n"); + knot_dname_release(nsec3_name); + return KNOT_ENSEC3CHAIN; + } + +dbg_zone_exec_verb( char *n = knot_dname_to_str(nsec3_name); - dbg_zone("NSEC3 node name: %s.\n", n); + dbg_zone_verb("NSEC3 node name: %s.\n", n); free(n); ); @@ -2180,26 +2238,33 @@ dbg_zone_exec( knot_dname_release(nsec3_name); -dbg_zone_exec( +dbg_zone_exec_detail( if (found) { char *n = knot_dname_to_str(found->owner); - dbg_zone("Found NSEC3 node: %s.\n", n); + dbg_zone_detail("Found NSEC3 node: %s.\n", n); free(n); } else { - dbg_zone("Found no NSEC3 node.\n"); + dbg_zone_detail("Found no NSEC3 node.\n"); } if (prev) { assert(prev->owner); char *n = knot_dname_to_str(prev->owner); - dbg_zone("Found previous NSEC3 node: %s.\n", n); + dbg_zone_detail("Found previous NSEC3 node: %s.\n", n); free(n); } else { - dbg_zone("Found no previous NSEC3 node.\n"); + dbg_zone_detail("Found no previous NSEC3 node.\n"); } ); *nsec3_node = found; + // This check cannot be used now, the function returns proper return + // value if the node was not found +// if (*nsec3_node == NULL) { +// // there is no NSEC3 node even if there should be +// return KNOT_ENSEC3CHAIN; +// } + if (prev == NULL) { // either the returned node is the root of the tree, or it is // the leftmost node in the tree; in both cases node was found @@ -2211,7 +2276,55 @@ dbg_zone_exec( *nsec3_previous = prev; } - dbg_zone("find_nsec3_for_name() returning %d\n", exact_match); + dbg_zone_verb("find_nsec3_for_name() returning %d\n", exact_match); + + /* The previous may be from wrong NSEC3 chain. Search for previous + * from the right chain. Check iterations, hash algorithm and salt + * values and compare them to the ones from NSEC3PARAM. + */ + const knot_rrset_t *nsec3_rrset = knot_node_rrset(*nsec3_previous, + KNOT_RRTYPE_NSEC3); + const knot_rdata_t *nsec3_rdata = (nsec3_rrset != NULL) + ? knot_rrset_rdata(nsec3_rrset) + : NULL; + const knot_node_t *original_prev = *nsec3_previous; + + while (nsec3_rdata != NULL + && !knot_zc_nsec3_parameters_match(nsec3_rdata, + &zone->nsec3_params)) { + /* Try other RDATA if there are some. In case of name collision + * the node would contain records from both NSEC3 chains. + */ + if ((nsec3_rdata = knot_rrset_rdata_next( + nsec3_rrset, nsec3_rdata)) != NULL) { + continue; + } + + /* If there is none, try previous node. */ + + *nsec3_previous = knot_node_previous(*nsec3_previous); + nsec3_rrset = knot_node_rrset(*nsec3_previous, + KNOT_RRTYPE_NSEC3); + nsec3_rdata = (nsec3_rrset != NULL) + ? knot_rrset_rdata(nsec3_rrset) + : NULL; +dbg_zone_exec_detail( + char *name = (*nsec3_previous) + ? knot_dname_to_str( + knot_node_owner(*nsec3_previous)) + : "none"; + dbg_zone_detail("Previous node: %s, checking parameters...\n", + name); + if (*nsec3_previous) { + free(name); + } +); + if (*nsec3_previous == original_prev || nsec3_rdata == NULL) { + // cycle + *nsec3_previous = NULL; + break; + } + } return (exact_match) ? KNOT_ZONE_NAME_FOUND @@ -2257,23 +2370,18 @@ int knot_zone_contents_adjust(knot_zone_contents_t *zone) adjust_arg.first_node = NULL; adjust_arg.previous_node = NULL; adjust_arg.err = KNOT_EOK; -// adjust_arg.check_ver = check_ver; /* - * Adjust the NSEC3 nodes first. - * There are independent on the normal nodes, but the normal nodes are - * dependent on them. + * First of all we must set node.prev pointers, as these are used in + * the search functions. */ - - dbg_zone("Adjusting NSEC3 nodes.\n"); - int ret = knot_zone_tree_forward_apply_inorder( - zone->nsec3_nodes, - knot_zone_contents_adjust_nsec3_node_in_tree, - &adjust_arg); + dbg_zone("Setting 'prev' pointers to NSEC3 nodes.\n"); + int ret = knot_zone_tree_forward_apply_inorder(zone->nsec3_nodes, + knot_zone_contents_adjust_nsec3_node_in_tree_ptr, &adjust_arg); assert(ret == KNOT_EOK); if (adjust_arg.err != KNOT_EOK) { - dbg_zone("Failed to adjust NSEC3 nodes: %s\n", + dbg_zone("Failed to set 'prev' pointers to NSEC3 nodes: %s\n", knot_strerror(adjust_arg.err)); return adjust_arg.err; } @@ -2281,13 +2389,46 @@ int knot_zone_contents_adjust(knot_zone_contents_t *zone) // set the last node as previous of the first node if (adjust_arg.first_node) { knot_node_set_previous(adjust_arg.first_node, - adjust_arg.previous_node); + adjust_arg.previous_node); } dbg_zone("Done.\n"); adjust_arg.first_node = NULL; adjust_arg.previous_node = NULL; + dbg_zone("Setting 'prev' pointers to normal nodes.\n"); + ret = knot_zone_tree_forward_apply_inorder(zone->nodes, + knot_zone_contents_adjust_node_in_tree_ptr, &adjust_arg); + assert(ret == KNOT_EOK); + + if (adjust_arg.err != KNOT_EOK) { + dbg_zone("Failed to set 'prev' pointers to normal nodes: %s\n", + knot_strerror(adjust_arg.err)); + return adjust_arg.err; + } + + // set the last node as previous of the first node + assert(zone->apex == adjust_arg.first_node); + knot_node_set_previous(zone->apex, adjust_arg.previous_node); + dbg_zone("Done.\n"); + + /* + * Adjust the NSEC3 nodes first. + * There are independent on the normal nodes, but the normal nodes are + * dependent on them. + */ + + dbg_zone("Adjusting NSEC3 nodes.\n"); + ret = knot_zone_tree_forward_apply_inorder(zone->nsec3_nodes, + knot_zone_contents_adjust_nsec3_node_in_tree, &adjust_arg); + assert(ret == KNOT_EOK); + + if (adjust_arg.err != KNOT_EOK) { + dbg_zone("Failed to adjust NSEC3 nodes: %s\n", + knot_strerror(adjust_arg.err)); + return adjust_arg.err; + } + dbg_zone("Adjusting normal nodes.\n"); ret = knot_zone_tree_forward_apply_inorder(zone->nodes, knot_zone_contents_adjust_node_in_tree, @@ -2300,9 +2441,6 @@ int knot_zone_contents_adjust(knot_zone_contents_t *zone) return adjust_arg.err; } - assert(zone->apex == adjust_arg.first_node); - knot_node_set_previous(zone->apex, adjust_arg.previous_node); - dbg_zone("Done.\n"); return ret; @@ -2339,58 +2477,6 @@ int knot_zone_contents_check_loops(knot_zone_contents_t *zone) /*----------------------------------------------------------------------------*/ -int knot_zone_contents_adjust_old(knot_zone_contents_t *zone) -{ - if (zone == NULL) { - return KNOT_EBADARG; - } - - // load NSEC3PARAM (needed on adjusting function) - knot_zone_contents_load_nsec3param(zone); - - knot_zone_adjust_arg_t adjust_arg; - adjust_arg.zone = zone; - adjust_arg.first_node = NULL; - adjust_arg.previous_node = NULL; -// adjust_arg.check_ver = check_ver; - adjust_arg.err = KNOT_EOK; - - dbg_zone("Adjusting normal nodes.\n"); - int ret = knot_zone_tree_forward_apply_inorder(zone->nodes, - knot_zone_contents_adjust_node_in_tree, - &adjust_arg); - if (ret != KNOT_EOK) { - return ret; - } - if (adjust_arg.err != KNOT_EOK) { - dbg_zone("Failed node adjusting: %s\n", - knot_strerror(adjust_arg.err)); - return adjust_arg.err; - } - - dbg_zone("Done.\n"); - - assert(zone->apex == adjust_arg.first_node); - knot_node_set_previous(zone->apex, adjust_arg.previous_node); - - adjust_arg.first_node = NULL; - adjust_arg.previous_node = NULL; - - dbg_zone("Adjusting NSEC3 nodes.\n"); - ret = knot_zone_tree_forward_apply_inorder(zone->nsec3_nodes, - knot_zone_contents_adjust_nsec3_node_in_tree, &adjust_arg); - - dbg_zone("Done.\n"); - if (adjust_arg.first_node) { - knot_node_set_previous(adjust_arg.first_node, - adjust_arg.previous_node); - } - - return ret; -} - -/*----------------------------------------------------------------------------*/ - int knot_zone_contents_load_nsec3param(knot_zone_contents_t *zone) { if (zone == NULL || zone->apex == NULL) { @@ -2418,8 +2504,8 @@ int knot_zone_contents_nsec3_enabled(const knot_zone_contents_t *zone) return KNOT_EBADARG; } - //return (zone->nsec3_params.algorithm != 0); - return (zone->nsec3_nodes->th_root != NULL); + return (zone->nsec3_params.algorithm != 0 + && zone->nsec3_nodes->th_root != NULL); } /*----------------------------------------------------------------------------*/ @@ -2662,16 +2748,15 @@ int knot_zone_contents_shallow_copy(const knot_zone_contents_t *from, // ret = ck_copy_table(from->table, &contents->table); ret = ck_shallow_copy(from->table, &contents->table); if (ret != 0) { - dbg_zone("knot_zone_contents_shallow_copy: " - "hash table copied\n"); + dbg_zone_verb("knot_zone_contents_shallow_copy: " + "hash table copied\n"); ret = KNOT_ERROR; goto cleanup; } } #endif - dbg_zone("knot_zone_contents_shallow_copy: " - "finished OK\n"); + dbg_zone("knot_zone_contents_shallow_copy: finished OK\n"); *to = contents; return KNOT_EOK; @@ -2740,13 +2825,11 @@ int knot_zone_contents_shallow_copy2(const knot_zone_contents_t *from, contents->node_count = from->node_count; contents->flags = from->flags; + // set the 'new' flag + knot_zone_contents_set_gen_new(contents); contents->zone = from->zone; -// /* Initialize NSEC3 params */ -// memcpy(&contents->nsec3_params, &from->nsec3_params, -// sizeof(knot_nsec3_params_t)); - if ((ret = knot_zone_tree_deep_copy(from->nodes, contents->nodes)) != KNOT_EOK || (ret = knot_zone_tree_deep_copy(from->nsec3_nodes, @@ -2758,8 +2841,8 @@ int knot_zone_contents_shallow_copy2(const knot_zone_contents_t *from, if (from->table != NULL) { ret = ck_deep_copy(from->table, &contents->table); if (ret != 0) { - dbg_zone("knot_zone_contents_shallow_copy: " - "hash table copied\n"); + dbg_zone_verb("knot_zone_contents_shallow_copy: " + "hash table copied\n"); ret = KNOT_ERROR; goto cleanup; } @@ -2903,16 +2986,6 @@ static void knot_zc_integrity_check_previous(const knot_node_t *node, ++check_data->errors; } -// if (knot_node_next(check_data->previous) != node) { -// char *name2 = knot_dname_to_str(knot_node_owner( -// knot_node_next(check_data->previous))); -// fprintf(stderr, "Wrong next node: node %s, next %s. " -// "Should be %s.\n", name_prev, name2 ,name); -// free(name2); - -// ++check_data->errors; -// } - free(name_prev); } } @@ -2995,11 +3068,6 @@ static void knot_zc_integrity_check_parent(const knot_node_t *node, && knot_dname_matched_labels(node_owner, parent_owner) == knot_dname_label_count(parent_owner)) { -// // increase the parent's children count -// fprintf(stderr, "Parent: %s, node: %s. Increasing children count.\n", -// pname, name); -// ++check_data->children; - // check the parent pointer const knot_node_t *parent = knot_node_parent(node); if (parent != check_data->parent) { @@ -3038,22 +3106,6 @@ static void knot_zc_integrity_check_parent(const knot_node_t *node, ++check_data->errors; } } - } else { - // not a direct child, check children count -// if (check_data->parent -// && knot_node_children(check_data->parent) -// != check_data->children) { -// fprintf(stderr, "Wrong children count: node %s, count: " -// "%u. Should be: %u\n", pname, -// knot_node_children(check_data->parent), -// check_data->children); - -// ++check_data->errors; -// } - - // reset the children count - //check_data->parent = node; -// check_data->children = 0; } free(pname); @@ -3110,15 +3162,6 @@ static int knot_zc_integrity_check_find_dname(const knot_zone_contents_t *zone, const char *node_name) { int ret = 0; - -// find_dname_data_t data_find; -// data_find.found = NULL; -// data_find.to_find = to_find; - -// int res = knot_zone_contents_dname_table_apply( -// (knot_zone_contents_t *)zone, -// find_in_dname_table, (void *)&data_find); -// assert(res == KNOT_EOK); knot_dname_t *found = knot_dname_table_find_dname(zone->dname_table, (knot_dname_t *)to_find); @@ -3364,27 +3407,12 @@ static void reset_new_nodes(knot_zone_tree_node_t *tree_node, void *data) /*----------------------------------------------------------------------------*/ -/*!< \todo remove debug code. */ -//static void print_child_count(knot_node_t *node, void *data) -//{ -// UNUSED(data); -// assert(node != NULL); - -// char *name = knot_dname_to_str(knot_node_owner(node)); -// fprintf(stderr, "Node: %s, children count: %d\n", name, -// knot_node_children(node)); -// free(name); -//} - -/*----------------------------------------------------------------------------*/ - static void count_nsec3_nodes(knot_zone_tree_node_t *tree_node, void *data) { assert(tree_node != NULL); assert(tree_node->node != NULL); assert(data != NULL); -// int *count = (int *)data; knot_node_t *apex = (knot_node_t *)data; assert(apex != NULL); @@ -3406,10 +3434,6 @@ int knot_zc_integrity_check_child_count(check_data_t *data) knot_zone_tree_init(nodes_copy); -// int ret = knot_zone_contents_tree_apply_inorder(data->contents, -// print_child_count, -// NULL); - int ret = knot_zone_tree_deep_copy(data->contents->nodes, nodes_copy); assert(ret == KNOT_EOK); @@ -3425,9 +3449,8 @@ int knot_zc_integrity_check_child_count(check_data_t *data) knot_zone_tree_forward_apply_inorder(nodes_copy, count_children, NULL); // add count of NSEC3 nodes to the apex' children count -// int nsec3_nodes = 0; - dbg_zone("Children count of new apex before NSEC3: %d\n", - data->contents->apex->new_node->children); + fprintf(stderr, "Children count of new apex before NSEC3: %d\n", + data->contents->apex->new_node->children); knot_zone_tree_forward_apply_inorder(data->contents->nsec3_nodes, count_nsec3_nodes, (void *)apex_copy); @@ -3435,7 +3458,6 @@ int knot_zc_integrity_check_child_count(check_data_t *data) // now compare the children counts // iterate over the old zone and search for nodes in the copy -// data->children = nsec3_nodes; knot_zone_tree_forward_apply_inorder(nodes_copy, check_child_count, (void *)data); diff --git a/src/libknot/zone/zone-contents.h b/src/libknot/zone/zone-contents.h index 3143ef9..2ca333e 100644..100755 --- a/src/libknot/zone/zone-contents.h +++ b/src/libknot/zone/zone-contents.h @@ -55,7 +55,7 @@ typedef struct knot_zone_contents_t { knot_nsec3_params_t nsec3_params; - /*! + /*! * \todo Unify the use of this field - authoritative nodes vs. all. */ uint node_count; @@ -80,17 +80,14 @@ typedef struct knot_zone_contents_t { /*----------------------------------------------------------------------------*/ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex, - uint node_count, - int use_domain_table, - struct knot_zone *zone); - -//short knot_zone_contents_generation(const knot_zone_contents_t *contents); + uint node_count, + int use_domain_table, + struct knot_zone *zone); int knot_zone_contents_gen_is_old(const knot_zone_contents_t *contents); int knot_zone_contents_gen_is_new(const knot_zone_contents_t *contents); int knot_zone_contents_gen_is_finished(const knot_zone_contents_t *contents); -//void knot_zone_contents_switch_generation(knot_zone_contents_t *contents); void knot_zone_contents_set_gen_old(knot_zone_contents_t *contents); void knot_zone_contents_set_gen_new(knot_zone_contents_t *contents); @@ -187,9 +184,6 @@ int knot_zone_contents_remove_node(knot_zone_contents_t *contents, const knot_node_t *node, knot_zone_tree_node_t **removed_tree, ck_hash_table_item_t **removed_hash); -//knot_zone_tree_node_t *knot_zone_contents_remove_node( -// knot_zone_contents_t *contents, const knot_node_t *node); - int knot_zone_contents_remove_nsec3_node(knot_zone_contents_t *contents, const knot_node_t *node, knot_zone_tree_node_t **removed); @@ -357,9 +351,6 @@ const knot_node_t *knot_zone_contents_apex( knot_node_t *knot_zone_contents_get_apex( const knot_zone_contents_t *contents); -//knot_dname_t *knot_zone_contents_name( -// const knot_zone_contents_t *contents); - /*! * \brief Optimizes zone by replacing domain names in RDATA with references to * domain names present in zone (as node owners). @@ -555,12 +546,6 @@ int knot_zone_contents_shallow_copy(const knot_zone_contents_t *from, int knot_zone_contents_shallow_copy2(const knot_zone_contents_t *from, knot_zone_contents_t **to); -//int knot_zone_contents_dnames_from_node_to_table( -// knot_dname_table_t *table, knot_node_t *node); - -//void knot_zone_contents_adjust_node(knot_node_t *node, -// knot_zone_contents_t *zone, int check_ver); - void knot_zone_contents_free(knot_zone_contents_t **contents); void knot_zone_contents_deep_free(knot_zone_contents_t **contents, diff --git a/src/libknot/zone/zone-diff.c b/src/libknot/zone/zone-diff.c new file mode 100755 index 0000000..d3fd961 --- /dev/null +++ b/src/libknot/zone/zone-diff.c @@ -0,0 +1,1004 @@ +/* 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 <assert.h> +#include <config.h> + +#include "libknot/util/error.h" +#include "libknot/util/debug.h" +#include "libknot/rdata.h" +#include "zone-diff.h" +#include "libknot/nameserver/name-server.h" + +struct zone_diff_param { + const knot_zone_contents_t *contents; + char nsec3; + knot_changeset_t *changeset; + int ret; +}; + +// forward declaration +static int knot_zone_diff_rdata(const knot_rrset_t *rrset1, + const knot_rrset_t *rrset2, + knot_changeset_t *changeset); + +static int knot_zone_diff_load_soas(const knot_zone_contents_t *zone1, + const knot_zone_contents_t *zone2, + knot_changeset_t *changeset) +{ + if (zone1 == NULL || zone2 == NULL || changeset == NULL) { + return KNOT_EBADARG; + } + + const knot_node_t *apex1 = knot_zone_contents_apex(zone1); + const knot_node_t *apex2 = knot_zone_contents_apex(zone2); + if (apex1 == NULL || apex2 == NULL) { + dbg_zonediff("zone_diff: " + "both zones must have apex nodes.\n"); + return KNOT_EBADARG; + } + + knot_rrset_t *soa_rrset1 = knot_node_get_rrset(apex1, KNOT_RRTYPE_SOA); + knot_rrset_t *soa_rrset2 = knot_node_get_rrset(apex2, KNOT_RRTYPE_SOA); + if (soa_rrset1 == NULL || soa_rrset2 == NULL) { + dbg_zonediff("zone_diff: " + "both zones must have apex nodes.\n"); + return KNOT_EBADARG; + } + + if (knot_rrset_rdata(soa_rrset1) == NULL || + knot_rrset_rdata(soa_rrset2) == NULL) { + dbg_zonediff("zone_diff: " + "both zones must have apex nodes with SOA " + "RRs.\n"); + return KNOT_EBADARG; + } + + int64_t soa_serial1 = + knot_rdata_soa_serial(knot_rrset_rdata(soa_rrset1)); + if (soa_serial1 == -1) { + dbg_zonediff("zone_diff: load_soas: Got bad SOA.\n"); + } + + int64_t soa_serial2 = + knot_rdata_soa_serial(knot_rrset_rdata(soa_rrset2)); + + if (soa_serial2 == -1) { + dbg_zonediff("zone_diff: load_soas: Got bad SOA.\n"); + } + + if (ns_serial_compare(soa_serial1, soa_serial2) == 0) { + dbg_zonediff("zone_diff: " + "second zone must have higher serial than the " + "first one. (%lld vs. %lld)\n", + soa_serial1, soa_serial2); + return KNOT_ENODIFF; + } + + if (ns_serial_compare(soa_serial1, soa_serial2) > 0) { + dbg_zonediff("zone_diff: " + "second zone must have higher serial than the " + "first one. (%lld vs. %lld)\n", + soa_serial1, soa_serial2); + return KNOT_ERANGE; + } + + /* We will not touch SOA later, now is the time to handle RRSIGs. */ + int ret = knot_zone_diff_rdata(knot_rrset_rrsigs(soa_rrset1), + knot_rrset_rrsigs(soa_rrset2), + changeset); + if (ret != KNOT_EOK) { + dbg_zonediff_verb("zone_diff: load_soas: Failed to diff SOAs' RRSIGs." + " Reason: %s.\n", knot_strerror(ret)); + /* This might not necasarilly be an error. */ + } + + assert(changeset); + + ret = knot_rrset_deep_copy(soa_rrset1, &changeset->soa_from, 1); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: load_soas: Cannot copy RRSet.\n"); + return ret; + } + + /* We MUST NOT save this RRSIG. */ + knot_rrset_deep_free(&changeset->soa_from->rrsigs, 1, 1, 1); + assert(changeset->soa_from->rrsigs == NULL); + + ret = knot_rrset_deep_copy(soa_rrset2, &changeset->soa_to, 1); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: load_soas: Cannot copy RRSet.\n"); + return ret; + } + + knot_rrset_deep_free(&changeset->soa_to->rrsigs, 1, 1, 1); + assert(changeset->soa_to->rrsigs == NULL); + + changeset->serial_from = soa_serial1; + changeset->serial_to = soa_serial2; + + dbg_zonediff_verb("zone_diff: load_soas: SOAs diffed. (%lld -> %lld)\n", + soa_serial1, soa_serial2); + + return KNOT_EOK; +} + +/*!< \todo Only use add or remove function, not both as they are the same. */ +/*!< \todo Also, this might be all handled by function in changesets.h!!! */ +static int knot_zone_diff_changeset_add_rrset(knot_changeset_t *changeset, + const knot_rrset_t *rrset) +{ + /* Remove all RRs of the RRSet. */ + if (changeset == NULL || rrset == NULL) { + dbg_zonediff("zone_diff: add_rrset: NULL parameters.\n"); + return KNOT_EBADARG; + } + + if (knot_rrset_rdata_rr_count(rrset) == 0) { + dbg_zonediff_detail("zone_diff: Nothing to add.\n"); + return KNOT_EOK; + } + + dbg_zonediff_detail("zone_diff: add_rrset: Adding RRSet (%d RRs):\n", + knot_rrset_rdata_rr_count(rrset)); + knot_rrset_dump(rrset, 1); + + knot_rrset_t *rrset_copy = NULL; + int ret = knot_rrset_deep_copy(rrset, &rrset_copy, 1); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: add_rrset: Cannot copy RRSet.\n"); + return ret; + } + if (rrset_copy->rrsigs != NULL) { + knot_rrset_deep_free(&rrset_copy->rrsigs, 1, 1, 1); + } + assert(knot_rrset_rrsigs(rrset_copy) == NULL); + + ret = knot_changeset_add_new_rr(changeset, rrset_copy, + XFRIN_CHANGESET_ADD); + if (ret != KNOT_EOK) { + /* We have to free the copy now! */ + knot_rrset_deep_free(&rrset_copy, 1, 1, 1); + dbg_zonediff("zone_diff: add_rrset: Could not add RRSet. " + "Reason: %s.\n", knot_strerror(ret)); + return ret; + } + + return KNOT_EOK; +} + +static int knot_zone_diff_changeset_remove_rrset(knot_changeset_t *changeset, + const knot_rrset_t *rrset) +{ + /* Remove all RRs of the RRSet. */ + if (changeset == NULL) { + dbg_zonediff("zone_diff: remove_rrset: NULL parameters.\n"); + return KNOT_EBADARG; + } + + if (rrset == NULL) { + return KNOT_EOK; + } + + if (knot_rrset_rdata_rr_count(rrset) == 0) { + dbg_zonediff_detail("zone_diff: Nothing to remove.\n"); + return KNOT_EOK; + } + + dbg_zonediff_detail("zone_diff: remove_rrset: Removing RRSet (%d RRs):\n", + knot_rrset_rdata_rr_count(rrset)); + knot_rrset_dump(rrset, 1); + + knot_rrset_t *rrset_copy = NULL; + int ret = knot_rrset_deep_copy(rrset, &rrset_copy, 1); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: remove_rrset: Cannot copy RRSet.\n"); + return ret; + } + if (rrset_copy->rrsigs != NULL) { + knot_rrset_deep_free(&rrset_copy->rrsigs, 1, 1, 1); + } + assert(knot_rrset_rrsigs(rrset_copy) == NULL); + + ret = knot_changeset_add_new_rr(changeset, rrset_copy, + XFRIN_CHANGESET_REMOVE); + if (ret != KNOT_EOK) { + /* We have to free the copy now. */ + knot_rrset_deep_free(&rrset_copy, 1, 1, 1); + dbg_zonediff("zone_diff: remove_rrset: Could not remove RRSet. " + "Reason: %s.\n", knot_strerror(ret)); + return ret; + } + + return KNOT_EOK; +} + +static int knot_zone_diff_add_node(const knot_node_t *node, + knot_changeset_t *changeset) +{ + if (node == NULL || changeset == NULL) { + dbg_zonediff("zone_diff: add_node: NULL arguments.\n"); + return KNOT_EBADARG; + } + + /* Add all rrsets from node. */ + const knot_rrset_t **rrsets = knot_node_rrsets(node); + if (rrsets == NULL) { + /* Empty non-terminals - legal case. */ + dbg_zonediff_detail("zone_diff: Node has no RRSets.\n"); + return KNOT_EOK; + } + + for (uint i = 0; i < knot_node_rrset_count(node); i++) { + assert(rrsets[i]); + int ret = knot_zone_diff_changeset_add_rrset(changeset, + rrsets[i]); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: add_node: Cannot add RRSet (%s).\n", + knot_strerror(ret)); + free(rrsets); + return ret; + } + } + + free(rrsets); + + return KNOT_EOK; +} + +static int knot_zone_diff_remove_node(knot_changeset_t *changeset, + const knot_node_t *node) +{ + if (changeset == NULL || node == NULL) { + dbg_zonediff("zone_diff: remove_node: NULL parameters.\n"); + return KNOT_EBADARG; + } + + dbg_zonediff("zone_diff: remove_node: Removing node:\n"); +dbg_zonediff_exec_detail( + knot_node_dump((knot_node_t *)node, 1); +); + + const knot_rrset_t **rrsets = knot_node_rrsets(node); + if (rrsets == NULL) { + dbg_zonediff_verb("zone_diff: remove_node: " + "Nothing to remove.\n"); + return KNOT_EOK; + } + + dbg_zonediff_detail("zone_diff: remove_node: Will be removing %d RRSets.\n", + knot_node_rrset_count(node)); + + /* Remove all the RRSets of the node. */ + for (uint i = 0; i < knot_node_rrset_count(node); i++) { + int ret = knot_zone_diff_changeset_remove_rrset(changeset, + rrsets[i]); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: remove_node: Failed to " + "remove rrset. Error: %s\n", + knot_strerror(ret)); + free(rrsets); + return ret; + } + } + + free(rrsets); + + return KNOT_EOK; +} + +static int knot_zone_diff_rdata_return_changes(const knot_rrset_t *rrset1, + const knot_rrset_t *rrset2, + knot_rrset_t **changes) +{ + if (rrset1 == NULL || rrset2 == NULL) { + dbg_zonediff("zone_diff: diff_rdata: NULL arguments. (%p) (%p).\n", + rrset1, rrset2); + return KNOT_EBADARG; + } + + /* + * Take one rdata from first list and search through the second list + * looking for an exact match. If no match occurs, it means that this + * particular RR has changed. + * After the list has been traversed, we have a list of + * changed/removed rdatas. This has awful computation time. + */ + dbg_zonediff_detail("zone_diff: diff_rdata: Diff of %s, type=%s. " + "RR count 1=%d RR count 2=%d.\n", + knot_dname_to_str(rrset1->owner), + knot_rrtype_to_string(rrset1->type), + knot_rrset_rdata_rr_count(rrset1), + knot_rrset_rdata_rr_count(rrset2)); + + /* Create fake RRSet, it will be easier to handle. */ + *changes = knot_rrset_new(knot_rrset_get_owner(rrset1), + knot_rrset_type(rrset1), + knot_rrset_class(rrset1), + knot_rrset_ttl(rrset1)); + if (*changes == NULL) { + dbg_zonediff("zone_diff: diff_rdata: " + "Could not create RRSet with changes.\n"); + return KNOT_ENOMEM; + } + + knot_rrtype_descriptor_t *desc = + knot_rrtype_descriptor_by_type(knot_rrset_type(rrset1)); + assert(desc); + + const knot_rdata_t *tmp_rdata = knot_rrset_rdata(rrset1); + while(tmp_rdata != NULL) { + const knot_rdata_t *tmp_rdata_second_rrset = + knot_rrset_rdata(rrset2); + while ((tmp_rdata_second_rrset != NULL) && + (knot_rdata_compare(tmp_rdata, + tmp_rdata_second_rrset, + desc->wireformat) != 0)) { + tmp_rdata_second_rrset = + knot_rrset_rdata_next(rrset2, + tmp_rdata_second_rrset); + } + if (tmp_rdata_second_rrset == NULL) { + /* + * This means that the while cycle above has finished + * because the list was traversed - there's no match. + */ + dbg_zonediff("zone_diff: diff_rdata: " + "No match for RR (type=%s owner=%s).\n", + knot_rrtype_to_string(knot_rrset_type(rrset1)), + knot_dname_to_str(rrset1->owner)); + /* Make a copy of tmp_rdata. */ + knot_rdata_t *tmp_rdata_copy = + knot_rdata_deep_copy(tmp_rdata, + knot_rrset_type(rrset1), + 1); + int ret = knot_rrset_add_rdata(*changes, + tmp_rdata_copy); + /*!< \todo dispose of the copy. */ + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_rdata: " + "Could not add rdata to rrset."); + knot_rrset_deep_free(changes, 1, 1, 0); + return ret; + } + } else { + dbg_zonediff_detail("zone_diff: diff_rdata: " + "Found matching RR for type %s.\n", + knot_rrtype_to_string(rrset1->type)); + } + tmp_rdata = knot_rrset_rdata_next(rrset1, tmp_rdata); + } + return KNOT_EOK; +} + +static int knot_zone_diff_rdata(const knot_rrset_t *rrset1, + const knot_rrset_t *rrset2, + knot_changeset_t *changeset) +{ + if ((changeset == NULL) || (rrset1 == NULL && rrset2 == NULL)) { + dbg_zonediff("zone_diff: diff_rdata: NULL arguments.\n"); + return KNOT_EBADARG; + } + /* + * The easiest solution is to remove all the RRs that had no match and + * to add all RRs that had no match, but those from second RRSet. */ + + /* Get RRs to remove from zone. */ + knot_rrset_t *to_remove = NULL; + if (rrset1 != NULL && rrset2 == NULL) { + assert(rrset1->type == KNOT_RRTYPE_RRSIG); + dbg_zonediff_detail("zone_diff: diff_rdata: RRSIG will be " + "removed.\n"); + int ret = knot_rrset_deep_copy(rrset1, &to_remove, 1); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_rdata: Could not copy rrset. " + "Error: %s.\n", knot_strerror(ret)); + return ret; + } + } else if (rrset1 != NULL && rrset2 != NULL) { + int ret = knot_zone_diff_rdata_return_changes(rrset1, rrset2, + &to_remove); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_rdata: Could not get changes. " + "Error: %s.\n", knot_strerror(ret)); + return ret; + } + } else { + dbg_zonediff("zone_diff: diff_rdata: These are not the diffs you " + "are looking for.\n"); + } + + dbg_zonediff_detail("zone_diff: diff_rdata: To remove:\n"); + knot_rrset_dump(to_remove, 1); + + int ret = knot_zone_diff_changeset_remove_rrset(changeset, + to_remove); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&to_remove, 1, 1, 1); + dbg_zonediff("zone_diff: diff_rdata: Could not remove RRs. " + "Error: %s.\n", knot_strerror(ret)); + return ret; + } + + /* Copy was made in add_rrset function, we can free now. */ + knot_rrset_deep_free(&to_remove, 1, 1, 1); + + /* Get RRs to add to zone. */ + knot_rrset_t *to_add = NULL; + if (rrset2 != NULL && rrset1 == NULL) { + assert(rrset2->type == KNOT_RRTYPE_RRSIG); + dbg_zonediff_detail("zone_diff: diff_rdata: RRSIG will be " + "added.\n"); + int ret = knot_rrset_deep_copy(rrset2, &to_add, 1); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_rdata: Could not copy rrset. " + "Error: %s.\n", knot_strerror(ret)); + return ret; + } + } else if (rrset1 != NULL && rrset2 != NULL) { + ret = knot_zone_diff_rdata_return_changes(rrset2, rrset1, + &to_add); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_rdata: Could not get changes. " + "Error: %s.\n", knot_strerror(ret)); + return ret; + } + } else { + dbg_zonediff("zone_diff: diff_rdata: These are not the diffs you " + "are looking for.\n"); + } + + dbg_zonediff_detail("zone_diff: diff_rdata: To add:\n"); + knot_rrset_dump(to_add, 1); + + ret = knot_zone_diff_changeset_add_rrset(changeset, + to_add); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&to_add, 1, 1, 1); + dbg_zonediff("zone_diff: diff_rdata: Could not remove RRs. " + "Error: %s.\n", knot_strerror(ret)); + return ret; + } + + /* Copy was made in add_rrset function, we can free now. */ + knot_rrset_deep_free(&to_add, 1, 1, 1); + + return KNOT_EOK; +} + +static int knot_zone_diff_rrsets(const knot_rrset_t *rrset1, + const knot_rrset_t *rrset2, + knot_changeset_t *changeset) +{ +// if (rrset1 == NULL || rrset2 == NULL) { +// /* This could happen when diffing RRSIGs. */ +// if (rrset1 == NULL && rrset2 != NULL) { +// dbg_zonediff("zone_diff: diff_rrsets: RRSIG missing in first" +// " rrset1.\n"); +// int ret = +// knot_zone_diff_changeset_add_rrset(changeset, +// rrset2); +// if (ret != KNOT_EOK) { +// dbg_zonediff("zone_diff: diff_rrsets: " +// "Cannot add RRSIG. (%s)\n", +// knot_strerror(ret)); +// } +// } else if (rrset1 != NULL && rrset2 == NULL) { +// dbg_zonediff("zone_diff: diff_rrsets: RRSIG missing in second" +// " rrset1.\n"); +// int ret = +// knot_zone_diff_changeset_remove_rrset(changeset, +// rrset1); +// if (ret != KNOT_EOK) { +// dbg_zonediff("zone_diff: diff_rrsets: " +// "Cannot remove RRSIG. (%s)\n", +// knot_strerror(ret)); +// } +// } +// dbg_zonediff_detail("zone_diff: diff_rrsets: " +// "NULL arguments (RRSIGs?). (%p) (%p)\n", +// rrset1, rrset2); +// return KNOT_EOK; +// } + + assert(knot_dname_compare(knot_rrset_owner(rrset1), + knot_rrset_owner(rrset2)) == 0); + assert(knot_rrset_type(rrset1) == knot_rrset_type(rrset2)); + + int ret = knot_zone_diff_rdata(knot_rrset_rrsigs(rrset1), + knot_rrset_rrsigs(rrset2), changeset); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_rrsets (%s:%s): Failed to diff RRSIGs. " + "They were: %p %p. (%s).\n", + knot_dname_to_str(rrset1->owner), + knot_rrtype_to_string(rrset1->type), + rrset1->rrsigs, + rrset2->rrsigs, knot_strerror(ret)); + } + + /* RRs (=rdata) have to be cross-compared, unfortunalely. */ + return knot_zone_diff_rdata(rrset1, rrset2, changeset); +} + +/*!< \todo this could be generic function for adding / removing. */ +static void knot_zone_diff_node(knot_node_t *node, void *data) +{ + if (node == NULL || data == NULL) { + dbg_zonediff("zone_diff: diff_node: NULL arguments.\n"); + return; + } + + struct zone_diff_param *param = (struct zone_diff_param *)data; + if (param->changeset == NULL || param->contents == NULL) { + dbg_zonediff("zone_diff: diff_node: NULL arguments.\n"); + param->ret = KNOT_EBADARG; + return; + } + + if (param->ret != KNOT_EOK) { + /* Error occured before, no point in continuing. */ + dbg_zonediff_detail("zone_diff: diff_node: error: %s\n", + knot_strerror(param->ret)); + return; + } + + /* + * First, we have to search the second tree to see if there's according + * node, if not, the whole node has been removed. + */ + const knot_node_t *node_in_second_tree = NULL; + const knot_dname_t *node_owner = knot_node_owner(node); + assert(node_owner); + if (!param->nsec3) { + node_in_second_tree = + knot_zone_contents_find_node(param->contents, + node_owner); + } else { + dbg_zonediff_verb("zone_diff: diff_node: NSEC3 zone.\n"); + node_in_second_tree = + knot_zone_contents_find_nsec3_node(param->contents, + node_owner); + } + + if (node_in_second_tree == NULL) { + dbg_zonediff_detail("zone_diff: diff_node: Node %s is not " + "in the second tree.\n", + knot_dname_to_str(node_owner)); + int ret = knot_zone_diff_remove_node(param->changeset, + node); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: failed to remove node.\n"); + param->ret = ret; + return; + } + param->ret = KNOT_EOK; + return; + } + + assert(node_in_second_tree != node); + + dbg_zonediff_detail("zone_diff: diff_node: Node %s is present in " + "both trees.\n", knot_dname_to_str(node_owner)); + /* The nodes are in both trees, we have to diff each RRSet. */ + const knot_rrset_t **rrsets = knot_node_rrsets(node); + if (rrsets == NULL) { + dbg_zonediff("zone_diff: Node in first tree has no RRSets.\n"); + /* + * If there are no RRs in the first tree, all of the RRs + * in the second tree will have to be inserted to ADD section. + */ + int ret = knot_zone_diff_add_node(node_in_second_tree, + param->changeset); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_node: " + "Could not add node from second tree. " + "Reason: %s.\n", knot_strerror(ret)); + } + param->ret = ret; + return; + } + + for (uint i = 0; i < knot_node_rrset_count(node); i++) { + /* Search for the RRSet in the node from the second tree. */ + const knot_rrset_t *rrset = rrsets[i]; + assert(rrset); + + /* SOAs are handled explicitly. */ + if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA) { + continue; + } + + const knot_rrset_t *rrset_from_second_node = + knot_node_rrset(node_in_second_tree, + knot_rrset_type(rrset)); + if (rrset_from_second_node == NULL) { + dbg_zonediff("zone_diff: diff_node: There is no counterpart " + "for RRSet of type %s in second tree.\n", + knot_rrtype_to_string(knot_rrset_type(rrset))); + /* RRSet has been removed. Make a copy and remove. */ + assert(rrset); + int ret = knot_zone_diff_changeset_remove_rrset( + param->changeset, + rrset); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_node: " + "Failed to remove RRSet.\n"); + param->ret = ret; + free(rrsets); + return; + } + } else { + dbg_zonediff("zone_diff: diff_node: There is a counterpart " + "for RRSet of type %s in second tree.\n", + knot_rrtype_to_string(knot_rrset_type(rrset))); + /* Diff RRSets. */ + int ret = knot_zone_diff_rrsets(rrset, + rrset_from_second_node, + param->changeset); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: " + "Failed to diff RRSets.\n"); + param->ret = ret; + free(rrsets); + return; + } + +// dbg_zonediff_verb("zone_diff: diff_node: Changes in " +// "RRSIGs.\n"); +// /*! \todo There is ad-hoc solution in the function, maybe handle here. */ +// ret = knot_zone_diff_rrsets(rrset->rrsigs, +// rrset_from_second_node->rrsigs, +// param->changeset); +// if (ret != KNOT_EOK) { +// dbg_zonediff("zone_diff: " +// "Failed to diff RRSIGs.\n"); +// param->ret = ret; +// return; +// } + } + } + + free(rrsets); + + /*! \todo move to one function with the code above. */ + rrsets = knot_node_rrsets(node_in_second_tree); + if (rrsets == NULL) { + dbg_zonediff("zone_diff: Node in second tree has no RRSets.\n"); + /* + * This can happen when node in second + * tree is empty non-terminal and as such has no RRs. + * Whole node from the first tree has to be removed. + */ + // TODO following code creates duplicated RR in diff. + // IHMO such case should be handled here +// int ret = knot_zone_diff_remove_node(param->changeset, +// node); +// if (ret != KNOT_EOK) { +// dbg_zonediff("zone_diff: diff_node: " +// "Cannot remove node. Reason: %s.\n", +// knot_strerror(ret)); +// } + param->ret = KNOT_EOK; + return; + } + + for (uint i = 0; i < knot_node_rrset_count(node_in_second_tree); i++) { + /* Search for the RRSet in the node from the second tree. */ + const knot_rrset_t *rrset = rrsets[i]; + assert(rrset); + + /* SOAs are handled explicitly. */ + if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA) { + continue; + } + + const knot_rrset_t *rrset_from_first_node = + knot_node_rrset(node, + knot_rrset_type(rrset)); + if (rrset_from_first_node == NULL) { + dbg_zonediff("zone_diff: diff_node: There is no counterpart " + "for RRSet of type %s in first tree.\n", + knot_rrtype_to_string(knot_rrset_type(rrset))); + /* RRSet has been added. Make a copy and add. */ + assert(rrset); + int ret = knot_zone_diff_changeset_add_rrset( + param->changeset, + rrset); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_node: " + "Failed to add RRSet.\n"); + param->ret = ret; + free(rrsets); + return; + } + } else { + /* Already handled. */ + ; + } + } + + free(rrsets); + + assert(param->ret == KNOT_EOK); +} + +/*!< \todo possibly not needed! */ +static void knot_zone_diff_add_new_nodes(knot_node_t *node, void *data) +{ + assert(node); + if (node == NULL || data == NULL) { + dbg_zonediff("zone_diff: add_new_nodes: NULL arguments.\n"); + return; + } + + struct zone_diff_param *param = (struct zone_diff_param *)data; + if (param->changeset == NULL || param->contents == NULL) { + dbg_zonediff("zone_diff: add_new_nodes: NULL arguments.\n"); + param->ret = KNOT_EBADARG; + return; + } + + if (param->ret != KNOT_EOK) { + /* Error occured before, no point in continuing. */ + dbg_zonediff_detail("zone_diff: add_new_nodes: error: %s\n", + knot_strerror(param->ret)); + return; + } + + /* + * If a node is not present in the second zone, it is a new node + * and has to be added to changeset. Differencies on the RRSet level are + * already handled. + */ + const knot_zone_contents_t *other_zone = param->contents; + assert(other_zone); + + const knot_dname_t *node_owner = knot_node_owner(node); + /* + * Node should definitely have an owner, otherwise it would not be in + * the tree. + */ + assert(node_owner); + + knot_node_t *new_node = NULL; + if (!param->nsec3) { + new_node = knot_zone_contents_get_node(other_zone, node_owner); + } else { + new_node = knot_zone_contents_get_nsec3_node(other_zone, + node_owner); + } + + if (!new_node) { + assert(node); + int ret = knot_zone_diff_add_node(node, param->changeset); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: add_new_nodes: Cannot add " + "node: %s to changeset. Reason: %s.\n", + knot_dname_to_str(node->owner), + knot_strerror(ret)); + } + } + + assert(param->ret == KNOT_EOK); +} + +int knot_zone_contents_diff(const knot_zone_contents_t *zone1, + const knot_zone_contents_t *zone2, + knot_changeset_t *changeset) +{ + if (zone1 == NULL || zone2 == NULL) { + dbg_zonediff("zone_diff: NULL argument(s).\n"); + return KNOT_EBADARG; + } + +// /* Create changeset structure. */ +// *changeset = malloc(sizeof(knot_changeset_t)); +// if (*changeset == NULL) { +// ERR_ALLOC_FAILED; +// return KNOT_ENOMEM; +// } + memset(changeset, 0, sizeof(knot_changeset_t)); + + /* Settle SOAs first. */ + int ret = knot_zone_diff_load_soas(zone1, zone2, changeset); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: loas_SOAs failed with error: %s\n", + knot_strerror(ret)); + return ret; + } + + dbg_zonediff("zone_diff: SOAs loaded.\n"); + + /* Traverse one tree, compare every node, each RRSet with its rdata. */ + struct zone_diff_param param; + param.contents = zone2; + param.nsec3 = 0; + param.changeset = changeset; + param.ret = KNOT_EOK; + ret = knot_zone_contents_tree_apply_inorder( + (knot_zone_contents_t *)zone1, + knot_zone_diff_node, + ¶m); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: Tree traversal failed " + "with error: %s. Error from inner function: %s\n", + knot_strerror(ret), + knot_strerror(param.ret)); + return ret; + } + + /* Do the same for NSEC3 nodes. */ + param.nsec3 = 1; + ret = knot_zone_contents_nsec3_apply_inorder((knot_zone_contents_t *)zone1, knot_zone_diff_node, + ¶m); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: Tree traversal failed " + "with error: %s\n", + knot_strerror(ret)); + return ret; + } + + /* + * Some nodes may have been added. The code above will not notice, + * we have to go through the second tree and add missing nodes to + * changeset. + */ + param.nsec3 = 0; + param.contents = zone1; + ret = knot_zone_contents_tree_apply_inorder((knot_zone_contents_t *)zone2, + knot_zone_diff_add_new_nodes, + ¶m); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: Tree traversal failed " + "with error: %s. Error from inner function: %s\n", + knot_strerror(ret), + knot_strerror(param.ret)); + return ret; + } + + /* NSEC3 nodes. */ + param.nsec3 = 1; + param.contents = zone1; + ret = knot_zone_contents_nsec3_apply_inorder((knot_zone_contents_t *)zone2, + knot_zone_diff_add_new_nodes, + ¶m); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: Tree traversal failed " + "with error: %s\n", + knot_strerror(ret)); + return ret; + } + + return KNOT_EOK; +} + +#ifdef KNOT_ZONEDIFF_DEBUG +#ifdef DEBUG_ENABLE_DETAILS +static void knot_zone_diff_dump_changeset(knot_changeset_t *ch) +{ + dbg_zonediff_detail("Changeset FROM: %d\n", ch->serial_from); + rrset_dump_text(ch->soa_from, stderr); + dbg_zonediff_detail("\n"); + dbg_zonediff_detail("Changeset TO: %d\n", ch->serial_to); + rrset_dump_text(ch->soa_to, stderr); + dbg_zonediff_detail("\n"); + dbg_zonediff_detail("Adding %d RRs.\n", ch->add_count); + dbg_zonediff_detail("Removing %d RRs.\n", ch->remove_count); + + dbg_zonediff_detail("ADD section:\n"); + dbg_zonediff_detail("**********************************************\n"); + for (int i = 0; i < ch->add_count; i++) { + rrset_dump_text(ch->add[i], stderr); + dbg_zonediff_detail("\n"); + } + dbg_zonediff_detail("REMOVE section:\n"); + dbg_zonediff_detail("**********************************************\n"); + for (int i = 0; i < ch->remove_count; i++) { + rrset_dump_text(ch->remove[i], stderr); + dbg_zonediff_detail("\n"); + } +} +#endif +#endif + +int knot_zone_diff_create_changesets(const knot_zone_contents_t *z1, + const knot_zone_contents_t *z2, + knot_changesets_t **changesets) +{ + if (z1 == NULL || z2 == NULL) { + dbg_zonediff("zone_diff: create_changesets: NULL arguments.\n"); + return KNOT_EBADARG; + } + /* Create changesets. */ + int ret = knot_changeset_allocate(changesets); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: create_changesets: " + "Could not allocate changesets." + "Reason: %s.\n", knot_strerror(ret)); + return ret; + } + + memset((*changesets)->sets, 0, sizeof(knot_changeset_t)); + + ret = knot_zone_contents_diff(z1, z2, (*changesets)->sets); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: create_changesets: " + "Could not diff zones. " + "Reason: %s.\n", knot_strerror(ret)); + return ret; + } + + (*changesets)->count = 1; + + dbg_zonediff("Changesets created successfully!\n"); + dbg_zonediff_detail("Changeset dump:\n"); +dbg_zonediff_exec_detail( + knot_zone_diff_dump_changeset((*changesets)->sets); +); + + return KNOT_EOK; +} + +/* Mostly just for testing. We only shall diff zones in memory later. */ +//int knot_zone_diff_zones(const char *zonefile1, const char *zonefile2) +//{ + /* Compile test zones. */ +// int ret = zone_read("example.com.", "/home/jan/test/testzone1", "tmpzone1.db", 0); +// assert(ret == KNOT_EOK); +// ret = zone_read("example.com.", "/home/jan/test/testzone2", "tmpzone2.db", 0); +// assert(ret == KNOT_EOK); +// /* Load test zones. */ +// zloader_t *loader = NULL; +// int ret = knot_zload_open(&loader, "tmpzone1.db"); +// assert(ret == KNOT_EOK); +// knot_zone_t *z1 = knot_zload_load(loader); +// ret = knot_zload_open(&loader, "tmpzone2.db"); +// assert(ret == KNOT_EOK); +// knot_zone_t *z2 = knot_zload_load(loader); +// assert(z1 && z2); +// knot_changeset_t *changeset = malloc(sizeof(knot_changeset_t)); +// memset(changeset, 0, sizeof(knot_changeset_t)); +// assert(knot_zone_contents_diff(z1->contents, z2->contents, +// changeset) == KNOT_EOK); +// dbg_zonediff("Changeset created: From=%d to=%d.\n", changeset.serial_from, +// changeset.serial_to); +//// knot_changesets_t chngsets; +//// chngsets->sets = malloc(sizeof(knot_changeset_t)); +//// chngsets->sets[0] = changeset; +//// chngsets->count = 1; +//// chngsets->allocated = 1; +//// knot_zone_contents_t *new_zone = NULL; +//// ret = xfrin_apply_changesets(z1, chngsets, &new_zone); +//// if (ret != KNOT_EOK) { +//// dbg_zonediff("Application of changesets failed. (%s)\n", +//// knot_strerror(ret)); +//// } + +//// assert(new_zone); + +// /* Dump creted zone. */ +//// FILE *f = fopen("testovani", "w"); +//// zone_dump_text(new_zone, f); + +// knot_zone_deep_free(&z2, 0); +// knot_zone_deep_free(&z1, 0); +//// knot_zone_contents_deep_free(&new_zone, 1); +//// knot_zone_free(&z1); + +// knot_free_changeset(&changeset); +// exit(0); +//} + diff --git a/src/tests/common/da_tests.h b/src/libknot/zone/zone-diff.h index d51b7be..6e0eb1d 100644..100755 --- a/src/tests/common/da_tests.h +++ b/src/libknot/zone/zone-diff.h @@ -14,12 +14,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef _KNOTD_DA_TESTS_H_ -#define _KNOTD_DA_TESTS_H_ +#ifndef _KNOT_ZONE_DIFF_H_ +#define _KNOT_ZONE_DIFF_H_ -#include "common/libtap/tap_unit.h" +#include "libknot/zone/zone-contents.h" +#include "libknot/updates/changesets.h" -/* Unit API. */ -unit_api da_tests_api; +/*! \brief zone1 -> zone2 */ +int knot_zone_diff_create_changesets(const knot_zone_contents_t *z1, + const knot_zone_contents_t *z2, + knot_changesets_t **changesets); -#endif /* _KNOTD_DA_TESTS_H_ */ +#endif // _KNOT_ZONE_DIFF_H_ diff --git a/src/libknot/zone/zone-tree.c b/src/libknot/zone/zone-tree.c index 2b79f86..7de460a 100644..100755 --- a/src/libknot/zone/zone-tree.c +++ b/src/libknot/zone/zone-tree.c @@ -110,7 +110,7 @@ static void knot_zone_tree_free_node(knot_zone_tree_node_t *node, knot_zone_tree_free_node(node->avl.avl_right, free_data); if (free_data) { - knot_node_free(&node->node, 0); + knot_node_free(&node->node); } free(node); @@ -132,8 +132,7 @@ static int knot_zone_tree_deep_copy_node(knot_zone_tree_node_t *from, } int ret = knot_node_shallow_copy(from->node, &(*to)->node); -// printf("Copied node: %p to node %p. New node1: %p, new node 2: %p\n", -// from->node, (*to)->node, from->node->new_node, (*to)->node->new_node); + if (ret != KNOT_EOK) { dbg_zone_verb("Failed to do shallow copy of node.\n"); free(*to); @@ -155,7 +154,7 @@ static int knot_zone_tree_deep_copy_node(knot_zone_tree_node_t *from, dbg_zone_verb("Failed to do shallow copy of right subtree.\n"); knot_zone_tree_free_node((*to)->avl.avl_left, 1); (*to)->avl.avl_left = NULL; - knot_node_free(&(*to)->node, 0); + knot_node_free(&(*to)->node); free(*to); *to = NULL; return ret; @@ -250,7 +249,7 @@ int knot_zone_tree_get(knot_zone_tree_t *tree, const knot_dname_t *owner, knot_zone_tree_node_t *n = TREE_FIND(tree, knot_zone_tree_node, avl, tmp); - knot_node_free(&tmp_data, 0); + knot_node_free(&tmp_data); free(tmp); if (n != NULL) { @@ -313,7 +312,7 @@ int knot_zone_tree_get_less_or_equal(knot_zone_tree_t *tree, int exact_match = TREE_FIND_LESS_EQUAL( tree, knot_zone_tree_node, avl, tmp, &f, &prev); - knot_node_free(&tmp_data, 0); + knot_node_free(&tmp_data); free(tmp); *found = (exact_match > 0) ? f->node : NULL; @@ -356,9 +355,17 @@ dbg_zone_exec_detail( /*! \todo Here we assume that the 'prev' pointer always points * to an empty non-terminal. */ + /*! \todo What did I mean by the previous TODO?? + * Nevertheless, it seems to me that node->prev can be + * an empty non-terminal too, cannot it? + */ + dbg_zone_detail("Previous: %p\n", prev->node); *previous = (knot_node_rrset_count(prev->node) == 0) ? knot_node_get_previous(prev->node) : prev->node; + dbg_zone_detail("Previous: %p, is empty: %d\n", *previous, + (*previous) ? knot_node_is_empty(*previous) + : -1); } assert(exact_match >= 0); @@ -399,11 +406,9 @@ int knot_zone_tree_remove(knot_zone_tree_t *tree, /*! \todo How to know if this was successful? */ TREE_REMOVE(tree, knot_zone_tree_node, avl, tmp); - knot_node_free(&tmp_data, 0); + knot_node_free(&tmp_data); free(tmp); -// *removed = (n) ? n->node : NULL; -// free(n); *removed = n; return KNOT_EOK; diff --git a/src/libknot/zone/zone-tree.h b/src/libknot/zone/zone-tree.h index ca65fe6..95b4e23 100644..100755 --- a/src/libknot/zone/zone-tree.h +++ b/src/libknot/zone/zone-tree.h @@ -39,8 +39,6 @@ typedef struct knot_zone_tree_node { TREE_ENTRY(knot_zone_tree_node) avl; /*! \brief Zone tree data. */ knot_node_t *node; - /*! \brief Owner of the node. */ -// knot_dname_t *owner; } knot_zone_tree_node_t; /*----------------------------------------------------------------------------*/ diff --git a/src/libknot/zone/zone.c b/src/libknot/zone/zone.c index 122b014..65f810d 100644..100755 --- a/src/libknot/zone/zone.c +++ b/src/libknot/zone/zone.c @@ -33,6 +33,12 @@ #include "hash/cuckoo-hash-table.h" #include "zone/zone-contents.h" +/*! \brief Adaptor for knot_zone_deep_free() */ +static void knot_zone_dtor(struct ref_t *p) { + knot_zone_t *z = (knot_zone_t *)p; + knot_zone_deep_free(&z, 0); +} + /*----------------------------------------------------------------------------*/ /* API functions */ /*----------------------------------------------------------------------------*/ @@ -55,6 +61,13 @@ knot_zone_t *knot_zone_new_empty(knot_dname_t *name) // save the zone name dbg_zone("Setting zone name.\n"); zone->name = name; + + /* Initialize reference counting. */ + ref_init(&zone->ref, knot_zone_dtor); + + /* Set reference counter to 1, caller should release it after use. */ + knot_zone_retain(zone); + return zone; } @@ -133,14 +146,18 @@ void knot_zone_set_version(knot_zone_t *zone, time_t version) short knot_zone_is_master(const knot_zone_t *zone) { - return zone->master; + return zone->flags & KNOT_ZONE_MASTER; } /*----------------------------------------------------------------------------*/ void knot_zone_set_master(knot_zone_t *zone, short master) { - zone->master = master; + if (master) { + zone->flags |= KNOT_ZONE_MASTER; + } else { + zone->flags &= ~KNOT_ZONE_MASTER; + } } /*----------------------------------------------------------------------------*/ @@ -175,6 +192,7 @@ knot_zone_contents_t *knot_zone_switch_contents(knot_zone_t *zone, knot_zone_contents_t *old_contents = rcu_xchg_pointer(&zone->contents, new_contents); + return old_contents; } @@ -192,7 +210,7 @@ void knot_zone_free(knot_zone_t **zone) && !knot_zone_contents_gen_is_old((*zone)->contents)) { // zone is in the middle of an update, report dbg_zone("Destroying zone that is in the middle of an " - "update.\n"); + "update.\n"); } knot_dname_release((*zone)->name); @@ -221,7 +239,7 @@ void knot_zone_deep_free(knot_zone_t **zone, int destroy_dname_table) && !knot_zone_contents_gen_is_old((*zone)->contents)) { // zone is in the middle of an update, report dbg_zone("Destroying zone that is in the middle of an " - "update.\n"); + "update.\n"); } dbg_zone_exec( @@ -241,3 +259,21 @@ dbg_zone_exec( free(*zone); *zone = NULL; } + +void knot_zone_set_dtor(knot_zone_t *zone, int (*dtor)(struct knot_zone *)) +{ + if (zone != NULL) { + zone->dtor = dtor; + } +} + +void knot_zone_set_flag(knot_zone_t *zone, knot_zone_flag_t flag, unsigned on) +{ + if (zone != NULL) { + if (on) { + zone->flags |= flag; + } else { + zone->flags &= ~flag; + } + } +} diff --git a/src/libknot/zone/zone.h b/src/libknot/zone/zone.h index 331ef1f..31ff2ac 100644..100755 --- a/src/libknot/zone/zone.h +++ b/src/libknot/zone/zone.h @@ -34,6 +34,7 @@ #include "nsec3.h" #include "zone/dname-table.h" #include "common/tree.h" +#include "common/ref.h" #include "hash/cuckoo-hash-table.h" #include "zone-tree.h" @@ -41,11 +42,6 @@ #include "zone/zone-contents.h" /*----------------------------------------------------------------------------*/ - -//typedef TREE_HEAD(avl_tree, knot_node) avl_tree_t; -//struct event_t; - -/*----------------------------------------------------------------------------*/ /*! * \brief Return values for search functions. * @@ -58,6 +54,15 @@ enum knot_zone_retvals { typedef enum knot_zone_retvals knot_zone_retvals_t; +/*! + * \brief Zone flags. + */ +typedef enum knot_zone_flag_t { + KNOT_ZONE_SLAVE = 0 << 0, /*! Slave zone */ + KNOT_ZONE_MASTER = 1 << 0, /*! Master zone. */ + KNOT_ZONE_DISCARDED = 1 << 1 /*! Zone waiting to be discarded. */ +} knot_zone_flag_t; + /*----------------------------------------------------------------------------*/ /*! @@ -68,14 +73,14 @@ typedef enum knot_zone_retvals knot_zone_retvals_t; * double-free errors when destroying the zone. */ struct knot_zone { + ref_t ref; /*!< Reference counting. */ knot_dname_t *name; knot_zone_contents_t *contents; time_t version; - /*! \todo Set when loading zone. */ - short master; + unsigned flags; void *data; /*!< Pointer to generic zone-related data. */ int (*dtor)(struct knot_zone *); /*!< Data destructor. */ @@ -88,7 +93,7 @@ typedef struct knot_zone knot_zone_t; /*! * \brief Creates new empty DNS zone. * - * \notice Zone will be created without contents. + * \note Zone will be created without contents. * * \param name Zone owner. * @@ -152,6 +157,54 @@ void knot_zone_free(knot_zone_t **zone); */ void knot_zone_deep_free(knot_zone_t **zone, int destroy_dname_table); +/*! + * \brief Set destructor and initialize reference counter to 1. + * + * \param zone Related zone. + * \param dtor Destructor. + */ +void knot_zone_set_dtor(knot_zone_t *zone, int (*dtor)(struct knot_zone *)); + +/*! + * \brief Increment reference counter for dname. + * + * \param zone Referenced zone. + */ + static inline void knot_zone_retain(knot_zone_t *zone) { + if (zone != NULL) { + ref_retain(&zone->ref); + } +} + +/*! + * \brief Decrement reference counter for dname. + * + * \param zone Referenced zone. + */ + static inline void knot_zone_release(knot_zone_t *zone) { + if (zone != NULL) { + ref_release(&zone->ref); + } +} + +/*! + * \brief Return zone flags. + * + * \param zone Zone. + */ +static inline unsigned knot_zone_flags(knot_zone_t *zone) { + return zone->flags; +} + +/*! + * \brief Set zone flag. + * + * \param zone Zone. + * \param flag Respected flag. + * \param on 1 to set, 0 to unset flag. + */ +void knot_zone_set_flag(knot_zone_t *zone, knot_zone_flag_t flag, unsigned on); + #endif /*! @} */ diff --git a/src/libknot/zone/zonedb.c b/src/libknot/zone/zonedb.c index a9b6545..43b4489 100644..100755 --- a/src/libknot/zone/zonedb.c +++ b/src/libknot/zone/zonedb.c @@ -51,11 +51,11 @@ static int knot_zonedb_compare_zone_names(void *p1, void *p2) int ret = knot_dname_compare(zone1->name, zone2->name); -dbg_zonedb_exec( +dbg_zonedb_exec_detail( char *name1 = knot_dname_to_str(zone1->name); char *name2 = knot_dname_to_str(zone2->name); - dbg_zonedb("Compared names %s and %s, result: %d.\n", - name1, name2, ret); + dbg_zonedb_detail("Compared names %s and %s, result: %d.\n", + name1, name2, ret); free(name1); free(name2); ); @@ -64,23 +64,6 @@ dbg_zonedb_exec( } /*----------------------------------------------------------------------------*/ - -//static int knot_zonedb_replace_zone_in_list(void **list_item, void **new_zone) -//{ -// assert(list_item != NULL); -// assert(*list_item != NULL); -// assert(new_zone != NULL); -// assert(*new_zone != NULL); - -// dbg_zonedb("Replacing list item %p with new zone %p\n", -// *list_item, *new_zone); - -// *list_item = *new_zone; - -// return 0; -//} - -/*----------------------------------------------------------------------------*/ /* API functions */ /*----------------------------------------------------------------------------*/ @@ -152,63 +135,13 @@ knot_zone_t *knot_zonedb_remove_zone(knot_zonedb_t *db, // remove the zone from the skip list, but do not destroy it gen_tree_remove(db->zone_tree, &dummy_zone); -// if (destroy_zone) { -// // properly destroy the zone and all its contents -// knot_zone_deep_free(&z, 0); -// } - db->zone_count--; - //return KNOT_EOK; return z; } /*----------------------------------------------------------------------------*/ -//knot_zone_t *knot_zonedb_replace_zone(knot_zonedb_t *db, -// knot_zone_t *zone) -//{ -// knot_zone_t *z = knot_zonedb_find_zone(db, -// knot_node_owner(knot_zone_apex(zone))); -// if (z == NULL) { -// return NULL; -// } - -// /*! \todo The replace should be atomic!!! */ - -// dbg_zonedb("Found zone: %p\n", z); - -// int ret = skip_remove(db->zones, -// (void *)knot_node_owner(knot_zone_apex(zone)), -// NULL, NULL); -// if (ret != 0) { -// return NULL; -// } - -// dbg_zonedb("Removed zone, return value: %d\n", ret); -// dbg_zonedb("Old zone: %p\n", z); - -// ret = skip_insert(db->zones, -// (void *)knot_node_owner(knot_zone_apex(zone)), -// (void *)zone, NULL); - -// dbg_zonedb("Inserted zone, return value: %d\n", ret); - -// if (ret != 0) { -// // return the removed zone back -// skip_insert(db->zones, -// (void *)knot_node_owner(knot_zone_apex(z)), -// (void *)z, NULL); -// /*! \todo There may be problems and the zone may remain -// removed. */ -// return NULL; -// } - -// return z; -//} - -/*----------------------------------------------------------------------------*/ - knot_zone_t *knot_zonedb_find_zone(const knot_zonedb_t *db, const knot_dname_t *zone_name) { @@ -229,11 +162,7 @@ const knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db, knot_zone_t dummy_zone; dummy_zone.name = knot_dname_deep_copy(dname); void *found = NULL; - -// int exact_match = gen_tree_find_less_or_equal(db->zone_tree, -// &dummy_zone, -// &found); -// UNUSED(exact_match); + found = gen_tree_find(db->zone_tree, &dummy_zone); while (found == NULL && knot_dname_label_count(dummy_zone.name) > 0) { knot_dname_left_chop_no_copy(dummy_zone.name); @@ -351,54 +280,16 @@ static void delete_zone_from_db(void *node, void *data) knot_zone_t *zone = (knot_zone_t *)node; assert(zone); synchronize_rcu(); - knot_zone_deep_free(&zone, 0); + knot_zone_set_flag(zone, KNOT_ZONE_DISCARDED, 1); + knot_zone_release(zone); + zone = NULL; } void knot_zonedb_deep_free(knot_zonedb_t **db) { dbg_zonedb("Deleting zone db (%p).\n", *db); -// dbg_zonedb("Is it empty (%p)? %s\n", -// (*db)->zones, skip_is_empty((*db)->zones) ? "yes" : "no"); - -//dbg_zonedb_exec( -// int i = 1; -// char *name = NULL; -// while (zn != NULL) { -// dbg_zonedb("%d. zone: %p, key: %p\n", i, zn->value, -// zn->key); -// assert(zn->key == ((knot_zone_t *)zn->value)->apex->owner); -// name = knot_dname_to_str((knot_dname_t *)zn->key); -// dbg_zonedb(" zone name: %s\n", name); -// free(name); - -// zn = skip_next(zn); -// } - -// zn = skip_first((*db)->zones); -//); - -// while (zn != NULL) { -// zone = (knot_zone_t *)zn->value; -// assert(zone != NULL); - -// // remove the zone from the database -// skip_remove((*db)->zones, zn->key, NULL, NULL); -// // wait for all readers to finish -// synchronize_rcu; -// // destroy the zone -// knot_zone_deep_free(&zone, 0); - -// zn = skip_first((*db)->zones); -// } - -// assert(skip_is_empty((*db)->zones)); - -// skip_destroy_list(&(*db)->zones, NULL, NULL); gen_tree_destroy(&((*db)->zone_tree), delete_zone_from_db, NULL); assert((*db)->zone_tree == NULL); free(*db); *db = NULL; } - -/*----------------------------------------------------------------------------*/ - diff --git a/src/libknot/zone/zonedb.h b/src/libknot/zone/zonedb.h index d5a4992..81326bf 100644..100755 --- a/src/libknot/zone/zonedb.h +++ b/src/libknot/zone/zonedb.h @@ -85,9 +85,6 @@ int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone); knot_zone_t * knot_zonedb_remove_zone(knot_zonedb_t *db, const knot_dname_t *zone_name); -//knot_zone_t *knot_zonedb_replace_zone(knot_zonedb_t *db, -// knot_zone_t *zone); - /*! * \brief Finds zone exactly matching the given zone name. * diff --git a/src/tests/README b/src/tests/README index 2f299ad..2f299ad 100644..100755 --- a/src/tests/README +++ b/src/tests/README diff --git a/src/tests/common/acl_tests.c b/src/tests/common/acl_tests.c index c1884cd..c1884cd 100644..100755 --- a/src/tests/common/acl_tests.c +++ b/src/tests/common/acl_tests.c diff --git a/src/tests/common/acl_tests.h b/src/tests/common/acl_tests.h index a928e2d..a928e2d 100644..100755 --- a/src/tests/common/acl_tests.h +++ b/src/tests/common/acl_tests.h diff --git a/src/tests/common/da_tests.c b/src/tests/common/da_tests.c deleted file mode 100644 index 627e8aa..0000000 --- a/src/tests/common/da_tests.c +++ /dev/null @@ -1,330 +0,0 @@ -/* 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 "tests/common/da_tests.h" -#include "common/dynamic-array.h" -#include <unistd.h> -#include <urcu.h> - -static int da_tests_count(int argc, char *argv[]); -static int da_tests_run(int argc, char *argv[]); - -/* - * Unit API. - */ -unit_api da_tests_api = { - "Dynamic array", - &da_tests_count, - &da_tests_run -}; - -/* - * Unit implementation. - */ - -static const int DA_TEST_COUNT = 5; -static const int RCU_THREADS = 3; -static const int DA_FRAGMENT = 10; -static const int DA_DEF_SIZE = 1000; -static const int DA_OPERATIONS = 1000; -enum Operations { - DA_RESERVE = 0, - DA_OCCUPY = 1, - DA_RELEASE = 2, - DA_OPCOUNT = 3 -}; - -static int da_tests_count(int argc, char *argv[]) -{ - return DA_TEST_COUNT; -} - -static void do_something(int loops) -{ - int i; - int res = 1; - - static const int LOOPS = 10000; - - for (int j = 1; j <= LOOPS; ++j) { - for (i = 1; i <= loops; ++i) { - res *= i; - } - } -} - -static void *test_rcu_routine(void *obj) -{ - rcu_register_thread(); - rcu_read_lock(); - - do_something(1000); - - rcu_read_unlock(); - rcu_unregister_thread(); - - return NULL; -} - -static int test_rcu_threads() -{ - // Create threads - pthread_t *threads = malloc(RCU_THREADS * sizeof(pthread_t)); - for (int i = 0; i < RCU_THREADS; ++i) { - if (pthread_create(&threads[i], NULL, test_rcu_routine, NULL)) { - diag("rcu: failed to create thread %d", i); - free(threads); - return 0; - } - } - - // Join threads - void *pret = NULL; - for (int i = 0; i < RCU_THREADS; ++i) { - if (pthread_join(threads[i], &pret)) { - diag("rcu: failed to join thread %d", i); - free(threads); - return 0; - } - } - - synchronize_rcu(); - free(threads); - - return 1; -} - -static int test_da_init(da_array_t *arr) -{ - return da_initialize(arr, DA_DEF_SIZE, sizeof(uint)) == 0; -} - -static int test_da_random_op(da_array_t *arr) -{ - unsigned seed = (unsigned)time(0); - uint allocated = DA_DEF_SIZE; - uint size = 0; - - for (int i = 0; i < DA_OPERATIONS; ++i) { - int r = rand_r(&seed) % DA_OPCOUNT; - int count = rand_r(&seed) % DA_FRAGMENT + 1; - - switch (r) { - - // Perform reserve operation - case DA_RESERVE: - if (da_reserve(arr, count) >= 0 && - size <= allocated) { - if ((allocated - size) < count) { - allocated *= 2; - } - } else { - diag("dynamic-array: da_reserve(%p, %d) failed" - " (size %d, alloc'd %d)", - arr, count, size, allocated); - return 0; - } - break; - - // Perform occupy operation - case DA_OCCUPY: - if (da_occupy(arr, count) == 0) { - uint *items = (uint *) da_get_items(arr); - for (int j = 0; j < da_get_count(arr); ++j) { - items[j] = rand_r(&seed); - } - if (size <= allocated && - (allocated - size) >= count) { - size += count; - } else { - return 0; - } - } else { - diag("dynamic-array: da_occupy(%p, %d) failed" - " (size %d, alloc'd %d)", - arr, count, size, allocated); - return 0; - } - break; - - // Perform release operation - case DA_RELEASE: - if (arr->count > 0) { - count = (rand_r(&seed) % DA_FRAGMENT) % arr->count; - da_release(arr, count); - - if (size <= allocated && size >= count) { - size -= count; - } else { - return 0; - } - } - break; - - default: - break; - } - - // Check allocated / size - if (allocated != arr->allocated || size != arr->count) { - diag("dynamic-array: allocated memory %d (expected %d)" - " size %d (expected %d) mismatch", - arr->allocated, allocated, arr->count, size); - return 0; - } - } - - return 1; -} - -void *test_da_read(void *obj) -{ - rcu_register_thread(); - rcu_read_lock(); - - unsigned seed = (unsigned)time(0); - da_array_t *arr = (da_array_t *) obj; - int index = rand_r(&seed) % da_get_count(arr); - - note(" dynamic-array: read thread"); - note(" read thread: saving pointer to %d. item", index); - uint *item = &((uint *) da_get_items(arr))[index]; - note(" read thread: before: pointer: %p item: %u", item, *item); - - do_something(100000); - - note(" read thread after: pointer: %p item: %u", item, *item); - rcu_read_unlock(); - note(" read thread unlocked: pointer: %p item: %u", item, *item); - - do_something(10000); - - note(" read thread: now the item should be deallocated"); - //note(" read thread: pointer: %p item: %u", item, *item); - - rcu_unregister_thread(); - - return NULL; -} - -static int test_da_resize_holding(da_array_t *arr) -{ - int ret = 1; - rcu_register_thread(); - pthread_t reader; - - // Create thread for reading - note("dynamic-array: creating read threads"); - if (pthread_create(&reader, NULL, test_da_read, (void *)arr)) { - diag("dynamic-array: failed to create reading thread", - __func__); - rcu_unregister_thread(); - return 0; - } - - // Wait some time, so the other thread gets the item for reading - do_something(5000); - - // Force resize - note(" dynamic-array: array resized"); - if (da_reserve(arr, arr->allocated - arr->count + 1) == -1) { - diag("dynamic-array: da_reserve(%p, %d) failed", arr, - arr->allocated - arr->count + 1); - ret = 0; - } - - //Wait for the thread to finish - void *pret = NULL; - if (pthread_join(reader, &pret)) { - diag("dynamic-array: failed to join reading thread", - __func__); - ret = 0; - } - - rcu_unregister_thread(); - return ret; -} - -static int test_da_resize(da_array_t *arr) -{ - unsigned seed = (unsigned)time(0); - int orig_count = da_get_count(arr); - note("dynamic-array: allocated: %d, items: %d", arr->allocated, - orig_count); - // store the items currently in the array - int *items = (int *)malloc(orig_count * sizeof(int)); - for (int i = 0; i < orig_count; ++i) { - items[i] = ((int *)da_get_items(arr))[i]; - } - - // force resize - int res = 0; - while ((res = da_reserve(arr, 10)) == 0) { - int i = da_get_count(arr); - da_occupy(arr, 10); - for (; i < da_get_count(arr); ++i) { - ((int *)da_get_items(arr))[i] = rand_r(&seed); - } - } - - if (res < 0) { - diag("dynamic-array: failed to reserve space"); - return 0; - } - - int errors = 0; - for (int i = 0; i < orig_count; ++i) { - if (items[i] != ((int *)da_get_items(arr))[i]) { - diag("dynamic-array: Wrong item on position %d." - "Should be: %d, " - "present value: %d", i, items[i], - ((int *)da_get_items(arr))[i]); - ++errors; - } - } - - free(items); - - return errors == 0; -} - -static int da_tests_run(int argc, char *argv[]) -{ - // Init - rcu_init(); - da_array_t array; - - // Test 1: test rcu - ok(test_rcu_threads(), "dynamic-array: rcu tests"); - - // Test 2: init - ok(test_da_init(&array), "dynamic-array: init"); - - // Test 3: reserve/occupy random operations - ok(test_da_random_op(&array), - "dynamic-array: randomized reserve/occupy/release"); - - // Test 4: resizing array while holding an item - ok(test_da_resize_holding(&array), - "dynamic-array: resize array while holding an item"); - - // Test 5: resize - ok(test_da_resize(&array), "dynamic-array: resize array"); - - // Cleanup - da_destroy(&array); - return 0; -} diff --git a/src/tests/common/events_tests.c b/src/tests/common/events_tests.c index 0acd706..0acd706 100644..100755 --- a/src/tests/common/events_tests.c +++ b/src/tests/common/events_tests.c diff --git a/src/tests/common/events_tests.h b/src/tests/common/events_tests.h index b54b6da..b54b6da 100644..100755 --- a/src/tests/common/events_tests.h +++ b/src/tests/common/events_tests.h diff --git a/src/tests/common/fdset_tests.c b/src/tests/common/fdset_tests.c index 08e0577..08e0577 100644..100755 --- a/src/tests/common/fdset_tests.c +++ b/src/tests/common/fdset_tests.c diff --git a/src/tests/common/fdset_tests.h b/src/tests/common/fdset_tests.h index d29e1a9..d29e1a9 100644..100755 --- a/src/tests/common/fdset_tests.h +++ b/src/tests/common/fdset_tests.h diff --git a/src/tests/common/skiplist_tests.c b/src/tests/common/skiplist_tests.c index 4fe99ec..4fe99ec 100644..100755 --- a/src/tests/common/skiplist_tests.c +++ b/src/tests/common/skiplist_tests.c diff --git a/src/tests/common/skiplist_tests.h b/src/tests/common/skiplist_tests.h index ff91706..ff91706 100644..100755 --- a/src/tests/common/skiplist_tests.h +++ b/src/tests/common/skiplist_tests.h diff --git a/src/tests/common/slab_tests.c b/src/tests/common/slab_tests.c index f362ca0..5724a23 100644..100755 --- a/src/tests/common/slab_tests.c +++ b/src/tests/common/slab_tests.c @@ -23,6 +23,11 @@ #include "common/slab/slab.h" #include "knot/common.h" +/*! \brief Type-safe maximum macro. */ +#define SLAB_MAX(a, b) \ + ({ typeof (a) _a = (a); typeof (b) _b = (b); _a > _b ? _a : _b; }) + + /* Explicitly ask for symbols, * as the constructor and destructor * aren't created for test modules. @@ -119,7 +124,7 @@ static int slab_tests_run(int argc, char *argv[]) for(int i = 0; i < alloc_count; ++i) { double roll = rand() / (double) RAND_MAX; size_t bsize = roll * 2048; - bsize = MAX(bsize, 8); + bsize = SLAB_MAX(bsize, 8); if ((ptrs_i == 0) || (roll < 0.6)) { void* m = slab_alloc_alloc(&alloc, bsize); if (m == 0) { diff --git a/src/tests/common/slab_tests.h b/src/tests/common/slab_tests.h index 4d45fb8..4d45fb8 100644..100755 --- a/src/tests/common/slab_tests.h +++ b/src/tests/common/slab_tests.h diff --git a/src/tests/files/sample_conf b/src/tests/files/sample_conf index b15fce5..b15fce5 100644..100755 --- a/src/tests/files/sample_conf +++ b/src/tests/files/sample_conf diff --git a/src/tests/knot/conf_tests.c b/src/tests/knot/conf_tests.c index 61520ea..61520ea 100644..100755 --- a/src/tests/knot/conf_tests.c +++ b/src/tests/knot/conf_tests.c diff --git a/src/tests/knot/conf_tests.h b/src/tests/knot/conf_tests.h index dfd2fd7..dfd2fd7 100644..100755 --- a/src/tests/knot/conf_tests.h +++ b/src/tests/knot/conf_tests.h diff --git a/src/tests/knot/dthreads_tests.c b/src/tests/knot/dthreads_tests.c index 982329b..982329b 100644..100755 --- a/src/tests/knot/dthreads_tests.c +++ b/src/tests/knot/dthreads_tests.c diff --git a/src/tests/knot/dthreads_tests.h b/src/tests/knot/dthreads_tests.h index e41bdc5..e41bdc5 100644..100755 --- a/src/tests/knot/dthreads_tests.h +++ b/src/tests/knot/dthreads_tests.h diff --git a/src/tests/knot/journal_tests.c b/src/tests/knot/journal_tests.c index 7c8125e..5c89de9 100644..100755 --- a/src/tests/knot/journal_tests.c +++ b/src/tests/knot/journal_tests.c @@ -34,7 +34,7 @@ unit_api journal_tests_api = { /* * Unit implementation. */ -static const int JOURNAL_TEST_COUNT = 20; +static const int JOURNAL_TEST_COUNT = 21; /*! \brief Generate random string with given length. */ static int randstr(char* dst, size_t len) @@ -76,6 +76,7 @@ static int journal_tests_run(int argc, char *argv[]) int tmp_fd = mkstemp(jfn_buf); ok(tmp_fd >= 0, "journal: create temporary file"); skip(tmp_fd < 0, JOURNAL_TEST_COUNT - 1); + close(tmp_fd); /* Test 2: Create journal. */ const char *jfilename = jfn_buf; @@ -139,7 +140,9 @@ static int journal_tests_run(int argc, char *argv[]) journal_close(journal); /* Recreate journal = NORMAL mode. */ - remove(jfilename); + if (remove(jfilename) < 0) { + diag("journal: couldn't remove filename"); + } fsize = 8092; jsize = 512; ret = journal_create(jfilename, jsize); @@ -199,7 +202,7 @@ static int journal_tests_run(int argc, char *argv[]) ok(j && ret == 0, "journal: mapped entry data integrity check"); endskip; - /* Test 17: Make a transaction. */ + /* Test 16: Make a transaction. */ uint64_t tskey = 0x75750000; ret = journal_trans_begin(j); ok(j && ret == 0, "journal: TRANS begin"); @@ -208,16 +211,16 @@ static int journal_tests_run(int argc, char *argv[]) journal_write(j, tskey + i, tmpbuf, sizeof(tmpbuf)); } - /* Test 18: Check if uncommited node exists. */ + /* Test 17: Check if uncommited node exists. */ ret = journal_read(j, tskey + rand() % 16, NULL, chk_buf); ok(j && ret != 0, "journal: check for uncommited node"); - /* Test 19: Commit transaction. */ + /* Test 18: Commit transaction. */ ret = journal_trans_commit(j); int read_ret = journal_read(j, tskey + rand() % 16, NULL, chk_buf); ok(j && ret == 0 && read_ret == 0, "journal: transaction commit"); - /* Test 20: Rollback transaction. */ + /* Test 19: Rollback transaction. */ tskey = 0x6B6B0000; journal_trans_begin(j); for (int i = 0; i < 16; ++i) { @@ -228,7 +231,7 @@ static int journal_tests_run(int argc, char *argv[]) read_ret = journal_read(j, tskey + rand() % 16, NULL, chk_buf); ok(j && ret == 0 && read_ret != 0, "journal: transaction rollback"); - /* Test 16: Write random data. */ + /* Test 20: Write random data. */ ret = 0; for (int i = 0; i < 512; ++i) { int key = i; @@ -258,16 +261,17 @@ static int journal_tests_run(int argc, char *argv[]) break; } } - - /* Test 16: Check data integrity. */ ok(j && ret == 0, "journal: sustained mmap r/w"); + /* Test 21: Open + create journal. */ + journal_close(j); + remove(jfilename); + j = journal_open(jfilename, fsize, 0, 0); + ok(j != NULL, "journal: open+create from scratch"); + /* Close journal. */ journal_close(j); - /* Close temporary file fd. */ - close(tmp_fd); - /* Delete journal. */ remove(jfilename); diff --git a/src/tests/knot/journal_tests.h b/src/tests/knot/journal_tests.h index beec8ca..beec8ca 100644..100755 --- a/src/tests/knot/journal_tests.h +++ b/src/tests/knot/journal_tests.h diff --git a/src/tests/knot/server_tests.c b/src/tests/knot/server_tests.c index 5ae04d8..5ae04d8 100644..100755 --- a/src/tests/knot/server_tests.c +++ b/src/tests/knot/server_tests.c diff --git a/src/tests/knot/server_tests.h b/src/tests/knot/server_tests.h index 43ad0c1..43ad0c1 100644..100755 --- a/src/tests/knot/server_tests.h +++ b/src/tests/knot/server_tests.h diff --git a/src/tests/libknot/files/parsed_data b/src/tests/libknot/files/parsed_data Binary files differindex 4027c92..4027c92 100644..100755 --- a/src/tests/libknot/files/parsed_data +++ b/src/tests/libknot/files/parsed_data diff --git a/src/tests/libknot/files/parsed_data_queries b/src/tests/libknot/files/parsed_data_queries Binary files differindex 5857c87..5857c87 100644..100755 --- a/src/tests/libknot/files/parsed_data_queries +++ b/src/tests/libknot/files/parsed_data_queries diff --git a/src/tests/libknot/files/raw_data b/src/tests/libknot/files/raw_data Binary files differindex f94236b..f94236b 100644..100755 --- a/src/tests/libknot/files/raw_data +++ b/src/tests/libknot/files/raw_data diff --git a/src/tests/libknot/files/raw_data_queries b/src/tests/libknot/files/raw_data_queries Binary files differindex 9062d5a..9062d5a 100644..100755 --- a/src/tests/libknot/files/raw_data_queries +++ b/src/tests/libknot/files/raw_data_queries diff --git a/src/tests/libknot/libknot/cuckoo_tests.c b/src/tests/libknot/libknot/cuckoo_tests.c index c1306a3..8d22e36 100644..100755 --- a/src/tests/libknot/libknot/cuckoo_tests.c +++ b/src/tests/libknot/libknot/cuckoo_tests.c @@ -15,6 +15,7 @@ */ #include <time.h> #include <assert.h> +#include <string.h> #include "tests/libknot/libknot/cuckoo_tests.h" diff --git a/src/tests/libknot/libknot/cuckoo_tests.h b/src/tests/libknot/libknot/cuckoo_tests.h index b6b0db8..b6b0db8 100644..100755 --- a/src/tests/libknot/libknot/cuckoo_tests.h +++ b/src/tests/libknot/libknot/cuckoo_tests.h diff --git a/src/tests/libknot/libknot/dname_table_tests.c b/src/tests/libknot/libknot/dname_table_tests.c index 0d00a44..0d00a44 100644..100755 --- a/src/tests/libknot/libknot/dname_table_tests.c +++ b/src/tests/libknot/libknot/dname_table_tests.c diff --git a/src/tests/libknot/libknot/dname_table_tests.h b/src/tests/libknot/libknot/dname_table_tests.h index f3088e9..f3088e9 100644..100755 --- a/src/tests/libknot/libknot/dname_table_tests.h +++ b/src/tests/libknot/libknot/dname_table_tests.h diff --git a/src/tests/libknot/libknot/dname_tests.c b/src/tests/libknot/libknot/dname_tests.c index 35ac230..35ac230 100644..100755 --- a/src/tests/libknot/libknot/dname_tests.c +++ b/src/tests/libknot/libknot/dname_tests.c diff --git a/src/tests/libknot/libknot/dname_tests.h b/src/tests/libknot/libknot/dname_tests.h index a7d75aa..a7d75aa 100644..100755 --- a/src/tests/libknot/libknot/dname_tests.h +++ b/src/tests/libknot/libknot/dname_tests.h diff --git a/src/tests/libknot/libknot/edns_tests.c b/src/tests/libknot/libknot/edns_tests.c index ac5d130..4d1a37a 100644..100755 --- a/src/tests/libknot/libknot/edns_tests.c +++ b/src/tests/libknot/libknot/edns_tests.c @@ -495,12 +495,13 @@ static int test_edns_has_option() */ for (int i = 0; i < TEST_EDNS; i++) { knot_opt_rr_t *edns = knot_edns_new(); - assert(edns->option_count == 0); if (edns == NULL) { ERR_ALLOC_FAILED; return 0; } + + assert(edns->option_count == 0); for (int j = 0; j < test_edns_data[i].option_count; j++) { if (knot_edns_add_option(edns, diff --git a/src/tests/libknot/libknot/edns_tests.h b/src/tests/libknot/libknot/edns_tests.h index 4553234..4553234 100644..100755 --- a/src/tests/libknot/libknot/edns_tests.h +++ b/src/tests/libknot/libknot/edns_tests.h diff --git a/src/tests/libknot/libknot/node_tests.c b/src/tests/libknot/libknot/node_tests.c index 10b01fa..b252982 100644..100755 --- a/src/tests/libknot/libknot/node_tests.c +++ b/src/tests/libknot/libknot/node_tests.c @@ -76,7 +76,7 @@ static int test_node_create() errors++; diag("Failed to create node structure"); } - knot_node_free(&tmp, 0); + knot_node_free(&tmp); } return (errors == 0); } @@ -134,7 +134,7 @@ static int test_node_add_rrset() diag("Values in found rrset are wrong"); } - knot_node_free(&tmp, 0); + knot_node_free(&tmp); } return (errors == 0); @@ -166,7 +166,7 @@ static int test_node_get_rrset() diag("Failed to get proper rrset from node"); } } - knot_node_free(&nodes[i], 0); + knot_node_free(&nodes[i]); } return (errors == 0); @@ -194,7 +194,7 @@ static int test_node_get_parent() errors++; diag("Failed to get proper parent from node"); } - knot_node_free(&nodes[i], 0); + knot_node_free(&nodes[i]); } return (errors == 0); } @@ -230,7 +230,7 @@ static int test_node_sorting() // last = *((uint16_t *)node->key); // } - knot_node_free(&tmp, 0); + knot_node_free(&tmp); return (errors == 0); } @@ -244,7 +244,7 @@ static int test_node_delete() tmp_node = knot_node_new(&test_nodes[i].owner, test_nodes[i].parent, 0); - knot_node_free(&tmp_node, 0); + knot_node_free(&tmp_node); errors += (tmp_node != NULL); } @@ -269,9 +269,9 @@ static int test_node_set_parent() diag("Parent node is wrongly set."); errors++; } - knot_node_free(&tmp_node, 0); + knot_node_free(&tmp_node); } - knot_node_free(&tmp_parent, 0); + knot_node_free(&tmp_parent); return (errors == 0); } @@ -289,7 +289,7 @@ static int test_node_free_rrsets() // errors += (tmp_node->rrsets != NULL); - knot_node_free(&tmp_node, 0); + knot_node_free(&tmp_node); } return (errors == 0); } diff --git a/src/tests/libknot/libknot/node_tests.h b/src/tests/libknot/libknot/node_tests.h index a90179f..a90179f 100644..100755 --- a/src/tests/libknot/libknot/node_tests.h +++ b/src/tests/libknot/libknot/node_tests.h diff --git a/src/tests/libknot/libknot/nsec3_tests.c b/src/tests/libknot/libknot/nsec3_tests.c index 7b95549..7b95549 100644..100755 --- a/src/tests/libknot/libknot/nsec3_tests.c +++ b/src/tests/libknot/libknot/nsec3_tests.c diff --git a/src/tests/libknot/libknot/nsec3_tests.h b/src/tests/libknot/libknot/nsec3_tests.h index 10e7ed9..10e7ed9 100644..100755 --- a/src/tests/libknot/libknot/nsec3_tests.h +++ b/src/tests/libknot/libknot/nsec3_tests.h diff --git a/src/tests/libknot/libknot/packet_tests.c b/src/tests/libknot/libknot/packet_tests.c index 916328d..916328d 100644..100755 --- a/src/tests/libknot/libknot/packet_tests.c +++ b/src/tests/libknot/libknot/packet_tests.c diff --git a/src/tests/libknot/libknot/packet_tests.h b/src/tests/libknot/libknot/packet_tests.h index 5a8ce03..5a8ce03 100644..100755 --- a/src/tests/libknot/libknot/packet_tests.h +++ b/src/tests/libknot/libknot/packet_tests.h diff --git a/src/tests/libknot/libknot/query_tests.c b/src/tests/libknot/libknot/query_tests.c index 1e4e081..1e4e081 100644..100755 --- a/src/tests/libknot/libknot/query_tests.c +++ b/src/tests/libknot/libknot/query_tests.c diff --git a/src/tests/libknot/libknot/query_tests.h b/src/tests/libknot/libknot/query_tests.h index 037ecab..037ecab 100644..100755 --- a/src/tests/libknot/libknot/query_tests.h +++ b/src/tests/libknot/libknot/query_tests.h diff --git a/src/tests/libknot/libknot/rdata_tests.c b/src/tests/libknot/libknot/rdata_tests.c index 0c53613..0c53613 100644..100755 --- a/src/tests/libknot/libknot/rdata_tests.c +++ b/src/tests/libknot/libknot/rdata_tests.c diff --git a/src/tests/libknot/libknot/rdata_tests.h b/src/tests/libknot/libknot/rdata_tests.h index 1f43c91..1f43c91 100644..100755 --- a/src/tests/libknot/libknot/rdata_tests.h +++ b/src/tests/libknot/libknot/rdata_tests.h diff --git a/src/tests/libknot/libknot/response_tests.c b/src/tests/libknot/libknot/response_tests.c index f40bb76..f40bb76 100644..100755 --- a/src/tests/libknot/libknot/response_tests.c +++ b/src/tests/libknot/libknot/response_tests.c diff --git a/src/tests/libknot/libknot/response_tests.h b/src/tests/libknot/libknot/response_tests.h index c9a117b..c9a117b 100644..100755 --- a/src/tests/libknot/libknot/response_tests.h +++ b/src/tests/libknot/libknot/response_tests.h diff --git a/src/tests/libknot/libknot/rrset_tests.c b/src/tests/libknot/libknot/rrset_tests.c index 41284df..41284df 100644..100755 --- a/src/tests/libknot/libknot/rrset_tests.c +++ b/src/tests/libknot/libknot/rrset_tests.c diff --git a/src/tests/libknot/libknot/rrset_tests.h b/src/tests/libknot/libknot/rrset_tests.h index b0787d6..b0787d6 100644..100755 --- a/src/tests/libknot/libknot/rrset_tests.h +++ b/src/tests/libknot/libknot/rrset_tests.h diff --git a/src/tests/libknot/libknot/tsig_tests.c b/src/tests/libknot/libknot/tsig_tests.c index e0a3d34..e0a3d34 100644..100755 --- a/src/tests/libknot/libknot/tsig_tests.c +++ b/src/tests/libknot/libknot/tsig_tests.c diff --git a/src/tests/libknot/libknot/tsig_tests.h b/src/tests/libknot/libknot/tsig_tests.h index 8ea15f6..8ea15f6 100644..100755 --- a/src/tests/libknot/libknot/tsig_tests.h +++ b/src/tests/libknot/libknot/tsig_tests.h diff --git a/src/tests/libknot/libknot/zone_tests.c b/src/tests/libknot/libknot/zone_tests.c index 72aae39..54cd38d 100644..100755 --- a/src/tests/libknot/libknot/zone_tests.c +++ b/src/tests/libknot/libknot/zone_tests.c @@ -95,13 +95,13 @@ static int test_zone_create(knot_zone_contents_t **zone) if ((*zone) == NULL) { diag("zone: Failed to create zone."); - knot_node_free(&node, 0); + knot_node_free(&node); return 0; } if ((*zone)->apex != node) { diag("zone: Zone apex not set right."); - knot_node_free(&node, 0); + knot_node_free(&node); return 0; } @@ -133,7 +133,7 @@ static int test_zone_add_node(knot_zone_contents_t *zone, int nsec3) : knot_zone_contents_add_node(zone, node, 0, 0, 0))) != 0) { diag("zone: Failed to insert node into zone (returned" " %d).", res); - knot_node_free(&node, 0); + knot_node_free(&node); ++errors; } /* TODO check values in the node as well */ @@ -157,7 +157,7 @@ static int test_zone_add_node(knot_zone_contents_t *zone, int nsec3) KNOT_EBADZONE); ++errors; } - knot_node_free(&node, 0); + knot_node_free(&node); } //note("NULL zone"); @@ -179,7 +179,7 @@ static int test_zone_add_node(knot_zone_contents_t *zone, int nsec3) ++errors; } - knot_node_free(&node, 0); + knot_node_free(&node); //note("NULL node"); note("Inserting NULL node...\n"); @@ -211,7 +211,7 @@ static int test_zone_add_node(knot_zone_contents_t *zone, int nsec3) ++errors; } - knot_node_free(&node, 0); + knot_node_free(&node); } // check if all nodes are inserted diff --git a/src/tests/libknot/libknot/zone_tests.h b/src/tests/libknot/libknot/zone_tests.h index 5539709..5539709 100644..100755 --- a/src/tests/libknot/libknot/zone_tests.h +++ b/src/tests/libknot/libknot/zone_tests.h diff --git a/src/tests/libknot/libknot/zone_tree_tests.c b/src/tests/libknot/libknot/zone_tree_tests.c index 207afea..207afea 100644..100755 --- a/src/tests/libknot/libknot/zone_tree_tests.c +++ b/src/tests/libknot/libknot/zone_tree_tests.c diff --git a/src/tests/libknot/libknot/zone_tree_tests.h b/src/tests/libknot/libknot/zone_tree_tests.h index 4cea88c..4cea88c 100644..100755 --- a/src/tests/libknot/libknot/zone_tree_tests.h +++ b/src/tests/libknot/libknot/zone_tree_tests.h diff --git a/src/tests/libknot/libknot/zonedb_tests.c b/src/tests/libknot/libknot/zonedb_tests.c index 7b45587..7b45587 100644..100755 --- a/src/tests/libknot/libknot/zonedb_tests.c +++ b/src/tests/libknot/libknot/zonedb_tests.c diff --git a/src/tests/libknot/libknot/zonedb_tests.h b/src/tests/libknot/libknot/zonedb_tests.h index 0c4f8ef..0c4f8ef 100644..100755 --- a/src/tests/libknot/libknot/zonedb_tests.h +++ b/src/tests/libknot/libknot/zonedb_tests.h diff --git a/src/tests/libknot/realdata/files/parsed_data b/src/tests/libknot/realdata/files/parsed_data Binary files differindex fe22b90..fe22b90 100644..100755 --- a/src/tests/libknot/realdata/files/parsed_data +++ b/src/tests/libknot/realdata/files/parsed_data diff --git a/src/tests/libknot/realdata/files/parsed_data_queries b/src/tests/libknot/realdata/files/parsed_data_queries Binary files differindex 5857c87..5857c87 100644..100755 --- a/src/tests/libknot/realdata/files/parsed_data_queries +++ b/src/tests/libknot/realdata/files/parsed_data_queries diff --git a/src/tests/libknot/realdata/files/raw_data b/src/tests/libknot/realdata/files/raw_data Binary files differindex 502005e..502005e 100644..100755 --- a/src/tests/libknot/realdata/files/raw_data +++ b/src/tests/libknot/realdata/files/raw_data diff --git a/src/tests/libknot/realdata/files/raw_data_queries b/src/tests/libknot/realdata/files/raw_data_queries Binary files differindex 9062d5a..9062d5a 100644..100755 --- a/src/tests/libknot/realdata/files/raw_data_queries +++ b/src/tests/libknot/realdata/files/raw_data_queries diff --git a/src/tests/libknot/realdata/libknot/dname_tests_realdata.c b/src/tests/libknot/realdata/libknot/dname_tests_realdata.c index d0216c7..d0216c7 100644..100755 --- a/src/tests/libknot/realdata/libknot/dname_tests_realdata.c +++ b/src/tests/libknot/realdata/libknot/dname_tests_realdata.c diff --git a/src/tests/libknot/realdata/libknot/dname_tests_realdata.h b/src/tests/libknot/realdata/libknot/dname_tests_realdata.h index a7d75aa..a7d75aa 100644..100755 --- a/src/tests/libknot/realdata/libknot/dname_tests_realdata.h +++ b/src/tests/libknot/realdata/libknot/dname_tests_realdata.h diff --git a/src/tests/libknot/realdata/libknot/edns_tests_realdata.c b/src/tests/libknot/realdata/libknot/edns_tests_realdata.c index 257d480..257d480 100644..100755 --- a/src/tests/libknot/realdata/libknot/edns_tests_realdata.c +++ b/src/tests/libknot/realdata/libknot/edns_tests_realdata.c diff --git a/src/tests/libknot/realdata/libknot/edns_tests_realdata.h b/src/tests/libknot/realdata/libknot/edns_tests_realdata.h index cfa64b0..cfa64b0 100644..100755 --- a/src/tests/libknot/realdata/libknot/edns_tests_realdata.h +++ b/src/tests/libknot/realdata/libknot/edns_tests_realdata.h diff --git a/src/tests/libknot/realdata/libknot/node_tests_realdata.c b/src/tests/libknot/realdata/libknot/node_tests_realdata.c index b9415ef..91209c9 100644..100755 --- a/src/tests/libknot/realdata/libknot/node_tests_realdata.c +++ b/src/tests/libknot/realdata/libknot/node_tests_realdata.c @@ -94,7 +94,7 @@ static int test_node_create(const list *node_list) errors++; diag("Failed to create node structure"); } - knot_node_free(&tmp, 0); + knot_node_free(&tmp); } return (errors == 0); @@ -168,7 +168,7 @@ static int test_node_add_rrset(list *rrset_list) diag("Values in found rrset are wrong"); } - knot_node_free(&tmp, 0); + knot_node_free(&tmp); } return (errors == 0); diff --git a/src/tests/libknot/realdata/libknot/node_tests_realdata.h b/src/tests/libknot/realdata/libknot/node_tests_realdata.h index a90179f..a90179f 100644..100755 --- a/src/tests/libknot/realdata/libknot/node_tests_realdata.h +++ b/src/tests/libknot/realdata/libknot/node_tests_realdata.h diff --git a/src/tests/libknot/realdata/libknot/packet_tests_realdata.c b/src/tests/libknot/realdata/libknot/packet_tests_realdata.c index 08c0882..08c0882 100644..100755 --- a/src/tests/libknot/realdata/libknot/packet_tests_realdata.c +++ b/src/tests/libknot/realdata/libknot/packet_tests_realdata.c diff --git a/src/tests/libknot/realdata/libknot/packet_tests_realdata.h b/src/tests/libknot/realdata/libknot/packet_tests_realdata.h index c0e0479..c0e0479 100644..100755 --- a/src/tests/libknot/realdata/libknot/packet_tests_realdata.h +++ b/src/tests/libknot/realdata/libknot/packet_tests_realdata.h diff --git a/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c index f4ba64c..f4ba64c 100644..100755 --- a/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c +++ b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c diff --git a/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h index 570b2b1..570b2b1 100644..100755 --- a/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h +++ b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h diff --git a/src/tests/libknot/realdata/libknot/response_tests_realdata.c b/src/tests/libknot/realdata/libknot/response_tests_realdata.c index 7dac341..7dac341 100644..100755 --- a/src/tests/libknot/realdata/libknot/response_tests_realdata.c +++ b/src/tests/libknot/realdata/libknot/response_tests_realdata.c diff --git a/src/tests/libknot/realdata/libknot/response_tests_realdata.h b/src/tests/libknot/realdata/libknot/response_tests_realdata.h index 731604b..731604b 100644..100755 --- a/src/tests/libknot/realdata/libknot/response_tests_realdata.h +++ b/src/tests/libknot/realdata/libknot/response_tests_realdata.h diff --git a/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c index cb59f4c..cb59f4c 100644..100755 --- a/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c +++ b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c diff --git a/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h index cc3b705..cc3b705 100644..100755 --- a/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h +++ b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h diff --git a/src/tests/libknot/realdata/libknot/zone_tests_realdata.c b/src/tests/libknot/realdata/libknot/zone_tests_realdata.c index 2738dab..4687978 100644..100755 --- a/src/tests/libknot/realdata/libknot/zone_tests_realdata.c +++ b/src/tests/libknot/realdata/libknot/zone_tests_realdata.c @@ -77,7 +77,7 @@ static int test_zone_create(list node_list) errors++; } knot_node_free_rrsets(node, 1); - knot_node_free(&node, 0); + knot_node_free(&node); } return (errors == 0); diff --git a/src/tests/libknot/realdata/libknot/zone_tests_realdata.h b/src/tests/libknot/realdata/libknot/zone_tests_realdata.h index 5539709..5539709 100644..100755 --- a/src/tests/libknot/realdata/libknot/zone_tests_realdata.h +++ b/src/tests/libknot/realdata/libknot/zone_tests_realdata.h diff --git a/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c index 96d1517..96d1517 100644..100755 --- a/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c +++ b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c diff --git a/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h index 0c4f8ef..0c4f8ef 100644..100755 --- a/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h +++ b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h diff --git a/src/tests/libknot/realdata/libknot_tests_loader_realdata.c b/src/tests/libknot/realdata/libknot_tests_loader_realdata.c index e972855..e972855 100644..100755 --- a/src/tests/libknot/realdata/libknot_tests_loader_realdata.c +++ b/src/tests/libknot/realdata/libknot_tests_loader_realdata.c diff --git a/src/tests/libknot/realdata/libknot_tests_loader_realdata.h b/src/tests/libknot/realdata/libknot_tests_loader_realdata.h index 8f57944..8f57944 100644..100755 --- a/src/tests/libknot/realdata/libknot_tests_loader_realdata.h +++ b/src/tests/libknot/realdata/libknot_tests_loader_realdata.h diff --git a/src/tests/libknot/realdata/unittests_libknot_realdata.c b/src/tests/libknot/realdata/unittests_libknot_realdata.c index e557c43..e557c43 100644..100755 --- a/src/tests/libknot/realdata/unittests_libknot_realdata.c +++ b/src/tests/libknot/realdata/unittests_libknot_realdata.c diff --git a/src/tests/libknot/unittests_libknot.c b/src/tests/libknot/unittests_libknot.c index d522e1d..d522e1d 100644..100755 --- a/src/tests/libknot/unittests_libknot.c +++ b/src/tests/libknot/unittests_libknot.c diff --git a/src/tests/unittests_main.c b/src/tests/unittests_main.c index 1ec336a..21eae14 100644..100755 --- a/src/tests/unittests_main.c +++ b/src/tests/unittests_main.c @@ -22,7 +22,6 @@ #include "tests/common/slab_tests.h" #include "tests/common/skiplist_tests.h" #include "tests/common/events_tests.h" -#include "tests/common/da_tests.h" #include "tests/common/acl_tests.h" #include "tests/common/fdset_tests.h" #include "tests/knot/dthreads_tests.h" @@ -47,7 +46,6 @@ int main(int argc, char *argv[]) &skiplist_tests_api, //! Skip list unit &dthreads_tests_api, //! DThreads testing unit &events_tests_api, //! Events testing unit - &da_tests_api, //! Dynamic array unit &acl_tests_api, //! ACLs &fdset_tests_api, //! FDSET polling wrapper diff --git a/src/tests/xfr_tests.c b/src/tests/xfr_tests.c index 5017109..b78678b 100644..100755 --- a/src/tests/xfr_tests.c +++ b/src/tests/xfr_tests.c @@ -172,11 +172,11 @@ int main(int argc, char **argv) server_t *server = server_create(); // Initialize configuration - conf_read_lock(); + rcu_read_lock(); conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0); conf_add_hook(conf(), CONF_LOG, zones_ns_conf_hook, server->nameserver); conf_add_hook(conf(), CONF_LOG, server_conf_hook, server); - conf_read_unlock(); + rcu_read_unlock(); // Find implicit configuration file if (!config_fn) { @@ -215,7 +215,7 @@ int main(int argc, char **argv) log_server_error("Couldn't open configuration file " "'%s'.\n", config_fn); } else { - log_server_error("Failed to parse configuration '%s'.\n", + log_server_error("Failed to load configuration '%s'.\n", config_fn); } server_destroy(&server); diff --git a/src/tests/xfr_tests.h b/src/tests/xfr_tests.h index 29de11d..29de11d 100644..100755 --- a/src/tests/xfr_tests.h +++ b/src/tests/xfr_tests.h diff --git a/src/zcompile/LICENSE b/src/zcompile/LICENSE index 55faacf..55faacf 100644..100755 --- a/src/zcompile/LICENSE +++ b/src/zcompile/LICENSE diff --git a/src/zcompile/parser-descriptor.c b/src/zcompile/parser-descriptor.c index b876877..bc3ee16 100644..100755 --- a/src/zcompile/parser-descriptor.c +++ b/src/zcompile/parser-descriptor.c @@ -539,3 +539,4 @@ uint16_t parser_rrclass_from_string(const char *name) return (uint16_t) rrclass; } +/*! @} */ diff --git a/src/zcompile/parser-descriptor.h b/src/zcompile/parser-descriptor.h index 48c6f02..48c6f02 100644..100755 --- a/src/zcompile/parser-descriptor.h +++ b/src/zcompile/parser-descriptor.h diff --git a/src/zcompile/parser-util.c b/src/zcompile/parser-util.c index cfbc624..955a7b0 100644..100755 --- a/src/zcompile/parser-util.c +++ b/src/zcompile/parser-util.c @@ -1128,25 +1128,32 @@ static ssize_t rdata_wireformat_to_rdata_atoms(const uint16_t *wireformat, // break; case KNOT_RDATA_WF_IPSECGATEWAY: dbg_rdata("Parsed item is an IPSECGATEWAY address.\n"); - switch (rdata_atom_data(temp_rdatas[1])[0]) { + dbg_rdata("Gateway type: %d\n", + ((uint8_t *)rdata_atom_data(temp_rdatas[1]))[0]); + switch (((uint8_t *)rdata_atom_data(temp_rdatas[1]))[0]) { /* gateway type */ - default: case IPSECKEY_NOGATEWAY: + dbg_rdata("NOGATEWAY\n"); length = 0; break; case IPSECKEY_IP4: + dbg_rdata("IPv4\n"); length = 4; break; case IPSECKEY_IP6: + dbg_rdata("IPv6\n"); length = IP6ADDRLEN; break; case IPSECKEY_DNAME: + dbg_rdata("DNAME\n"); is_domain = 1; is_wirestore = 1; break; - } - - break; + default: + dbg_rdata("Unknown IPSECKEY gateway!\n"); + free(temp_rdatas); + return -1; + } // switch } if (is_domain) { @@ -2473,3 +2480,4 @@ void set_bitnsec(uint8_t bits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE], bits[window][bit / 8] |= (1 << (7 - bit % 8)); } +/*! @} */ diff --git a/src/zcompile/parser-util.h b/src/zcompile/parser-util.h index 57258dc..57258dc 100644..100755 --- a/src/zcompile/parser-util.h +++ b/src/zcompile/parser-util.h diff --git a/src/zcompile/tests/unittests_zp_main.c b/src/zcompile/tests/unittests_zp_main.c index 5d8c5e9..5d8c5e9 100644..100755 --- a/src/zcompile/tests/unittests_zp_main.c +++ b/src/zcompile/tests/unittests_zp_main.c diff --git a/src/zcompile/tests/zcompile_tests.c b/src/zcompile/tests/zcompile_tests.c index 5d3dce6..5d3dce6 100644..100755 --- a/src/zcompile/tests/zcompile_tests.c +++ b/src/zcompile/tests/zcompile_tests.c diff --git a/src/zcompile/zcompile-error.c b/src/zcompile/zcompile-error.c index 9357cde..9357cde 100644..100755 --- a/src/zcompile/zcompile-error.c +++ b/src/zcompile/zcompile-error.c diff --git a/src/zcompile/zcompile-error.h b/src/zcompile/zcompile-error.h index c6d999c..c6d999c 100644..100755 --- a/src/zcompile/zcompile-error.h +++ b/src/zcompile/zcompile-error.h diff --git a/src/zcompile/zcompile.c b/src/zcompile/zcompile.c index c4415d4..e2f05e3 100644..100755 --- a/src/zcompile/zcompile.c +++ b/src/zcompile/zcompile.c @@ -42,6 +42,7 @@ #include "zcompile/parser-util.h" #include "zcompile/zcompile-error.h" #include "knot/zone/zone-dump.h" +#include "libknot/zone/zone-diff.h" #include "libknot/libknot.h" #include "libknot/util/utils.h" @@ -111,69 +112,6 @@ static void rrset_list_delete(rrset_list_t **head) dbg_zp("zp: list_delete: List deleleted.\n"); } -static int find_rrset_for_rrsig_in_zone(knot_zone_contents_t *zone, - knot_rrset_t *rrsig) -{ - assert(rrsig != NULL); - assert(rrsig->rdata->items[0].raw_data); - - knot_node_t *tmp_node = NULL; - - if (knot_rdata_rrsig_type_covered(knot_rrset_rdata(rrsig)) - != KNOT_RRTYPE_NSEC3) { - tmp_node = knot_zone_contents_get_node(zone, rrsig->owner); - } else { - tmp_node = knot_zone_contents_get_nsec3_node(zone, - rrsig->owner); - } - - dbg_zp_verb("zp: find_rr_for_sig: Found this node for RRSIG: %p.\n", - tmp_node); - - if (tmp_node == NULL) { - dbg_zp("zp: find_rr_for_sig: There is no node for this RR.\n"); - return KNOTDZCOMPILE_EINVAL; - } - - knot_rrset_t *tmp_rrset = - knot_node_get_rrset(tmp_node, - knot_rdata_rrsig_type_covered( - rrsig->rdata)); - - dbg_zp_verb("zp: find_rr_for_sig: Found this RRSet for RRSIG: %p.\n", - tmp_rrset); - - if (tmp_rrset == NULL) { - dbg_zp("zp: find_rr_for_sig: There is no RRSet " - "for this RRSIG.\n"); - return KNOTDZCOMPILE_EINVAL; - } - - - if (tmp_rrset->rrsigs != NULL) { - int ret = knot_zone_contents_add_rrsigs(zone, rrsig, - &tmp_rrset, &tmp_node, - KNOT_RRSET_DUPL_MERGE, 1); - if (ret != KNOT_EOK) { - dbg_zp("zp: find_rr_for_sig: Cannot add RRSIG.\n"); - return ret; - } - knot_rrset_free(&rrsig); - } else { - int ret = knot_zone_contents_add_rrsigs(zone, rrsig, - &tmp_rrset, &tmp_node, - KNOT_RRSET_DUPL_SKIP, 1); - if (ret != KNOT_EOK) { - dbg_zp("zp: find_rr_for_sig: Cannot add RRSIG.\n"); - return ret; - } - } - - dbg_zp("zp: find_rr_for_sig: Found node: %p found rrset: %p.\n", - tmo_node, tmp_rrset); - return KNOTDZCOMPILE_EOK; -} - static int find_rrset_for_rrsig_in_node(knot_zone_contents_t *zone, knot_node_t *node, knot_rrset_t *rrsig) @@ -187,28 +125,51 @@ static int find_rrset_for_rrsig_in_node(knot_zone_contents_t *zone, knot_rrset_t *tmp_rrset = knot_node_get_rrset(node, rrsig_type_covered(rrsig)); + int ret; + if (tmp_rrset == NULL) { dbg_zp("zp: find_rr_for_sig_in_node: Node does not contain " "RRSet of type %s.\n", knot_rrtype_to_string(rrsig_type_covered(rrsig))); - return KNOTDZCOMPILE_EINVAL; - } + tmp_rrset = knot_rrset_new(rrsig->owner, + rrsig_type_covered(rrsig), + rrsig->rclass, + rrsig->ttl); + if (tmp_rrset == NULL) { + dbg_zp("zp: find_rr_for_sig_in_node: Cannot create " + "dummy RRSet.\n"); + return KNOT_ERROR; + } - if (tmp_rrset->rrsigs != NULL) { - if (knot_zone_contents_add_rrsigs(zone, rrsig, - &tmp_rrset, &node, - KNOT_RRSET_DUPL_MERGE, 1) < 0) { - dbg_zp("zp: find_rr_for_sig: Cannot add RRSIG.\n"); - return KNOTDZCOMPILE_EINVAL; + ret = knot_zone_contents_add_rrset(zone, tmp_rrset, &node, + KNOT_RRSET_DUPL_MERGE, 1); + assert(ret <= 0); + if (ret < 0) { + dbg_zp("zp: Failed to add new dummy RRSet to the zone." + "\n"); + return KNOT_ERROR; } + } + + assert(tmp_rrset); + + if (tmp_rrset->ttl != rrsig->ttl) { + char *name = knot_dname_to_str(tmp_rrset->owner); + assert(name); + log_zone_warning("RRSIG owned by: %s cannot be added to " + "its RRSet, because their TTLs differ. " + "Changing TTL to value=%d.\n", + name, tmp_rrset->ttl); + free(name); + } + + ret = knot_zone_contents_add_rrsigs(zone, rrsig, &tmp_rrset, &node, + KNOT_RRSET_DUPL_MERGE, 1); + if (ret < 0) { + dbg_zp("zp: find_rr_for_sig: Cannot add RRSIG.\n"); + return KNOTDZCOMPILE_EINVAL; + } else if (ret > 0) { knot_rrset_free(&rrsig); - } else { - if (knot_zone_contents_add_rrsigs(zone, rrsig, - &tmp_rrset, &node, - KNOT_RRSET_DUPL_SKIP, 1) < 0) { - dbg_zp("zp: find_rr_for_sig: Cannot add RRSIG.\n"); - return KNOTDZCOMPILE_EINVAL; - } } assert(tmp_rrset->rrsigs != NULL); @@ -244,10 +205,9 @@ static void process_rrsigs_in_node(knot_zone_contents_t *zone, rrset_list_t *tmp = parser->node_rrsigs; while (tmp != NULL) { if (find_rrset_for_rrsig_in_node(zone, node, - tmp->data) != 0) { - rrset_list_add(&parser->rrsig_orphans, - tmp->data); - parser->rrsig_orphan_count++; + tmp->data) != KNOT_EOK) { + zc_error_prev_line("Could not add RRSIG to zone!\n"); + return; } tmp = tmp->next; } @@ -279,6 +239,7 @@ dbg_zp_exec_detail( assert(current_rrset->rdata->count == descriptor->length); } + assert(current_rrset->rdata->count > 0); assert(knot_dname_is_fqdn(current_rrset->owner)); @@ -336,6 +297,7 @@ dbg_zp_exec_detail( } if (current_rrset->type == KNOT_RRTYPE_RRSIG) { + /*!< \todo Use deep copy function here! */ knot_rrset_t *tmp_rrsig = knot_rrset_new(current_rrset->owner, KNOT_RRTYPE_RRSIG, @@ -458,11 +420,10 @@ dbg_zp_exec_detail( "add RDATA to RRSet.\n"); return KNOTDZCOMPILE_EBRDATA; } - - /* I chose skip, but there should not really be - * any rrset to skip */ + + /* Selected merge option does not really matter here. */ if (knot_zone_contents_add_rrset(contents, rrset, &node, - KNOT_RRSET_DUPL_SKIP, 1) < 0) { + KNOT_RRSET_DUPL_MERGE, 1) < 0) { knot_rrset_free(&rrset); dbg_zp("zp: process_rr: Cannot " "add RDATA to RRSet.\n"); @@ -472,8 +433,9 @@ dbg_zp_exec_detail( if (current_rrset->type != KNOT_RRTYPE_RRSIG && rrset->ttl != current_rrset->ttl) { - zc_error_prev_line( - "TTL does not match the TTL of the RRSet"); + zc_warning_prev_line( + "TTL does not match the TTL of the RRSet. " + "Changing to %lu.\n", rrset->ttl); } if (knot_zone_contents_add_rrset(contents, current_rrset, @@ -497,31 +459,6 @@ dbg_zp_exec_detail( return KNOTDZCOMPILE_EOK; } -static uint find_rrsets_orphans(knot_zone_contents_t *zone, rrset_list_t - *head) -{ - uint found_rrsets = 0; - while (head != NULL) { - if (find_rrset_for_rrsig_in_zone(zone, head->data) == 0) { - found_rrsets += 1; - dbg_zp("zp: find_orphans: " - "RRSet succesfully found: owner %s type %s\n", - knot_dname_to_str(head->data->owner), - knot_rrtype_to_string(head->data->type)); - } - else { /* we can throw it away now */ - dbg_zp("zp: find_orphans: " - "RRSet not found for RRSIG: %s (%s)\n", - knot_dname_to_str(head->data->owner), - knot_rrtype_to_string( - knot_rdata_rrsig_type_covered(head->data->rdata))); - knot_rrset_free(&head->data); - } - head = head->next; - } - return found_rrsets; -} - static int zone_open(const char *filename, uint32_t ttl, uint16_t rclass, knot_node_t *origin, void *scanner, knot_dname_t *origin_from_config) { @@ -549,19 +486,23 @@ static int zone_open(const char *filename, uint32_t ttl, uint16_t rclass, int zone_read(const char *name, const char *zonefile, const char *outfile, int semantic_checks) -{ +{ + if (!outfile) { + zc_error_prev_line("Missing output file for '%s'\n", + zonefile); + return KNOTDZCOMPILE_EINVAL; + } + dbg_zp("zp: zone_read: Reading zone: %s.\n", zonefile); /* Check that we can write to outfile. */ - if (outfile != NULL) { - FILE *f = fopen(outfile, "wb"); - if (f == NULL) { - fprintf(stderr, "Cannot write zone db to file '%s' (%s).\n", - outfile, strerror(errno)); - return KNOTDZCOMPILE_EINVAL; - } - fclose(f); + FILE *f = fopen(outfile, "wb"); + if (f == NULL) { + log_zone_error("Cannot write zone db to file '%s' (%s).\n", + outfile, strerror(errno)); + return KNOTDZCOMPILE_EINVAL; } + fclose(f); knot_dname_t *dname = knot_dname_new_from_str(name, strlen(name), NULL); @@ -570,7 +511,7 @@ int zone_read(const char *name, const char *zonefile, const char *outfile, } if (!knot_dname_is_fqdn(dname)) { - fprintf(stderr, "Error: given zone origin is not FQDN.\n"); + log_zone_error("Error: given zone origin is not FQDN.\n"); knot_dname_release(dname); return KNOTDZCOMPILE_EINVAL; } @@ -587,7 +528,7 @@ int zone_read(const char *name, const char *zonefile, const char *outfile, knot_dname_t *origin_from_config = knot_dname_new_from_str(name, strlen(name), NULL); if (origin_from_config == NULL) { - knot_node_free(&origin_node, 0); + knot_node_free(&origin_node); return KNOTDZCOMPILE_ENOMEM; } @@ -595,7 +536,7 @@ int zone_read(const char *name, const char *zonefile, const char *outfile, zp_lex_init(&scanner); if (scanner == NULL) { knot_dname_release(origin_from_config); - knot_node_free(&origin_node, 0); + knot_node_free(&origin_node); return KNOTDZCOMPILE_ENOMEM; } @@ -608,25 +549,55 @@ int zone_read(const char *name, const char *zonefile, const char *outfile, // knot_node_free(&origin_node, 0); return KNOTDZCOMPILE_EZONEINVAL; } + + /* Lock zone file. There should not be any modifications. */ + struct flock lock; + lock.l_type = F_RDLCK; + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; + lock.l_pid = getpid(); + if (fcntl(fileno(zp_get_in(scanner)), F_SETLK, &lock) == -1) { + log_zone_error("Cannot obtain zone source file lock (%d).\n", + errno); + FILE *in_file = (FILE *)zp_get_in(scanner); + fclose(in_file); + zp_lex_destroy(scanner); + knot_dname_release(origin_from_config); + return KNOTDZCOMPILE_EINVAL; + } + + /* Change lock type to unlock rigth away. */ + lock.l_type = F_UNLCK; if (zp_parse(scanner) != 0) { - /*!< \todo #1676 Implement proper locking. */ + log_zone_error("Parse failed.\n"); FILE *in_file = (FILE *)zp_get_in(scanner); fclose(in_file); - zp_lex_destroy(scanner); knot_dname_release(origin_from_config); // knot_node_free(&origin_node, 0); + /* Release file lock. */ + if (fcntl(fileno(zp_get_in(scanner)), F_SETLK, &lock) == -1) { + log_zone_error("Cannot release zone source file " + "lock (%d).\n", + errno); + } + zp_lex_destroy(scanner); return KNOTDZCOMPILE_ESYNT; } knot_zone_contents_t *contents = knot_zone_get_contents(parser->current_zone); + + /* Release file lock. */ + if (fcntl(fileno(zp_get_in(scanner)), F_SETLK, &lock) == -1) { + log_zone_error("Cannot release zone source file lock (%d).\n", + errno); + } FILE *in_file = (FILE *)zp_get_in(scanner); fclose(in_file); zp_lex_destroy(scanner); - - /*!< \todo #1676 Implement proper locking. */ dbg_zp("zp: zone_read: Parse complete for %s.\n", zonefile); @@ -650,31 +621,20 @@ int zone_read(const char *name, const char *zonefile, const char *outfile, return KNOTDZCOMPILE_EZONEINVAL; } - uint found_orphans; - found_orphans = find_rrsets_orphans(contents, - parser->rrsig_orphans); - - dbg_zp("zp: zone_read: %u RRSIG orphans found.\n", found_orphans); - - rrset_list_delete(&parser->rrsig_orphans); - - if (found_orphans != parser->rrsig_orphan_count) { - /*! \todo This might be desired behaviour. */ - fprintf(stderr, - "There are unassigned RRSIGs in the zone!\n"); + int ret = knot_zone_contents_adjust(contents); + if (ret != KNOT_EOK) { + fprintf(stderr, "Zone could not be adjusted, error: %s.\n", + knot_strerror(ret)); parser->errors++; } - - /*! \todo Check return value. */ - knot_zone_contents_adjust(contents); - + dbg_zp("zp: zone_read: Zone adjusted.\n"); if (parser->errors != 0) { - log_zone_error("Parser finished with %d error(s)%s\n", - parser->errors, outfile == NULL ? - "." : ", not dumping the zone!"); - } else if (outfile != NULL) { + log_zone_error("Parser finished with %d error(s), " + "not dumping the zone!\n", + parser->errors); + } else { int fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU | S_IRWXG); if (fd < 0) { log_zone_error("Could not open destination file for db: %s.\n", @@ -688,7 +648,10 @@ int zone_read(const char *name, const char *zonefile, const char *outfile, if (ret != KNOT_EOK) { log_zone_error("Could not dump zone, reason: " "%s.\n", knot_strerror(ret)); - remove(outfile); + if (remove(outfile) != 0) { + log_zone_error("Could not remove " + "db file!\n"); + } totalerrors++; } else { /* Write CRC file. */ diff --git a/src/zcompile/zcompile.h b/src/zcompile/zcompile.h index 37b3791..d19ef4c 100644..100755 --- a/src/zcompile/zcompile.h +++ b/src/zcompile/zcompile.h @@ -131,18 +131,11 @@ struct zparser { knot_rrset_t *current_rrset; /*!< Current RRSet. */ knot_rdata_item_t *temporary_items; /*!< Temporary rdata items. */ - /*! - * \brief list of RRSIGs that were not inside their nodes in zone file - */ - rrset_list_t *rrsig_orphans; - unsigned long rrsig_orphan_count; /*!< RRSIG orphan count */ - knot_dname_t *root_domain; /*!< Root domain name. */ slab_cache_t *parser_slab; /*!< Slab for parser. */ rrset_list_t *node_rrsigs; /*!< List of RRSIGs in current node. */ int rdata_count; /*!< Count of parsed rdata. */ - int origin_directive; /*!< Set to 1 if $ORIGIN directive is present. */ }; typedef struct zparser zparser_type; diff --git a/src/zcompile/zcompile_main.c b/src/zcompile/zcompile_main.c index c84515e..983376a 100644..100755 --- a/src/zcompile/zcompile_main.c +++ b/src/zcompile/zcompile_main.c @@ -26,7 +26,7 @@ static void help(int argc, char **argv) { printf("Usage: %s [parameters] origin zonefile\n", argv[0]); - printf("Parameters:\n" + printf("\n:Parameters:\n" " -o <outfile> Override output file.\n" " -v Verbose mode - additional runtime information.\n" " -s Enable semantic checks.\n" @@ -114,7 +114,6 @@ int main(int argc, char **argv) } else { log_zone_info("Zone file for '%s' is OK.\n", origin); } - //log_close(); return 0; } diff --git a/src/zcompile/zlexer.l b/src/zcompile/zlexer.l index 58e6439..58e6439 100644..100755 --- a/src/zcompile/zlexer.l +++ b/src/zcompile/zlexer.l diff --git a/src/zcompile/zparser.y b/src/zcompile/zparser.y index 89af437..c35060e 100644..100755 --- a/src/zcompile/zparser.y +++ b/src/zcompile/zparser.y @@ -314,7 +314,6 @@ origin_directive: DOLLAR_ORIGIN sp abs_dname trail // knot_node_free(&parser->origin, 1); } parser->origin = origin_node; - parser->origin_directive = 1; } | DOLLAR_ORIGIN sp rel_dname trail { @@ -406,12 +405,8 @@ abs_dname: '.' } | '@' { - if (parser->origin_directive) { - $$ = parser->origin->owner; - } else { - zc_error("@ used, but no $ORIGIN specified.\n"); - $$ = parser->origin->owner; - } + assert(parser->origin != NULL); + $$ = parser->origin->owner; } | rel_dname '.' { @@ -1267,13 +1262,13 @@ rdata_apl: rdata_apl_seq trail rdata_apl_seq: dotted_str { - zadd_rdata_wireformat(zparser_conv_apl_rdata($1.str)); + zadd_rdata_txt_wireformat(zparser_conv_apl_rdata($1.str), 1); free($1.str); } | rdata_apl_seq sp dotted_str { - zadd_rdata_wireformat(zparser_conv_apl_rdata($3.str)); + zadd_rdata_txt_wireformat(zparser_conv_apl_rdata($3.str), 0); free($3.str); } @@ -1637,13 +1632,9 @@ zparser_init(const char *filename, uint32_t ttl, uint16_t rclass, parser->current_zone = knot_zone_new(origin, 0, 1); parser->node_rrsigs = NULL; - parser->rrsig_orphans = NULL; - parser->rrsig_orphan_count = 0; parser->current_rrset->rclass = parser->default_class; parser->current_rrset->rdata = NULL; - - parser->origin_directive = 0; } diff --git a/tests/querytcp.c b/tests/querytcp.c index 7e1418f..7e1418f 100644..100755 --- a/tests/querytcp.c +++ b/tests/querytcp.c |